summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/monitoring-plugins.spec26
-rw-r--r--.gitignore2
-rw-r--r--REQUIREMENTS8
-rw-r--r--configure.ac5
-rw-r--r--plugins/Makefile.am7
-rw-r--r--plugins/check_nt.c789
-rw-r--r--plugins/check_nt.d/config.h53
-rw-r--r--plugins/check_ntp.c947
-rw-r--r--plugins/check_ntp_time.c1
-rwxr-xr-xplugins/tests/check_nt.t80
10 files changed, 4 insertions, 1914 deletions
diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec
index ce22606b..fa47cdc7 100644
--- a/.github/monitoring-plugins.spec
+++ b/.github/monitoring-plugins.spec
@@ -652,32 +652,6 @@ Provides check_nagios of the Monitoring Plugins.
652 652
653 653
654 654
655# check_nt
656%package nt
657Summary: Monitoring Plugins - check_nt
658Requires: %{name} = %{version}-%{release}
659
660%description nt
661Provides check_nt of the Monitoring Plugins.
662
663%files nt
664%{plugindir}/check_nt
665
666
667
668# check_ntp
669%package ntp
670Summary: Monitoring Plugins - check_ntp
671Requires: %{name} = %{version}-%{release}
672
673%description ntp
674Provides check_ntp of the Monitoring Plugins.
675
676%files ntp
677%{plugindir}/check_ntp
678
679
680
681# check_ntp_peer 655# check_ntp_peer
682%package ntp_peer 656%package ntp_peer
683Summary: Monitoring Plugins - check_ntp_peer 657Summary: Monitoring Plugins - check_ntp_peer
diff --git a/.gitignore b/.gitignore
index 00e19d52..d0115b05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -174,8 +174,6 @@ plugins/check_disk.d/.dirstamp
174/plugins/check_netsaint 174/plugins/check_netsaint
175/plugins/check_nntp 175/plugins/check_nntp
176/plugins/check_nntps 176/plugins/check_nntps
177/plugins/check_nt
178/plugins/check_ntp
179/plugins/check_ntp_peer 177/plugins/check_ntp_peer
180/plugins/check_ntp_time 178/plugins/check_ntp_time
181/plugins/check_pgsql 179/plugins/check_pgsql
diff --git a/REQUIREMENTS b/REQUIREMENTS
index 551fdb1a..8f1befbd 100644
--- a/REQUIREMENTS
+++ b/REQUIREMENTS
@@ -26,7 +26,7 @@ check_curl:
26 other SSL implementations are currently not supported 26 other SSL implementations are currently not supported
27 - uriparser 0.7.5 or later 27 - uriparser 0.7.5 or later
28 https://uriparser.github.io/ 28 https://uriparser.github.io/
29 29
30check_fping: 30check_fping:
31 - Requires the fping utility distributed with SATAN. Either 31 - Requires the fping utility distributed with SATAN. Either
32 download and install SATAN or grab the fping program from 32 download and install SATAN or grab the fping program from
@@ -87,16 +87,12 @@ check_ifstatus/check_ifoperstatus
87 - Requires Net::SNMP perl module 87 - Requires Net::SNMP perl module
88 http://www.perl.com/CPAN/modules/by-authors/id/D/DT/DTOWN/ 88 http://www.perl.com/CPAN/modules/by-authors/id/D/DT/DTOWN/
89 89
90check_nt:
91 - Requires NSClient to run on the NT server to monitor
92 http://nsclient.ready2run.nl/
93
94check_ups: 90check_ups:
95 - Requires Network UPS Tools (>= 1.4) to run on the server to monitor 91 - Requires Network UPS Tools (>= 1.4) to run on the server to monitor
96 http://www.networkupstools.org/ 92 http://www.networkupstools.org/
97 93
98check_ide_smart: 94check_ide_smart:
99 - Uses the Linux specific SMART interface [http://smartlinux.sourceforge.net/smart/index.php]. 95 - Uses the Linux specific SMART interface [http://smartlinux.sourceforge.net/smart/index.php].
100 96
101OS Specific Issues 97OS Specific Issues
102------------------ 98------------------
diff --git a/configure.ac b/configure.ac
index 705183a2..2bf94014 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1832,11 +1832,6 @@ if test -n "$PATH_TO_APTGET" ; then
1832fi 1832fi
1833 1833
1834 1834
1835if test -f $srcdir/plugins/check_nt.c ; then
1836 EXTRAS="$EXTRAS check_nt\$(EXEEXT)"
1837fi
1838
1839
1840dnl used in check_dhcp 1835dnl used in check_dhcp
1841AC_CHECK_HEADERS(sys/sockio.h) 1836AC_CHECK_HEADERS(sys/sockio.h)
1842 1837
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 1a9399f0..d098fa8a 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -33,7 +33,7 @@ MATHLIBS = @MATHLIBS@
33#AM_CFLAGS = -Wall 33#AM_CFLAGS = -Wall
34 34
35libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ 35libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \
36 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_ping \ 36 check_mrtg check_mrtgtraf check_ntp_peer check_ping \
37 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ 37 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \
38 check_ups check_users negate \ 38 check_ups check_users negate \
39 urlize @EXTRAS@ \ 39 urlize @EXTRAS@ \
@@ -44,7 +44,7 @@ check_tcp_programs = check_ftp check_imap check_nntp check_pop \
44 44
45EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \ 45EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \
46 check_swap check_fping check_ldap check_game check_dig \ 46 check_swap check_fping check_ldap check_game check_dig \
47 check_nagios check_by_ssh check_dns check_nt check_ide_smart \ 47 check_nagios check_by_ssh check_dns check_ide_smart \
48 check_procs check_mysql_query check_apt check_dbi check_curl \ 48 check_procs check_mysql_query check_apt check_dbi check_curl \
49 \ 49 \
50 tests/test_check_swap \ 50 tests/test_check_swap \
@@ -76,7 +76,6 @@ EXTRA_DIST = t \
76 check_tcp.d \ 76 check_tcp.d \
77 check_real.d \ 77 check_real.d \
78 check_ssh.d \ 78 check_ssh.d \
79 check_nt.d \
80 check_dns.d \ 79 check_dns.d \
81 check_mrtgtraf.d \ 80 check_mrtgtraf.d \
82 check_mysql_query.d \ 81 check_mysql_query.d \
@@ -157,8 +156,6 @@ check_mysql_query_CFLAGS = $(AM_CFLAGS) $(MYSQLCFLAGS)
157check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE) 156check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE)
158check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS) 157check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS)
159check_nagios_LDADD = $(BASEOBJS) 158check_nagios_LDADD = $(BASEOBJS)
160check_nt_LDADD = $(NETLIBS)
161check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
162check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 159check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
163check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) 160check_pgsql_LDADD = $(NETLIBS) $(PGLIBS)
164check_ping_LDADD = $(NETLIBS) 161check_ping_LDADD = $(NETLIBS)
diff --git a/plugins/check_nt.c b/plugins/check_nt.c
deleted file mode 100644
index 35ca92cd..00000000
--- a/plugins/check_nt.c
+++ /dev/null
@@ -1,789 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_nt plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2002 Yves Rubin (rubiyz@yahoo.com)
7 * Copyright (c) 2003-2024 Monitoring Plugins Development Team
8 *
9 * Description:
10 *
11 * This file contains the check_nt plugin
12 *
13 * This plugin collects data from the NSClient service running on a
14 * Windows NT/2000/XP/2003 server.
15 * This plugin requires NSClient software to run on NT
16 * (https://nsclient.org/)
17 *
18 *
19 * This program is free software: you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation, either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 *
32 *
33 *****************************************************************************/
34
35const char *progname = "check_nt";
36const char *copyright = "2000-2024";
37const char *email = "devel@monitoring-plugins.org";
38
39#include "common.h"
40#include "netutils.h"
41#include "utils.h"
42#include "check_nt.d/config.h"
43
44enum {
45 MAX_VALUE_LIST = 30,
46};
47
48static char recv_buffer[MAX_INPUT_BUFFER];
49
50static void fetch_data(const char *address, int port, const char *sendb);
51
52typedef struct {
53 int errorcode;
54 check_nt_config config;
55} check_nt_config_wrapper;
56static check_nt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57
58static void preparelist(char *string);
59static bool strtoularray(unsigned long *array, char *string, const char *delim);
60static void print_help(void);
61void print_usage(void);
62
63int main(int argc, char **argv) {
64 setlocale(LC_ALL, "");
65 bindtextdomain(PACKAGE, LOCALEDIR);
66 textdomain(PACKAGE);
67
68 /* Parse extra opts if any */
69 argv = np_extra_opts(&argc, argv, progname);
70
71 check_nt_config_wrapper tmp_config = process_arguments(argc, argv);
72 if (tmp_config.errorcode == ERROR) {
73 usage4(_("Could not parse arguments"));
74 }
75
76 const check_nt_config config = tmp_config.config;
77
78 /* initialize alarm signal handling */
79 signal(SIGALRM, socket_timeout_alarm_handler);
80
81 /* set socket timeout */
82 alarm(socket_timeout);
83
84 int return_code = STATE_UNKNOWN;
85 char *send_buffer = NULL;
86 char *output_message = NULL;
87 char *perfdata = NULL;
88 char *temp_string = NULL;
89 char *temp_string_perf = NULL;
90 char *description = NULL;
91 char *counter_unit = NULL;
92 char *errcvt = NULL;
93 unsigned long lvalue_list[MAX_VALUE_LIST];
94 switch (config.vars_to_check) {
95 case CHECK_CLIENTVERSION:
96 xasprintf(&send_buffer, "%s&1", config.req_password);
97 fetch_data(config.server_address, config.server_port, send_buffer);
98 if (config.value_list != NULL && strcmp(recv_buffer, config.value_list) != 0) {
99 xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"),
100 recv_buffer, config.value_list);
101 return_code = STATE_WARNING;
102 } else {
103 xasprintf(&output_message, "%s", recv_buffer);
104 return_code = STATE_OK;
105 }
106 break;
107 case CHECK_CPULOAD:
108 if (config.value_list == NULL) {
109 output_message = strdup(_("missing -l parameters"));
110 } else if (!strtoularray(lvalue_list, config.value_list, ",")) {
111 output_message = strdup(_("wrong -l parameter."));
112 } else {
113 /* -l parameters is present with only integers */
114 return_code = STATE_OK;
115 temp_string = strdup(_("CPU Load"));
116 temp_string_perf = strdup(" ");
117
118 /* loop until one of the parameters is wrong or not present */
119 int offset = 0;
120 while (lvalue_list[0 + offset] > (unsigned long)0 &&
121 lvalue_list[0 + offset] <= (unsigned long)17280 &&
122 lvalue_list[1 + offset] > (unsigned long)0 &&
123 lvalue_list[1 + offset] <= (unsigned long)100 &&
124 lvalue_list[2 + offset] > (unsigned long)0 &&
125 lvalue_list[2 + offset] <= (unsigned long)100) {
126
127 /* Send request and retrieve data */
128 xasprintf(&send_buffer, "%s&2&%lu", config.req_password, lvalue_list[0 + offset]);
129 fetch_data(config.server_address, config.server_port, send_buffer);
130
131 unsigned long utilization = strtoul(recv_buffer, NULL, 10);
132
133 /* Check if any of the request is in a warning or critical state */
134 if (utilization >= lvalue_list[2 + offset]) {
135 return_code = STATE_CRITICAL;
136 } else if (utilization >= lvalue_list[1 + offset] && return_code < STATE_WARNING) {
137 return_code = STATE_WARNING;
138 }
139
140 xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization,
141 lvalue_list[0 + offset]);
142 xasprintf(&temp_string, "%s%s", temp_string, output_message);
143 xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"),
144 lvalue_list[0 + offset], utilization, lvalue_list[1 + offset],
145 lvalue_list[2 + offset]);
146 xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata);
147 offset += 3; /* move across the array */
148 }
149
150 if (strlen(temp_string) > 10) { /* we had at least one loop */
151 output_message = strdup(temp_string);
152 perfdata = temp_string_perf;
153 } else {
154 output_message = strdup(_("not enough values for -l parameters"));
155 }
156 }
157 break;
158 case CHECK_UPTIME: {
159 char *tmp_value_list = config.value_list;
160 if (config.value_list == NULL) {
161 tmp_value_list = "minutes";
162 }
163 if (strncmp(tmp_value_list, "seconds", strlen("seconds") + 1) &&
164 strncmp(tmp_value_list, "minutes", strlen("minutes") + 1) &&
165 strncmp(config.value_list, "hours", strlen("hours") + 1) &&
166 strncmp(tmp_value_list, "days", strlen("days") + 1)) {
167
168 output_message = strdup(_("wrong -l argument"));
169 } else {
170 xasprintf(&send_buffer, "%s&3", config.req_password);
171 fetch_data(config.server_address, config.server_port, send_buffer);
172 unsigned long uptime = strtoul(recv_buffer, NULL, 10);
173 int updays = uptime / 86400;
174 int uphours = (uptime % 86400) / 3600;
175 int upminutes = ((uptime % 86400) % 3600) / 60;
176
177 if (!strncmp(tmp_value_list, "minutes", strlen("minutes"))) {
178 uptime = uptime / 60;
179 } else if (!strncmp(tmp_value_list, "hours", strlen("hours"))) {
180 uptime = uptime / 3600;
181 } else if (!strncmp(tmp_value_list, "days", strlen("days"))) {
182 uptime = uptime / 86400;
183 }
184 /* else uptime in seconds, nothing to do */
185
186 xasprintf(&output_message,
187 _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays,
188 uphours, upminutes, uptime);
189
190 if (config.check_critical_value && uptime <= config.critical_value) {
191 return_code = STATE_CRITICAL;
192 } else if (config.check_warning_value && uptime <= config.warning_value) {
193 return_code = STATE_WARNING;
194 } else {
195 return_code = STATE_OK;
196 }
197 }
198 } break;
199 case CHECK_USEDDISKSPACE:
200 if (config.value_list == NULL) {
201 output_message = strdup(_("missing -l parameters"));
202 } else if (strlen(config.value_list) != 1) {
203 output_message = strdup(_("wrong -l argument"));
204 } else {
205 xasprintf(&send_buffer, "%s&4&%s", config.req_password, config.value_list);
206 fetch_data(config.server_address, config.server_port, send_buffer);
207 char *fds = strtok(recv_buffer, "&");
208 char *tds = strtok(NULL, "&");
209 double total_disk_space = 0;
210 double free_disk_space = 0;
211 if (fds != NULL) {
212 free_disk_space = atof(fds);
213 }
214 if (tds != NULL) {
215 total_disk_space = atof(tds);
216 }
217
218 if (total_disk_space > 0 && free_disk_space >= 0) {
219 double percent_used_space =
220 ((total_disk_space - free_disk_space) / total_disk_space) * 100;
221 double warning_used_space = ((float)config.warning_value / 100) * total_disk_space;
222 double critical_used_space =
223 ((float)config.critical_value / 100) * total_disk_space;
224
225 xasprintf(
226 &temp_string,
227 _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"),
228 config.value_list, total_disk_space / 1073741824,
229 (total_disk_space - free_disk_space) / 1073741824, percent_used_space,
230 free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100);
231 xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"),
232 config.value_list, (total_disk_space - free_disk_space) / 1073741824,
233 warning_used_space / 1073741824, critical_used_space / 1073741824,
234 total_disk_space / 1073741824);
235
236 if (config.check_critical_value && percent_used_space >= config.critical_value) {
237 return_code = STATE_CRITICAL;
238 } else if (config.check_warning_value &&
239 percent_used_space >= config.warning_value) {
240 return_code = STATE_WARNING;
241 } else {
242 return_code = STATE_OK;
243 }
244
245 output_message = strdup(temp_string);
246 perfdata = temp_string_perf;
247 } else {
248 output_message = strdup(_("Free disk space : Invalid drive"));
249 return_code = STATE_UNKNOWN;
250 }
251 }
252 break;
253 case CHECK_SERVICESTATE:
254 case CHECK_PROCSTATE:
255 if (config.value_list == NULL) {
256 output_message = strdup(_("No service/process specified"));
257 } else {
258 preparelist(
259 config.value_list); /* replace , between services with & to send the request */
260 xasprintf(&send_buffer, "%s&%u&%s&%s", config.req_password,
261 (config.vars_to_check == CHECK_SERVICESTATE) ? 5 : 6,
262 (config.show_all) ? "ShowAll" : "ShowFail", config.value_list);
263 fetch_data(config.server_address, config.server_port, send_buffer);
264 char *numstr = strtok(recv_buffer, "&");
265 if (numstr == NULL) {
266 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
267 }
268 return_code = atoi(numstr);
269 temp_string = strtok(NULL, "&");
270 output_message = strdup(temp_string);
271 }
272 break;
273 case CHECK_MEMUSE:
274 xasprintf(&send_buffer, "%s&7", config.req_password);
275 fetch_data(config.server_address, config.server_port, send_buffer);
276 char *numstr = strtok(recv_buffer, "&");
277 if (numstr == NULL) {
278 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
279 }
280 double mem_commitLimit = atof(numstr);
281 numstr = strtok(NULL, "&");
282 if (numstr == NULL) {
283 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
284 }
285 double mem_commitByte = atof(numstr);
286 double percent_used_space = (mem_commitByte / mem_commitLimit) * 100;
287 double warning_used_space = ((float)config.warning_value / 100) * mem_commitLimit;
288 double critical_used_space = ((float)config.critical_value / 100) * mem_commitLimit;
289
290 /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here,
291 which equals RAM + Pagefiles. */
292 xasprintf(
293 &output_message,
294 _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"),
295 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space,
296 (mem_commitLimit - mem_commitByte) / 1048567,
297 (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100);
298 xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"),
299 mem_commitByte / 1048567, warning_used_space / 1048567,
300 critical_used_space / 1048567, mem_commitLimit / 1048567);
301
302 return_code = STATE_OK;
303 if (config.check_critical_value && percent_used_space >= config.critical_value) {
304 return_code = STATE_CRITICAL;
305 } else if (config.check_warning_value && percent_used_space >= config.warning_value) {
306 return_code = STATE_WARNING;
307 }
308
309 break;
310 case CHECK_COUNTER: {
311 /*
312 CHECK_COUNTER has been modified to provide extensive perfdata information.
313 In order to do this, some modifications have been done to the code
314 and some constraints have been introduced.
315
316 1) For the sake of simplicity of the code, perfdata information will only be
317 provided when the "description" field is added.
318
319 2) If the counter you're going to measure is percent-based, the code will detect
320 the percent sign in its name and will attribute minimum (0%) and maximum (100%)
321 values automagically, as well the "%" sign to graph units.
322
323 3) OTOH, if the counter is "absolute", you'll have to provide the following
324 the counter unit - that is, the dimensions of the counter you're getting. Examples:
325 pages/s, packets transferred, etc.
326
327 4) If you want, you may provide the minimum and maximum values to expect. They aren't
328 mandatory, but once specified they MUST have the same order of magnitude and units of -w and
329 -c; otherwise. strange things will happen when you make graphs of your data.
330 */
331
332 double counter_value = 0.0;
333 if (config.value_list == NULL) {
334 output_message = strdup(_("No counter specified"));
335 } else {
336 preparelist(
337 config.value_list); /* replace , between services with & to send the request */
338 bool isPercent = (strchr(config.value_list, '%') != NULL);
339
340 strtok(config.value_list, "&"); /* burn the first parameters */
341 description = strtok(NULL, "&");
342 counter_unit = strtok(NULL, "&");
343 xasprintf(&send_buffer, "%s&8&%s", config.req_password, config.value_list);
344 fetch_data(config.server_address, config.server_port, send_buffer);
345 counter_value = atof(recv_buffer);
346
347 bool allRight = false;
348 if (description == NULL) {
349 xasprintf(&output_message, "%.f", counter_value);
350 } else if (isPercent) {
351 counter_unit = strdup("%");
352 allRight = true;
353 }
354
355 char *minval = NULL;
356 char *maxval = NULL;
357 double fminval = 0;
358 double fmaxval = 0;
359 if ((counter_unit != NULL) && (!allRight)) {
360 minval = strtok(NULL, "&");
361 maxval = strtok(NULL, "&");
362
363 /* All parameters specified. Let's check the numbers */
364
365 fminval = (minval != NULL) ? strtod(minval, &errcvt) : -1;
366 fmaxval = (minval != NULL) ? strtod(maxval, &errcvt) : -1;
367
368 if ((fminval == 0) && (minval == errcvt)) {
369 output_message = strdup(_("Minimum value contains non-numbers"));
370 } else {
371 if ((fmaxval == 0) && (maxval == errcvt)) {
372 output_message = strdup(_("Maximum value contains non-numbers"));
373 } else {
374 allRight = true; /* Everything is OK. */
375 }
376 }
377 } else if ((counter_unit == NULL) && (description != NULL)) {
378 output_message = strdup(_("No unit counter specified"));
379 }
380
381 if (allRight) {
382 /* Let's format the output string, finally... */
383 if (strstr(description, "%") == NULL) {
384 xasprintf(&output_message, "%s = %.2f %s", description, counter_value,
385 counter_unit);
386 } else {
387 /* has formatting, will segv if wrong */
388 xasprintf(&output_message, description, counter_value);
389 }
390 xasprintf(&output_message, "%s |", output_message);
391 xasprintf(&output_message, "%s %s", output_message,
392 fperfdata(description, counter_value, counter_unit, 1,
393 config.warning_value, 1, config.critical_value,
394 (!(isPercent) && (minval != NULL)), fminval,
395 (!(isPercent) && (minval != NULL)), fmaxval));
396 }
397 }
398
399 if (config.critical_value > config.warning_value) { /* Normal thresholds */
400 if (config.check_critical_value && counter_value >= config.critical_value) {
401 return_code = STATE_CRITICAL;
402 } else if (config.check_warning_value && counter_value >= config.warning_value) {
403 return_code = STATE_WARNING;
404 } else {
405 return_code = STATE_OK;
406 }
407 } else { /* inverse thresholds */
408 return_code = STATE_OK;
409 if (config.check_critical_value && counter_value <= config.critical_value) {
410 return_code = STATE_CRITICAL;
411 } else if (config.check_warning_value && counter_value <= config.warning_value) {
412 return_code = STATE_WARNING;
413 }
414 }
415 } break;
416 case CHECK_FILEAGE:
417 if (config.value_list == NULL) {
418 output_message = strdup(_("No counter specified"));
419 } else {
420 preparelist(
421 config.value_list); /* replace , between services with & to send the request */
422 xasprintf(&send_buffer, "%s&9&%s", config.req_password, config.value_list);
423 fetch_data(config.server_address, config.server_port, send_buffer);
424 unsigned long age_in_minutes = atoi(strtok(recv_buffer, "&"));
425 description = strtok(NULL, "&");
426 output_message = strdup(description);
427
428 if (config.critical_value > config.warning_value) { /* Normal thresholds */
429 if (config.check_critical_value && age_in_minutes >= config.critical_value) {
430 return_code = STATE_CRITICAL;
431 } else if (config.check_warning_value && age_in_minutes >= config.warning_value) {
432 return_code = STATE_WARNING;
433 } else {
434 return_code = STATE_OK;
435 }
436 } else { /* inverse thresholds */
437 if (config.check_critical_value && age_in_minutes <= config.critical_value) {
438 return_code = STATE_CRITICAL;
439 } else if (config.check_warning_value && age_in_minutes <= config.warning_value) {
440 return_code = STATE_WARNING;
441 } else {
442 return_code = STATE_OK;
443 }
444 }
445 }
446 break;
447
448 case CHECK_INSTANCES:
449 if (config.value_list == NULL) {
450 output_message = strdup(_("No counter specified"));
451 } else {
452 xasprintf(&send_buffer, "%s&10&%s", config.req_password, config.value_list);
453 fetch_data(config.server_address, config.server_port, send_buffer);
454 if (!strncmp(recv_buffer, "ERROR", 5)) {
455 printf("NSClient - %s\n", recv_buffer);
456 exit(STATE_UNKNOWN);
457 }
458 xasprintf(&output_message, "%s", recv_buffer);
459 return_code = STATE_OK;
460 }
461 break;
462
463 case CHECK_NONE:
464 default:
465 usage4(_("Please specify a variable to check"));
466 break;
467 }
468
469 /* reset timeout */
470 alarm(0);
471
472 if (perfdata == NULL) {
473 printf("%s\n", output_message);
474 } else {
475 printf("%s | %s\n", output_message, perfdata);
476 }
477 return return_code;
478}
479
480/* process command-line arguments */
481check_nt_config_wrapper process_arguments(int argc, char **argv) {
482 static struct option longopts[] = {{"port", required_argument, 0, 'p'},
483 {"timeout", required_argument, 0, 't'},
484 {"critical", required_argument, 0, 'c'},
485 {"warning", required_argument, 0, 'w'},
486 {"variable", required_argument, 0, 'v'},
487 {"hostname", required_argument, 0, 'H'},
488 {"params", required_argument, 0, 'l'},
489 {"secret", required_argument, 0, 's'},
490 {"display", required_argument, 0, 'd'},
491 {"unknown-timeout", no_argument, 0, 'u'},
492 {"version", no_argument, 0, 'V'},
493 {"help", no_argument, 0, 'h'},
494 {0, 0, 0, 0}};
495
496 check_nt_config_wrapper result = {
497 .errorcode = OK,
498 .config = check_nt_config_init(),
499 };
500
501 /* no options were supplied */
502 if (argc < 2) {
503 result.errorcode = ERROR;
504 return result;
505 }
506
507 /* backwards compatibility */
508 if (!is_option(argv[1])) {
509 result.config.server_address = strdup(argv[1]);
510 argv[1] = argv[0];
511 argv = &argv[1];
512 argc--;
513 }
514
515 for (int index = 1; index < argc; index++) {
516 if (strcmp("-to", argv[index]) == 0) {
517 strcpy(argv[index], "-t");
518 } else if (strcmp("-wv", argv[index]) == 0) {
519 strcpy(argv[index], "-w");
520 } else if (strcmp("-cv", argv[index]) == 0) {
521 strcpy(argv[index], "-c");
522 }
523 }
524
525 int option = 0;
526 while (true) {
527 int option_index = getopt_long(argc, argv, "+hVH:t:c:w:p:v:l:s:d:u", longopts, &option);
528
529 if (option_index == -1 || option_index == EOF || option_index == 1) {
530 break;
531 }
532
533 switch (option_index) {
534 case '?': /* print short usage statement if args not parsable */
535 usage5();
536 case 'h': /* help */
537 print_help();
538 exit(STATE_UNKNOWN);
539 case 'V': /* version */
540 print_revision(progname, NP_VERSION);
541 exit(STATE_UNKNOWN);
542 case 'H': /* hostname */
543 result.config.server_address = optarg;
544 break;
545 case 's': /* password */
546 result.config.req_password = optarg;
547 break;
548 case 'p': /* port */
549 if (is_intnonneg(optarg)) {
550 result.config.server_port = atoi(optarg);
551 } else {
552 die(STATE_UNKNOWN, _("Server port must be an integer\n"));
553 }
554 break;
555 case 'v':
556 if (strlen(optarg) < 4) {
557 result.errorcode = ERROR;
558 return result;
559 }
560 if (!strcmp(optarg, "CLIENTVERSION")) {
561 result.config.vars_to_check = CHECK_CLIENTVERSION;
562 } else if (!strcmp(optarg, "CPULOAD")) {
563 result.config.vars_to_check = CHECK_CPULOAD;
564 } else if (!strcmp(optarg, "UPTIME")) {
565 result.config.vars_to_check = CHECK_UPTIME;
566 } else if (!strcmp(optarg, "USEDDISKSPACE")) {
567 result.config.vars_to_check = CHECK_USEDDISKSPACE;
568 } else if (!strcmp(optarg, "SERVICESTATE")) {
569 result.config.vars_to_check = CHECK_SERVICESTATE;
570 } else if (!strcmp(optarg, "PROCSTATE")) {
571 result.config.vars_to_check = CHECK_PROCSTATE;
572 } else if (!strcmp(optarg, "MEMUSE")) {
573 result.config.vars_to_check = CHECK_MEMUSE;
574 } else if (!strcmp(optarg, "COUNTER")) {
575 result.config.vars_to_check = CHECK_COUNTER;
576 } else if (!strcmp(optarg, "FILEAGE")) {
577 result.config.vars_to_check = CHECK_FILEAGE;
578 } else if (!strcmp(optarg, "INSTANCES")) {
579 result.config.vars_to_check = CHECK_INSTANCES;
580 } else {
581 result.errorcode = ERROR;
582 return result;
583 }
584 break;
585 case 'l': /* value list */
586 result.config.value_list = optarg;
587 break;
588 case 'w': /* warning threshold */
589 result.config.warning_value = strtoul(optarg, NULL, 10);
590 result.config.check_warning_value = true;
591 break;
592 case 'c': /* critical threshold */
593 result.config.critical_value = strtoul(optarg, NULL, 10);
594 result.config.check_critical_value = true;
595 break;
596 case 'd': /* Display select for services */
597 if (!strcmp(optarg, "SHOWALL")) {
598 result.config.show_all = true;
599 }
600 break;
601 case 'u':
602 socket_timeout_state = STATE_UNKNOWN;
603 break;
604 case 't': /* timeout */
605 socket_timeout = atoi(optarg);
606 if (socket_timeout <= 0) {
607 result.errorcode = ERROR;
608 return result;
609 }
610 }
611 }
612 if (result.config.server_address == NULL) {
613 usage4(_("You must provide a server address or host name"));
614 }
615
616 if (result.config.vars_to_check == CHECK_NONE) {
617 result.errorcode = ERROR;
618 return result;
619 }
620
621 if (result.config.req_password == NULL) {
622 result.config.req_password = strdup(_("None"));
623 }
624
625 return result;
626}
627
628void fetch_data(const char *address, int port, const char *sendb) {
629 int result = process_tcp_request(address, port, sendb, recv_buffer, sizeof(recv_buffer));
630
631 if (result != STATE_OK) {
632 die(result, _("could not fetch information from server\n"));
633 }
634
635 if (!strncmp(recv_buffer, "ERROR", 5)) {
636 die(STATE_UNKNOWN, "NSClient - %s\n", recv_buffer);
637 }
638}
639
640bool strtoularray(unsigned long *array, char *string, const char *delim) {
641 /* split a <delim> delimited string into a long array */
642 for (int idx = 0; idx < MAX_VALUE_LIST; idx++) {
643 array[idx] = 0;
644 }
645
646 int idx = 0;
647 for (char *t1 = strtok(string, delim); t1 != NULL; t1 = strtok(NULL, delim)) {
648 if (is_numeric(t1) && idx < MAX_VALUE_LIST) {
649 array[idx] = strtoul(t1, NULL, 10);
650 idx++;
651 } else {
652 return false;
653 }
654 }
655 return true;
656}
657
658void preparelist(char *string) {
659 /* Replace all , with & which is the delimiter for the request */
660 for (int i = 0; (size_t)i < strlen(string); i++) {
661 if (string[i] == ',') {
662 string[i] = '&';
663 }
664 }
665}
666
667void print_help(void) {
668 print_revision(progname, NP_VERSION);
669
670 printf("Copyright (c) 2000 Yves Rubin (rubiyz@yahoo.com)\n");
671 printf(COPYRIGHT, copyright, email);
672
673 printf("%s\n", _("This plugin collects data from the NSClient service running on a"));
674 printf("%s\n", _("Windows NT/2000/XP/2003 server."));
675
676 printf("\n\n");
677
678 print_usage();
679
680 printf(UT_HELP_VRSN);
681 printf(UT_EXTRA_OPTS);
682
683 printf("%s\n", _("Options:"));
684 printf(" %s\n", "-H, --hostname=HOST");
685 printf(" %s\n", _("Name of the host to check"));
686 printf(" %s\n", "-p, --port=INTEGER");
687 printf(" %s", _("Optional port number (default: "));
688 printf("%d)\n", PORT);
689 printf(" %s\n", "-s, --secret=<password>");
690 printf(" %s\n", _("Password needed for the request"));
691 printf(" %s\n", "-w, --warning=INTEGER");
692 printf(" %s\n", _("Threshold which will result in a warning status"));
693 printf(" %s\n", "-c, --critical=INTEGER");
694 printf(" %s\n", _("Threshold which will result in a critical status"));
695 printf(" %s\n", "-t, --timeout=INTEGER");
696 printf(" %s", _("Seconds before connection attempt times out (default: "));
697 printf(" %s\n", "-l, --params=<parameters>");
698 printf(" %s", _("Parameters passed to specified check (see below)"));
699 printf(" %s\n", "-d, --display={SHOWALL}");
700 printf(" %s", _("Display options (currently only SHOWALL works)"));
701 printf(" %s\n", "-u, --unknown-timeout");
702 printf(" %s", _("Return UNKNOWN on timeouts"));
703 printf("%d)\n", DEFAULT_SOCKET_TIMEOUT);
704 printf(" %s\n", "-h, --help");
705 printf(" %s\n", _("Print this help screen"));
706 printf(" %s\n", "-V, --version");
707 printf(" %s\n", _("Print version information"));
708 printf(" %s\n", "-v, --variable=STRING");
709 printf(" %s\n\n", _("Variable to check"));
710 printf("%s\n", _("Valid variables are:"));
711 printf(" %s", "CLIENTVERSION =");
712 printf(" %s\n", _("Get the NSClient version"));
713 printf(" %s\n", _("If -l <version> is specified, will return warning if versions differ."));
714 printf(" %s\n", "CPULOAD =");
715 printf(" %s\n", _("Average CPU load on last x minutes."));
716 printf(" %s\n", _("Request a -l parameter with the following syntax:"));
717 printf(" %s\n", _("-l <minutes range>,<warning threshold>,<critical threshold>."));
718 printf(" %s\n", _("<minute range> should be less than 24*60."));
719 printf(" %s\n", _("Thresholds are percentage and up to 10 requests can be done in one shot."));
720 printf(" %s\n", "ie: -l 60,90,95,120,90,95");
721 printf(" %s\n", "UPTIME =");
722 printf(" %s\n", _("Get the uptime of the machine."));
723 printf(" %s\n", _("-l <unit> "));
724 printf(" %s\n", _("<unit> = seconds, minutes, hours, or days. (default: minutes)"));
725 printf(" %s\n", _("Thresholds will use the unit specified above."));
726 printf(" %s\n", "USEDDISKSPACE =");
727 printf(" %s\n", _("Size and percentage of disk use."));
728 printf(" %s\n", _("Request a -l parameter containing the drive letter only."));
729 printf(" %s\n", _("Warning and critical thresholds can be specified with -w and -c."));
730 printf(" %s\n", "MEMUSE =");
731 printf(" %s\n", _("Memory use."));
732 printf(" %s\n", _("Warning and critical thresholds can be specified with -w and -c."));
733 printf(" %s\n", "SERVICESTATE =");
734 printf(" %s\n", _("Check the state of one or several services."));
735 printf(" %s\n", _("Request a -l parameters with the following syntax:"));
736 printf(" %s\n", _("-l <service1>,<service2>,<service3>,..."));
737 printf(" %s\n", _("You can specify -d SHOWALL in case you want to see working services"));
738 printf(" %s\n", _("in the returned string."));
739 printf(" %s\n", "PROCSTATE =");
740 printf(" %s\n", _("Check if one or several process are running."));
741 printf(" %s\n", _("Same syntax as SERVICESTATE."));
742 printf(" %s\n", "COUNTER =");
743 printf(" %s\n", _("Check any performance counter of Windows NT/2000."));
744 printf(" %s\n", _("Request a -l parameters with the following syntax:"));
745 printf(" %s\n", _("-l \"\\\\<performance object>\\\\counter\",\"<description>"));
746 printf(" %s\n", _("The <description> parameter is optional and is given to a printf "));
747 printf(" %s\n", _("output command which requires a float parameter."));
748 printf(" %s\n", _("If <description> does not include \"%%\", it is used as a label."));
749 printf(" %s\n", _("Some examples:"));
750 printf(" %s\n", "\"Paging file usage is %%.2f %%%%\"");
751 printf(" %s\n", "\"%%.f %%%% paging file used.\"");
752 printf(" %s\n", "INSTANCES =");
753 printf(" %s\n", _("Check any performance counter object of Windows NT/2000."));
754 printf(" %s\n",
755 _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>"));
756 printf(" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),"));
757 printf(" %s\n", _("if it is two words, it should be enclosed in quotes"));
758 printf(" %s\n", _("The returned results will be a comma-separated list of instances on "));
759 printf(" %s\n", _(" the selected computer for that object."));
760 printf(" %s\n",
761 _("The purpose of this is to be run from command line to determine what instances"));
762 printf(" %s\n",
763 _(" are available for monitoring without having to log onto the Windows server"));
764 printf(" %s\n", _(" to run Perfmon directly."));
765 printf(" %s\n",
766 _("It can also be used in scripts that automatically create the monitoring service"));
767 printf(" %s\n", _(" configuration files."));
768 printf(" %s\n", _("Some examples:"));
769 printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process"));
770
771 printf("%s\n", _("Notes:"));
772 printf(" %s\n",
773 _("- The NSClient service should be running on the server to get any information"));
774 printf(" %s\n", "(http://nsclient.ready2run.nl).");
775 printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds"));
776 printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error"));
777 printf(" %s\n",
778 _("output when this happens contains \"Cannot map xxxxx to protocol number\"."));
779 printf(" %s\n", _("One fix for this is to change the port to something else on check_nt "));
780 printf(" %s\n", _("and on the client service it\'s connecting to."));
781
782 printf(UT_SUPPORT);
783}
784
785void print_usage(void) {
786 printf("%s\n", _("Usage:"));
787 printf("%s -H host -v variable [-p port] [-w warning] [-c critical]\n", progname);
788 printf("[-l params] [-d SHOWALL] [-u] [-t timeout]\n");
789}
diff --git a/plugins/check_nt.d/config.h b/plugins/check_nt.d/config.h
deleted file mode 100644
index 431889cb..00000000
--- a/plugins/check_nt.d/config.h
+++ /dev/null
@@ -1,53 +0,0 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PORT = 1248,
8};
9
10enum checkvars {
11 CHECK_NONE,
12 CHECK_CLIENTVERSION,
13 CHECK_CPULOAD,
14 CHECK_UPTIME,
15 CHECK_USEDDISKSPACE,
16 CHECK_SERVICESTATE,
17 CHECK_PROCSTATE,
18 CHECK_MEMUSE,
19 CHECK_COUNTER,
20 CHECK_FILEAGE,
21 CHECK_INSTANCES
22};
23
24typedef struct {
25 char *server_address;
26 int server_port;
27 char *req_password;
28 enum checkvars vars_to_check;
29 bool show_all;
30 char *value_list;
31 bool check_warning_value;
32 unsigned long warning_value;
33 bool check_critical_value;
34 unsigned long critical_value;
35} check_nt_config;
36
37check_nt_config check_nt_config_init() {
38 check_nt_config tmp = {
39 .server_address = NULL,
40 .server_port = PORT,
41 .req_password = NULL,
42
43 .vars_to_check = CHECK_NONE,
44 .show_all = false,
45 .value_list = NULL,
46
47 .check_warning_value = false,
48 .warning_value = 0,
49 .check_critical_value = false,
50 .critical_value = 0,
51 };
52 return tmp;
53}
diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c
deleted file mode 100644
index b22cc3c1..00000000
--- a/plugins/check_ntp.c
+++ /dev/null
@@ -1,947 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_ntp plugin
4 *
5 * License: GPL
6 * Copyright (c) 2006 Sean Finney <seanius@seanius.net>
7 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
8 *
9 * Description:
10 *
11 * This file contains the check_ntp plugin
12 *
13 * This plugin to check ntp servers independent of any commandline
14 * programs or external libraries.
15 *
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 *
30 *
31 *****************************************************************************/
32
33const char *progname = "check_ntp";
34const char *copyright = "2006-2024";
35const char *email = "devel@monitoring-plugins.org";
36
37#include "common.h"
38#include "netutils.h"
39#include "utils.h"
40
41static char *server_address = NULL;
42static int verbose = 0;
43static bool do_offset = false;
44static char *owarn = "60";
45static char *ocrit = "120";
46static bool do_jitter = false;
47static char *jwarn = "5000";
48static char *jcrit = "10000";
49
50static int process_arguments(int /*argc*/, char ** /*argv*/);
51static thresholds *offset_thresholds = NULL;
52static thresholds *jitter_thresholds = NULL;
53static void print_help(void);
54void print_usage(void);
55
56/* number of times to perform each request to get a good average. */
57#ifndef AVG_NUM
58# define AVG_NUM 4
59#endif
60
61/* max size of control message data */
62#define MAX_CM_SIZE 468
63
64/* this structure holds everything in an ntp request/response as per rfc1305 */
65typedef struct {
66 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
67 uint8_t stratum; /* clock stratum */
68 int8_t poll; /* polling interval */
69 int8_t precision; /* precision of the local clock */
70 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */
71 uint32_t rtdisp; /* like above, but for max err to primary src */
72 uint32_t refid; /* ref clock identifier */
73 uint64_t refts; /* reference timestamp. local time local clock */
74 uint64_t origts; /* time at which request departed client */
75 uint64_t rxts; /* time at which request arrived at server */
76 uint64_t txts; /* time at which request departed server */
77} ntp_message;
78
79/* this structure holds data about results from querying offset from a peer */
80typedef struct {
81 time_t waiting; /* ts set when we started waiting for a response */
82 int num_responses; /* number of successfully received responses */
83 uint8_t stratum; /* copied verbatim from the ntp_message */
84 double rtdelay; /* converted from the ntp_message */
85 double rtdisp; /* converted from the ntp_message */
86 double offset[AVG_NUM]; /* offsets from each response */
87 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
88} ntp_server_results;
89
90/* this structure holds everything in an ntp control message as per rfc1305 */
91typedef struct {
92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
93 uint8_t op; /* R,E,M bits and Opcode */
94 uint16_t seq; /* Packet sequence */
95 uint16_t status; /* Clock status */
96 uint16_t assoc; /* Association */
97 uint16_t offset; /* Similar to TCP sequence # */
98 uint16_t count; /* # bytes of data */
99 char data[MAX_CM_SIZE]; /* ASCII data of the request */
100 /* NB: not necessarily NULL terminated! */
101} ntp_control_message;
102
103/* this is an association/status-word pair found in control packet responses */
104typedef struct {
105 uint16_t assoc;
106 uint16_t status;
107} ntp_assoc_status_pair;
108
109/* bits 1,2 are the leap indicator */
110#define LI_MASK 0xc0
111#define LI(x) ((x & LI_MASK) >> 6)
112#define LI_SET(x, y) \
113 do { \
114 x |= ((y << 6) & LI_MASK); \
115 } while (0)
116/* and these are the values of the leap indicator */
117#define LI_NOWARNING 0x00
118#define LI_EXTRASEC 0x01
119#define LI_MISSINGSEC 0x02
120#define LI_ALARM 0x03
121/* bits 3,4,5 are the ntp version */
122#define VN_MASK 0x38
123#define VN(x) ((x & VN_MASK) >> 3)
124#define VN_SET(x, y) \
125 do { \
126 x |= ((y << 3) & VN_MASK); \
127 } while (0)
128#define VN_RESERVED 0x02
129/* bits 6,7,8 are the ntp mode */
130#define MODE_MASK 0x07
131#define MODE(x) (x & MODE_MASK)
132#define MODE_SET(x, y) \
133 do { \
134 x |= (y & MODE_MASK); \
135 } while (0)
136/* here are some values */
137#define MODE_CLIENT 0x03
138#define MODE_CONTROLMSG 0x06
139/* In control message, bits 8-10 are R,E,M bits */
140#define REM_MASK 0xe0
141#define REM_RESP 0x80
142#define REM_ERROR 0x40
143#define REM_MORE 0x20
144/* In control message, bits 11 - 15 are opcode */
145#define OP_MASK 0x1f
146#define OP_SET(x, y) \
147 do { \
148 x |= (y & OP_MASK); \
149 } while (0)
150#define OP_READSTAT 0x01
151#define OP_READVAR 0x02
152/* In peer status bytes, bits 6,7,8 determine clock selection status */
153#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07)
154#define PEER_INCLUDED 0x04
155#define PEER_SYNCSOURCE 0x06
156
157/**
158 ** a note about the 32-bit "fixed point" numbers:
159 **
160 they are divided into halves, each being a 16-bit int in network byte order:
161 - the first 16 bits are an int on the left side of a decimal point.
162 - the second 16 bits represent a fraction n/(2^16)
163 likewise for the 64-bit "fixed point" numbers with everything doubled :)
164 **/
165
166/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point"
167 number. note that these can be used as lvalues too */
168#define L16(x) (((uint16_t *)&x)[0])
169#define R16(x) (((uint16_t *)&x)[1])
170/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
171 number. these too can be used as lvalues */
172#define L32(x) (((uint32_t *)&x)[0])
173#define R32(x) (((uint32_t *)&x)[1])
174
175/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */
176#define EPOCHDIFF 0x83aa7e80UL
177
178/* extract a 32-bit ntp fixed point number into a double */
179#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0)
180
181/* likewise for a 64-bit ntp fp number */
182#define NTP64asDOUBLE(n) \
183 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \
184 (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \
185 : 0)
186
187/* convert a struct timeval to a double */
188#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec))
189
190/* convert an ntp 64-bit fp number to a struct timeval */
191#define NTP64toTV(n, t) \
192 do { \
193 if (!n) \
194 t.tv_sec = t.tv_usec = 0; \
195 else { \
196 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \
197 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \
198 } \
199 } while (0)
200
201/* convert a struct timeval to an ntp 64-bit fp number */
202#define TVtoNTP64(t, n) \
203 do { \
204 if (!t.tv_usec && !t.tv_sec) \
205 n = 0x0UL; \
206 else { \
207 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \
208 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \
209 } \
210 } while (0)
211
212/* NTP control message header is 12 bytes, plus any data in the data
213 * field, plus null padding to the nearest 32-bit boundary per rfc.
214 */
215#define SIZEOF_NTPCM(m) \
216 (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0))
217
218/* finally, a little helper or two for debugging: */
219#define DBG(x) \
220 do { \
221 if (verbose > 1) { \
222 x; \
223 } \
224 } while (0);
225#define PRINTSOCKADDR(x) \
226 do { \
227 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
228 } while (0);
229
230/* calculate the offset of the local clock */
231static inline double calc_offset(const ntp_message *m, const struct timeval *t) {
232 double client_tx, peer_rx, peer_tx, client_rx;
233 client_tx = NTP64asDOUBLE(m->origts);
234 peer_rx = NTP64asDOUBLE(m->rxts);
235 peer_tx = NTP64asDOUBLE(m->txts);
236 client_rx = TVasDOUBLE((*t));
237 return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx)));
238}
239
240/* print out a ntp packet in human readable/debuggable format */
241void print_ntp_message(const ntp_message *p) {
242 struct timeval ref, orig, rx, tx;
243
244 NTP64toTV(p->refts, ref);
245 NTP64toTV(p->origts, orig);
246 NTP64toTV(p->rxts, rx);
247 NTP64toTV(p->txts, tx);
248
249 printf("packet contents:\n");
250 printf("\tflags: 0x%.2x\n", p->flags);
251 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK);
252 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK);
253 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK);
254 printf("\tstratum = %d\n", p->stratum);
255 printf("\tpoll = %g\n", pow(2, p->poll));
256 printf("\tprecision = %g\n", pow(2, p->precision));
257 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay));
258 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp));
259 printf("\trefid = %x\n", p->refid);
260 printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts));
261 printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts));
262 printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts));
263 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts));
264}
265
266void print_ntp_control_message(const ntp_control_message *p) {
267 int i = 0, numpeers = 0;
268 const ntp_assoc_status_pair *peer = NULL;
269
270 printf("control packet contents:\n");
271 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op);
272 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK);
273 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK);
274 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK);
275 printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP);
276 printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE);
277 printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR);
278 printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK);
279 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq));
280 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status));
281 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc));
282 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset));
283 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count));
284 numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair));
285 if (p->op & REM_RESP && p->op & OP_READSTAT) {
286 peer = (ntp_assoc_status_pair *)p->data;
287 for (i = 0; i < numpeers; i++) {
288 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
289 if (PEER_SEL(peer[i].status) >= PEER_INCLUDED) {
290 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
291 printf(" <-- current sync source");
292 } else {
293 printf(" <-- current sync candidate");
294 }
295 }
296 printf("\n");
297 }
298 }
299}
300
301void setup_request(ntp_message *p) {
302 struct timeval t;
303
304 memset(p, 0, sizeof(ntp_message));
305 LI_SET(p->flags, LI_ALARM);
306 VN_SET(p->flags, 4);
307 MODE_SET(p->flags, MODE_CLIENT);
308 p->poll = 4;
309 p->precision = (int8_t)0xfa;
310 L16(p->rtdelay) = htons(1);
311 L16(p->rtdisp) = htons(1);
312
313 gettimeofday(&t, NULL);
314 TVtoNTP64(t, p->txts);
315}
316
317/* select the "best" server from a list of servers, and return its index.
318 * this is done by filtering servers based on stratum, dispersion, and
319 * finally round-trip delay. */
320int best_offset_server(const ntp_server_results *slist, int nservers) {
321 int cserver = 0, best_server = -1;
322
323 /* for each server */
324 for (cserver = 0; cserver < nservers; cserver++) {
325 /* We don't want any servers that fails these tests */
326 /* Sort out servers that didn't respond or responede with a 0 stratum;
327 * stratum 0 is for reference clocks so no NTP server should ever report
328 * a stratum 0 */
329 if (slist[cserver].stratum == 0) {
330 if (verbose) {
331 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
332 }
333 continue;
334 }
335 /* Sort out servers with error flags */
336 if (LI(slist[cserver].flags) == LI_ALARM) {
337 if (verbose) {
338 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
339 }
340 continue;
341 }
342
343 /* If we don't have a server yet, use the first one */
344 if (best_server == -1) {
345 best_server = cserver;
346 DBG(printf("using peer %d as our first candidate\n", best_server));
347 continue;
348 }
349
350 /* compare the server to the best one we've seen so far */
351 /* does it have an equal or better stratum? */
352 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server));
353 if (slist[cserver].stratum <= slist[best_server].stratum) {
354 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server));
355 /* does it have an equal or better dispersion? */
356 if (slist[cserver].rtdisp <= slist[best_server].rtdisp) {
357 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server));
358 /* does it have a better rtdelay? */
359 if (slist[cserver].rtdelay < slist[best_server].rtdelay) {
360 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server));
361 best_server = cserver;
362 DBG(printf("peer %d is now our best candidate\n", best_server));
363 }
364 }
365 }
366 }
367
368 if (best_server >= 0) {
369 DBG(printf("best server selected: peer %d\n", best_server));
370 return best_server;
371 } else {
372 DBG(printf("no peers meeting synchronization criteria :(\n"));
373 return -1;
374 }
375}
376
377/* do everything we need to get the total average offset
378 * - we use a certain amount of parallelization with poll() to ensure
379 * we don't waste time sitting around waiting for single packets.
380 * - we also "manually" handle resolving host names and connecting, because
381 * we have to do it in a way that our lazy macros don't handle currently :( */
382double offset_request(const char *host, int *status) {
383 int i = 0, ga_result = 0, num_hosts = 0, *socklist = NULL, respnum = 0;
384 int servers_completed = 0, one_read = 0, servers_readable = 0, best_index = -1;
385 time_t now_time = 0, start_ts = 0;
386 ntp_message *req = NULL;
387 double avg_offset = 0.;
388 struct timeval recv_time;
389 struct addrinfo *ai = NULL, *ai_tmp = NULL, hints;
390 struct pollfd *ufds = NULL;
391 ntp_server_results *servers = NULL;
392
393 /* setup hints to only return results from getaddrinfo that we'd like */
394 memset(&hints, 0, sizeof(struct addrinfo));
395 hints.ai_family = address_family;
396 hints.ai_protocol = IPPROTO_UDP;
397 hints.ai_socktype = SOCK_DGRAM;
398
399 /* fill in ai with the list of hosts resolved by the host name */
400 ga_result = getaddrinfo(host, "123", &hints, &ai);
401 if (ga_result != 0) {
402 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
403 }
404
405 /* count the number of returned hosts, and allocate stuff accordingly */
406 for (ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
407 num_hosts++;
408 }
409 req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts);
410 if (req == NULL) {
411 die(STATE_UNKNOWN, "can not allocate ntp message array");
412 }
413 socklist = (int *)malloc(sizeof(int) * num_hosts);
414 if (socklist == NULL) {
415 die(STATE_UNKNOWN, "can not allocate socket array");
416 }
417 ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts);
418 if (ufds == NULL) {
419 die(STATE_UNKNOWN, "can not allocate socket array");
420 }
421 servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts);
422 if (servers == NULL) {
423 die(STATE_UNKNOWN, "can not allocate server array");
424 }
425 memset(servers, 0, sizeof(ntp_server_results) * num_hosts);
426 DBG(printf("Found %d peers to check\n", num_hosts));
427
428 /* setup each socket for writing, and the corresponding struct pollfd */
429 ai_tmp = ai;
430 for (i = 0; ai_tmp; i++) {
431 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
432 if (socklist[i] == -1) {
433 perror(NULL);
434 die(STATE_UNKNOWN, "can not create new socket");
435 }
436 if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) {
437 /* don't die here, because it is enough if there is one server
438 answering in time. This also would break for dual ipv4/6 stacked
439 ntp servers when the client only supports on of them.
440 */
441 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno)));
442 } else {
443 ufds[i].fd = socklist[i];
444 ufds[i].events = POLLIN;
445 ufds[i].revents = 0;
446 }
447 ai_tmp = ai_tmp->ai_next;
448 }
449
450 /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds
451 * have passed in order to ensure post-processing and jitter time. */
452 now_time = start_ts = time(NULL);
453 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) {
454 /* loop through each server and find each one which hasn't
455 * been touched in the past second or so and is still lacking
456 * some responses. for each of these servers, send a new request,
457 * and update the "waiting" timestamp with the current time. */
458 now_time = time(NULL);
459
460 for (i = 0; i < num_hosts; i++) {
461 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) {
462 if (verbose && servers[i].waiting != 0) {
463 printf("re-");
464 }
465 if (verbose) {
466 printf("sending request to peer %d\n", i);
467 }
468 setup_request(&req[i]);
469 write(socklist[i], &req[i], sizeof(ntp_message));
470 servers[i].waiting = now_time;
471 break;
472 }
473 }
474
475 /* quickly poll for any sockets with pending data */
476 servers_readable = poll(ufds, num_hosts, 100);
477 if (servers_readable == -1) {
478 perror("polling ntp sockets");
479 die(STATE_UNKNOWN, "communication errors");
480 }
481
482 /* read from any sockets with pending data */
483 for (i = 0; servers_readable && i < num_hosts; i++) {
484 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) {
485 if (verbose) {
486 printf("response from peer %d: ", i);
487 }
488
489 read(ufds[i].fd, &req[i], sizeof(ntp_message));
490 gettimeofday(&recv_time, NULL);
491 DBG(print_ntp_message(&req[i]));
492 respnum = servers[i].num_responses++;
493 servers[i].offset[respnum] = calc_offset(&req[i], &recv_time);
494 if (verbose) {
495 printf("offset %.10g\n", servers[i].offset[respnum]);
496 }
497 servers[i].stratum = req[i].stratum;
498 servers[i].rtdisp = NTP32asDOUBLE(req[i].rtdisp);
499 servers[i].rtdelay = NTP32asDOUBLE(req[i].rtdelay);
500 servers[i].waiting = 0;
501 servers[i].flags = req[i].flags;
502 servers_readable--;
503 one_read = 1;
504 if (servers[i].num_responses == AVG_NUM) {
505 servers_completed++;
506 }
507 }
508 }
509 /* lather, rinse, repeat. */
510 }
511
512 if (one_read == 0) {
513 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
514 }
515
516 /* now, pick the best server from the list */
517 best_index = best_offset_server(servers, num_hosts);
518 if (best_index < 0) {
519 *status = STATE_UNKNOWN;
520 } else {
521 /* finally, calculate the average offset */
522 for (i = 0; i < servers[best_index].num_responses; i++) {
523 avg_offset += servers[best_index].offset[i];
524 }
525 avg_offset /= servers[best_index].num_responses;
526 }
527
528 /* cleanup */
529 /* FIXME: Not closing the socket to avoid reuse of the local port
530 * which can cause old NTP packets to be read instead of NTP control
531 * packets in jitter_request(). THERE MUST BE ANOTHER WAY...
532 * for(j=0; j<num_hosts; j++){ close(socklist[j]); } */
533 free(socklist);
534 free(ufds);
535 free(servers);
536 free(req);
537 freeaddrinfo(ai);
538
539 if (verbose) {
540 printf("overall average offset: %.10g\n", avg_offset);
541 }
542 return avg_offset;
543}
544
545void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) {
546 memset(p, 0, sizeof(ntp_control_message));
547 LI_SET(p->flags, LI_NOWARNING);
548 VN_SET(p->flags, VN_RESERVED);
549 MODE_SET(p->flags, MODE_CONTROLMSG);
550 OP_SET(p->op, opcode);
551 p->seq = htons(seq);
552 /* Remaining fields are zero for requests */
553}
554
555/* XXX handle responses with the error bit set */
556double jitter_request(int *status) {
557 int conn = -1, i, npeers = 0, num_candidates = 0;
558 bool syncsource_found = false;
559 int run = 0, min_peer_sel = PEER_INCLUDED, num_selected = 0, num_valid = 0;
560 int peers_size = 0, peer_offset = 0;
561 ntp_assoc_status_pair *peers = NULL;
562 ntp_control_message req;
563 const char *getvar = "jitter";
564 double rval = 0.0, jitter = -1.0;
565 char *startofvalue = NULL, *nptr = NULL;
566 void *tmp;
567
568 /* Long-winded explanation:
569 * Getting the jitter requires a number of steps:
570 * 1) Send a READSTAT request.
571 * 2) Interpret the READSTAT reply
572 * a) The data section contains a list of peer identifiers (16 bits)
573 * and associated status words (16 bits)
574 * b) We want the value of 0x06 in the SEL (peer selection) value,
575 * which means "current synchronizatin source". If that's missing,
576 * we take anything better than 0x04 (see the rfc for details) but
577 * set a minimum of warning.
578 * 3) Send a READVAR request for information on each peer identified
579 * in 2b greater than the minimum selection value.
580 * 4) Extract the jitter value from the data[] (it's ASCII)
581 */
582 my_udp_connect(server_address, 123, &conn);
583
584 /* keep sending requests until the server stops setting the
585 * REM_MORE bit, though usually this is only 1 packet. */
586 do {
587 setup_control_request(&req, OP_READSTAT, 1);
588 DBG(printf("sending READSTAT request"));
589 write(conn, &req, SIZEOF_NTPCM(req));
590 DBG(print_ntp_control_message(&req));
591 /* Attempt to read the largest size packet possible */
592 req.count = htons(MAX_CM_SIZE);
593 DBG(printf("receiving READSTAT response"))
594 read(conn, &req, SIZEOF_NTPCM(req));
595 DBG(print_ntp_control_message(&req));
596 /* Each peer identifier is 4 bytes in the data section, which
597 * we represent as a ntp_assoc_status_pair datatype.
598 */
599 peers_size += ntohs(req.count);
600 if ((tmp = realloc(peers, peers_size)) == NULL) {
601 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
602 }
603 peers = tmp;
604 memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count));
605 npeers = peers_size / sizeof(ntp_assoc_status_pair);
606 peer_offset += ntohs(req.count);
607 } while (req.op & REM_MORE);
608
609 /* first, let's find out if we have a sync source, or if there are
610 * at least some candidates. in the case of the latter we'll issue
611 * a warning but go ahead with the check on them. */
612 for (i = 0; i < npeers; i++) {
613 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
614 num_candidates++;
615 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
616 syncsource_found = true;
617 min_peer_sel = PEER_SYNCSOURCE;
618 }
619 }
620 }
621 if (verbose) {
622 printf("%d candidate peers available\n", num_candidates);
623 }
624 if (verbose && syncsource_found) {
625 printf("synchronization source found\n");
626 }
627 if (!syncsource_found) {
628 *status = STATE_UNKNOWN;
629 if (verbose) {
630 printf("warning: no synchronization source found\n");
631 }
632 }
633
634 for (run = 0; run < AVG_NUM; run++) {
635 if (verbose) {
636 printf("jitter run %d of %d\n", run + 1, AVG_NUM);
637 }
638 for (i = 0; i < npeers; i++) {
639 /* Only query this server if it is the current sync source */
640 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
641 char jitter_data[MAX_CM_SIZE + 1];
642 size_t jitter_data_count;
643
644 num_selected++;
645 setup_control_request(&req, OP_READVAR, 2);
646 req.assoc = peers[i].assoc;
647 /* By spec, putting the variable name "jitter" in the request
648 * should cause the server to provide _only_ the jitter value.
649 * thus reducing net traffic, guaranteeing us only a single
650 * datagram in reply, and making interpretation much simpler
651 */
652 /* Older servers doesn't know what jitter is, so if we get an
653 * error on the first pass we redo it with "dispersion" */
654 strncpy(req.data, getvar, MAX_CM_SIZE - 1);
655 req.count = htons(strlen(getvar));
656 DBG(printf("sending READVAR request...\n"));
657 write(conn, &req, SIZEOF_NTPCM(req));
658 DBG(print_ntp_control_message(&req));
659
660 req.count = htons(MAX_CM_SIZE);
661 DBG(printf("receiving READVAR response...\n"));
662 read(conn, &req, SIZEOF_NTPCM(req));
663 DBG(print_ntp_control_message(&req));
664
665 if (req.op & REM_ERROR && strstr(getvar, "jitter")) {
666 if (verbose) {
667 printf("The 'jitter' command failed (old ntp server?)\nRestarting with "
668 "'dispersion'...\n");
669 }
670 getvar = "dispersion";
671 num_selected--;
672 i--;
673 continue;
674 }
675
676 /* get to the float value */
677 if (verbose) {
678 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc));
679 }
680 if ((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)) {
681 die(STATE_UNKNOWN, _("jitter response too large (%lu bytes)\n"),
682 (unsigned long)jitter_data_count);
683 }
684 memcpy(jitter_data, req.data, jitter_data_count);
685 jitter_data[jitter_data_count] = '\0';
686 startofvalue = strchr(jitter_data, '=');
687 if (startofvalue != NULL) {
688 startofvalue++;
689 jitter = strtod(startofvalue, &nptr);
690 }
691 if (startofvalue == NULL || startofvalue == nptr) {
692 printf("warning: unable to read server jitter response.\n");
693 *status = STATE_UNKNOWN;
694 } else {
695 if (verbose) {
696 printf("%g\n", jitter);
697 }
698 num_valid++;
699 rval += jitter;
700 }
701 }
702 }
703 if (verbose) {
704 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
705 }
706 }
707
708 rval = num_valid ? rval / num_valid : -1.0;
709
710 close(conn);
711 if (peers != NULL) {
712 free(peers);
713 }
714 /* If we return -1.0, it means no synchronization source was found */
715 return rval;
716}
717
718int process_arguments(int argc, char **argv) {
719 int c;
720 int option = 0;
721 static struct option longopts[] = {
722 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
723 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'},
724 {"use-ipv6", no_argument, 0, '6'}, {"warning", required_argument, 0, 'w'},
725 {"critical", required_argument, 0, 'c'}, {"jwarn", required_argument, 0, 'j'},
726 {"jcrit", required_argument, 0, 'k'}, {"timeout", required_argument, 0, 't'},
727 {"hostname", required_argument, 0, 'H'}, {0, 0, 0, 0}};
728
729 if (argc < 2) {
730 usage("\n");
731 }
732
733 while (1) {
734 c = getopt_long(argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option);
735 if (c == -1 || c == EOF || c == 1) {
736 break;
737 }
738
739 switch (c) {
740 case 'h':
741 print_help();
742 exit(STATE_UNKNOWN);
743 break;
744 case 'V':
745 print_revision(progname, NP_VERSION);
746 exit(STATE_UNKNOWN);
747 break;
748 case 'v':
749 verbose++;
750 break;
751 case 'w':
752 do_offset = true;
753 owarn = optarg;
754 break;
755 case 'c':
756 do_offset = true;
757 ocrit = optarg;
758 break;
759 case 'j':
760 do_jitter = true;
761 jwarn = optarg;
762 break;
763 case 'k':
764 do_jitter = true;
765 jcrit = optarg;
766 break;
767 case 'H':
768 if (!is_host(optarg)) {
769 usage2(_("Invalid hostname/address"), optarg);
770 }
771 server_address = strdup(optarg);
772 break;
773 case 't':
774 socket_timeout = atoi(optarg);
775 break;
776 case '4':
777 address_family = AF_INET;
778 break;
779 case '6':
780#ifdef USE_IPV6
781 address_family = AF_INET6;
782#else
783 usage4(_("IPv6 support not available"));
784#endif
785 break;
786 case '?':
787 /* print short usage statement if args not parsable */
788 usage5();
789 break;
790 }
791 }
792
793 if (server_address == NULL) {
794 usage4(_("Hostname was not supplied"));
795 }
796
797 return 0;
798}
799
800char *perfd_offset(double offset) {
801 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
802 offset_thresholds->critical->end, false, 0, false, 0);
803}
804
805char *perfd_jitter(double jitter) {
806 return fperfdata("jitter", jitter, "s", do_jitter, jitter_thresholds->warning->end, do_jitter,
807 jitter_thresholds->critical->end, true, 0, false, 0);
808}
809
810int main(int argc, char *argv[]) {
811 int result, offset_result, jitter_result;
812 double offset = 0, jitter = 0;
813 char *result_line, *perfdata_line;
814
815 setlocale(LC_ALL, "");
816 bindtextdomain(PACKAGE, LOCALEDIR);
817 textdomain(PACKAGE);
818
819 result = offset_result = jitter_result = STATE_OK;
820
821 /* Parse extra opts if any */
822 argv = np_extra_opts(&argc, argv, progname);
823
824 if (process_arguments(argc, argv) == ERROR) {
825 usage4(_("Could not parse arguments"));
826 }
827
828 set_thresholds(&offset_thresholds, owarn, ocrit);
829 set_thresholds(&jitter_thresholds, jwarn, jcrit);
830
831 /* initialize alarm signal handling */
832 signal(SIGALRM, socket_timeout_alarm_handler);
833
834 /* set socket timeout */
835 alarm(socket_timeout);
836
837 offset = offset_request(server_address, &offset_result);
838 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN.
839 * Now we'll only do that is the offset thresholds were set */
840 if (do_offset && offset_result == STATE_UNKNOWN) {
841 result = STATE_CRITICAL;
842 } else {
843 result = get_status(fabs(offset), offset_thresholds);
844 }
845
846 /* If not told to check the jitter, we don't even send packets.
847 * jitter is checked using NTP control packets, which not all
848 * servers recognize. Trying to check the jitter on OpenNTPD
849 * (for example) will result in an error
850 */
851 if (do_jitter) {
852 jitter = jitter_request(&jitter_result);
853 result = max_state_alt(result, get_status(jitter, jitter_thresholds));
854 /* -1 indicates that we couldn't calculate the jitter
855 * Only overrides STATE_OK from the offset */
856 if (jitter == -1.0 && result == STATE_OK) {
857 result = STATE_UNKNOWN;
858 }
859 }
860 result = max_state_alt(result, jitter_result);
861
862 switch (result) {
863 case STATE_CRITICAL:
864 xasprintf(&result_line, _("NTP CRITICAL:"));
865 break;
866 case STATE_WARNING:
867 xasprintf(&result_line, _("NTP WARNING:"));
868 break;
869 case STATE_OK:
870 xasprintf(&result_line, _("NTP OK:"));
871 break;
872 default:
873 xasprintf(&result_line, _("NTP UNKNOWN:"));
874 break;
875 }
876 if (offset_result == STATE_UNKNOWN) {
877 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
878 xasprintf(&perfdata_line, "");
879 } else {
880 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
881 xasprintf(&perfdata_line, "%s", perfd_offset(offset));
882 }
883 if (do_jitter) {
884 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter);
885 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter));
886 }
887 printf("%s|%s\n", result_line, perfdata_line);
888
889 if (server_address != NULL) {
890 free(server_address);
891 }
892 return result;
893}
894
895void print_help(void) {
896 print_revision(progname, NP_VERSION);
897
898 printf("Copyright (c) 2006 Sean Finney\n");
899 printf(COPYRIGHT, copyright, email);
900
901 printf("%s\n", _("This plugin checks the selected ntp server"));
902
903 printf("\n\n");
904
905 print_usage();
906 printf(UT_HELP_VRSN);
907 printf(UT_EXTRA_OPTS);
908 printf(UT_HOST_PORT, 'p', "123");
909 printf(UT_IPv46);
910 printf(" %s\n", "-w, --warning=THRESHOLD");
911 printf(" %s\n", _("Offset to result in warning status (seconds)"));
912 printf(" %s\n", "-c, --critical=THRESHOLD");
913 printf(" %s\n", _("Offset to result in critical status (seconds)"));
914 printf(" %s\n", "-j, --jwarn=THRESHOLD");
915 printf(" %s\n", _("Warning threshold for jitter"));
916 printf(" %s\n", "-k, --jcrit=THRESHOLD");
917 printf(" %s\n", _("Critical threshold for jitter"));
918 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
919 printf(UT_VERBOSE);
920
921 printf("\n");
922 printf("%s\n", _("Notes:"));
923 printf(UT_THRESHOLDS_NOTES);
924
925 printf("\n");
926 printf("%s\n", _("Examples:"));
927 printf(" %s\n", _("Normal offset check:"));
928 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1"));
929 printf("\n");
930 printf(" %s\n",
931 _("Check jitter too, avoiding critical notifications if jitter isn't available"));
932 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
933 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
934
935 printf(UT_SUPPORT);
936
937 printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
938 printf("%s\n\n", _("check_ntp_time instead."));
939}
940
941void print_usage(void) {
942 printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
943 printf("%s\n\n", _("check_ntp_time instead."));
944 printf("%s\n", _("Usage:"));
945 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n",
946 progname);
947}
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index 1300faea..9e0beb9c 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -348,7 +348,6 @@ static offset_request_wrapper offset_request(const char *host, const char *port,
348 is_socket = false; 348 is_socket = false;
349 349
350 /* fill in ai with the list of hosts resolved by the host name */ 350 /* fill in ai with the list of hosts resolved by the host name */
351 struct addrinfo *addresses = NULL;
352 int ga_result = getaddrinfo(host, port, &hints, &addresses); 351 int ga_result = getaddrinfo(host, port, &hints, &addresses);
353 if (ga_result != 0) { 352 if (ga_result != 0) {
354 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result)); 353 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
diff --git a/plugins/tests/check_nt.t b/plugins/tests/check_nt.t
deleted file mode 100755
index 223d4933..00000000
--- a/plugins/tests/check_nt.t
+++ /dev/null
@@ -1,80 +0,0 @@
1#! /usr/bin/perl -w -I ..
2#
3# Test check_nt by having a stub check_nt daemon
4#
5
6use strict;
7use Test::More;
8use NPTest;
9use FindBin qw($Bin);
10
11use IO::Socket;
12use IO::Select;
13use POSIX;
14
15my $port = 50000 + int(rand(1000));
16
17my $pid = fork();
18if ($pid) {
19 # Parent
20 #print "parent\n";
21 # give our webserver some time to startup
22 sleep(1);
23} else {
24 # Child
25 #print "child\n";
26
27 my $server = IO::Socket::INET->new(
28 LocalPort => $port,
29 Type => SOCK_STREAM,
30 Reuse => 1,
31 Proto => "tcp",
32 Listen => 10,
33 ) or die "Cannot be a tcp server on port $port: $@";
34
35 $server->autoflush(1);
36
37 print "Please contact me at port $port\n";
38 while (my $client = $server->accept ) {
39 my $data = "";
40 my $rv = $client->recv($data, POSIX::BUFSIZ, 0);
41
42 my ($password, $command, $arg) = split('&', $data);
43
44 if ($command eq "4") {
45 if ($arg eq "c") {
46 print $client "930000000&1000000000";
47 } elsif ($arg eq "d") {
48 print $client "UNKNOWN: Drive is not a fixed drive";
49 }
50 }
51 }
52 exit;
53}
54
55END { if ($pid) { print "Killing $pid\n"; kill "INT", $pid } };
56
57if ($ARGV[0] && $ARGV[0] eq "-d") {
58 sleep 1000;
59}
60
61if (-x "./check_nt") {
62 plan tests => 5;
63} else {
64 plan skip_all => "No check_nt compiled";
65}
66
67my $result;
68my $command = "./check_nt -H 127.0.0.1 -p $port";
69
70$result = NPTest->testCmd( "$command -v USEDDISKSPACE -l c" );
71is( $result->return_code, 0, "USEDDISKSPACE c");
72is( $result->output, q{c:\ - total: 0.93 Gb - used: 0.07 Gb (7%) - free 0.87 Gb (93%) | 'c:\ Used Space'=0.07Gb;0.00;0.00;0.00;0.93}, "Output right" );
73
74$result = NPTest->testCmd( "$command -v USEDDISKSPACE -l d" );
75is( $result->return_code, 3, "USEDDISKSPACE d - invalid");
76is( $result->output, "Free disk space : Invalid drive", "Output right" );
77
78$result = NPTest->testCmd( "./check_nt -v USEDDISKSPACE -l d" );
79is( $result->return_code, 3, "Fail if -H missing");
80