summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format2
-rw-r--r--.github/monitoring-plugins.spec14
-rw-r--r--.github/os_detect.sh7
-rw-r--r--.github/workflows/test-next.yml2
-rw-r--r--.github/workflows/test.yml2
-rw-r--r--.gitignore6
-rw-r--r--REQUIREMENTS4
-rw-r--r--configure.ac50
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/output.c93
-rw-r--r--lib/output.h47
-rw-r--r--lib/perfdata.c91
-rw-r--r--lib/perfdata.h16
-rw-r--r--lib/tests/Makefile.am6
-rwxr-xr-xlib/tests/test_disk.t6
-rw-r--r--lib/thresholds.c14
-rw-r--r--lib/thresholds.h3
-rw-r--r--lib/utils_base.c18
-rw-r--r--lib/utils_cmd.c2
-rw-r--r--lib/utils_cmd.h2
-rw-r--r--lib/utils_disk.c255
-rw-r--r--lib/utils_disk.h48
-rw-r--r--lib/utils_tcp.h1
-rw-r--r--plugins-root/Makefile.am7
-rw-r--r--plugins-root/check_dhcp.c867
-rw-r--r--plugins-root/check_dhcp.d/config.h50
-rw-r--r--plugins-root/check_icmp.c2922
-rw-r--r--plugins-root/check_icmp.d/check_icmp_helpers.c134
-rw-r--r--plugins-root/check_icmp.d/check_icmp_helpers.h68
-rw-r--r--plugins-root/check_icmp.d/config.h115
-rw-r--r--plugins-root/t/check_dhcp.t24
-rw-r--r--plugins-root/t/check_icmp.t58
-rw-r--r--plugins/Makefile.am21
-rw-r--r--plugins/check_disk.c1299
-rw-r--r--plugins/check_disk.d/utils_disk.c517
-rw-r--r--plugins/check_disk.d/utils_disk.h157
-rw-r--r--plugins/check_fping.c114
-rw-r--r--plugins/check_fping.d/config.h24
-rw-r--r--plugins/check_http.c10
-rw-r--r--plugins/check_ide_smart.c90
-rw-r--r--plugins/check_ntp_peer.c385
-rw-r--r--plugins/check_ntp_peer.d/config.h67
-rw-r--r--plugins/check_nwstat.c1527
-rw-r--r--plugins/check_ping.c529
-rw-r--r--plugins/check_ping.d/config.h46
-rw-r--r--plugins/check_procs.c1135
-rw-r--r--plugins/check_procs.d/config.h75
-rw-r--r--plugins/check_radius.c541
-rw-r--r--plugins/check_radius.d/config.h42
-rw-r--r--plugins/check_swap.c8
-rw-r--r--plugins/check_swap.d/check_swap.h5
-rw-r--r--plugins/check_swap.d/swap.c102
-rw-r--r--plugins/check_tcp.c777
-rw-r--r--plugins/check_tcp.d/config.h84
-rw-r--r--plugins/check_time.c191
-rw-r--r--plugins/check_time.d/config.h42
-rw-r--r--plugins/check_ups.c274
-rw-r--r--plugins/check_ups.d/config.h55
-rw-r--r--plugins/common.h6
-rw-r--r--plugins/negate.c96
-rw-r--r--plugins/negate.d/config.h24
-rw-r--r--plugins/sslutils.c2
-rw-r--r--plugins/t/check_disk.t220
-rw-r--r--plugins/t/check_ftp.t2
-rw-r--r--plugins/t/check_jabber.t4
-rw-r--r--plugins/t/check_tcp.t12
-rw-r--r--plugins/t/check_udp.t4
-rw-r--r--plugins/tests/test_check_disk.c (renamed from lib/tests/test_disk.c)137
-rwxr-xr-xplugins/tests/test_check_disk.t6
-rw-r--r--plugins/utils.h2
70 files changed, 7148 insertions, 6421 deletions
diff --git a/.clang-format b/.clang-format
index ca411edd..0ff68114 100644
--- a/.clang-format
+++ b/.clang-format
@@ -4,7 +4,7 @@ TabWidth: 4
4AllowShortIfStatementsOnASingleLine: false 4AllowShortIfStatementsOnASingleLine: false
5BreakBeforeBraces: Attach 5BreakBeforeBraces: Attach
6AlignConsecutiveMacros: true 6AlignConsecutiveMacros: true
7ColumnLimit: 140 7ColumnLimit: 100
8IndentPPDirectives: AfterHash 8IndentPPDirectives: AfterHash
9SortIncludes: Never 9SortIncludes: Never
10AllowShortEnumsOnASingleLine: false 10AllowShortEnumsOnASingleLine: false
diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec
index 64ee34f2..10799128 100644
--- a/.github/monitoring-plugins.spec
+++ b/.github/monitoring-plugins.spec
@@ -191,7 +191,6 @@ Requires: %{name}-nt
191Requires: %{name}-ntp 191Requires: %{name}-ntp
192Requires: %{name}-ntp_peer 192Requires: %{name}-ntp_peer
193Requires: %{name}-ntp_time 193Requires: %{name}-ntp_time
194Requires: %{name}-nwstat
195Requires: %{name}-oracle 194Requires: %{name}-oracle
196Requires: %{name}-pgsql 195Requires: %{name}-pgsql
197Requires: %{name}-ping 196Requires: %{name}-ping
@@ -702,19 +701,6 @@ Provides check_ntp_time of the Monitoring Plugins.
702 701
703 702
704 703
705# check_nwstat
706%package nwstat
707Summary: Monitoring Plugins - check_nwstat
708Requires: %{name} = %{version}-%{release}
709
710%description nwstat
711Provides check_nwstat of the Monitoring Plugins.
712
713%files nwstat
714%{plugindir}/check_nwstat
715
716
717
718# check_oracle 704# check_oracle
719%package oracle 705%package oracle
720Summary: Monitoring Plugins - check_oracle 706Summary: Monitoring Plugins - check_oracle
diff --git a/.github/os_detect.sh b/.github/os_detect.sh
index ee9c145d..47c762d3 100644
--- a/.github/os_detect.sh
+++ b/.github/os_detect.sh
@@ -1,10 +1,17 @@
1#!/bin/sh -e 1#!/bin/sh -e
2
3. /etc/os-release
4
2# workaround for really bare-bones Archlinux containers: 5# workaround for really bare-bones Archlinux containers:
3if [ -x "$(command -v pacman)" ]; then 6if [ -x "$(command -v pacman)" ]; then
4 pacman --noconfirm -Sy 7 pacman --noconfirm -Sy
5 pacman --noconfirm -S grep gawk sed 8 pacman --noconfirm -S grep gawk sed
6fi 9fi
7 10
11if [ ${ID} == "fedora" -a ${VERSION_ID} -gt 41 ]; then
12 dnf install -y gawk
13fi
14
8os_release_file= 15os_release_file=
9if [ -s "/etc/os-release" ]; then 16if [ -s "/etc/os-release" ]; then
10 os_release_file="/etc/os-release" 17 os_release_file="/etc/os-release"
diff --git a/.github/workflows/test-next.yml b/.github/workflows/test-next.yml
index 81240759..fd59e85d 100644
--- a/.github/workflows/test-next.yml
+++ b/.github/workflows/test-next.yml
@@ -38,7 +38,7 @@ jobs:
38 ${{ matrix.distro }} \ 38 ${{ matrix.distro }} \
39 /bin/sh -c '${{ matrix.prepare }} && \ 39 /bin/sh -c '${{ matrix.prepare }} && \
40 tools/setup && \ 40 tools/setup && \
41 ./configure --enable-libtap --with-ipv6=no && \ 41 ./configure --enable-libtap && \
42 make && \ 42 make && \
43 make test && \ 43 make test && \
44 make dist && \ 44 make dist && \
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 77ca6585..ce0ec547 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -35,7 +35,7 @@ jobs:
35 ${{ matrix.distro }} \ 35 ${{ matrix.distro }} \
36 /bin/sh -c '${{ matrix.prepare }} && \ 36 /bin/sh -c '${{ matrix.prepare }} && \
37 tools/setup && \ 37 tools/setup && \
38 ./configure --enable-libtap --with-ipv6=no && \ 38 ./configure --enable-libtap && \
39 make && \ 39 make && \
40 make test && \ 40 make test && \
41 make dist && \ 41 make dist && \
diff --git a/.gitignore b/.gitignore
index f9cb37e4..8b14f429 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,7 +114,6 @@ NP-VERSION-FILE
114/lib/tests/Makefile.in 114/lib/tests/Makefile.in
115/lib/tests/test_base64 115/lib/tests/test_base64
116/lib/tests/test_cmd 116/lib/tests/test_cmd
117/lib/tests/test_disk
118/lib/tests/test_tcp 117/lib/tests/test_tcp
119/lib/tests/test_utils 118/lib/tests/test_utils
120/lib/tests/utils_base.Po 119/lib/tests/utils_base.Po
@@ -153,6 +152,8 @@ NP-VERSION-FILE
153/plugins/check_dbi 152/plugins/check_dbi
154/plugins/check_dig 153/plugins/check_dig
155/plugins/check_disk 154/plugins/check_disk
155plugins/check_disk.d/.deps/
156plugins/check_disk.d/.dirstamp
156/plugins/check_dns 157/plugins/check_dns
157/plugins/check_dummy 158/plugins/check_dummy
158/plugins/check_fping 159/plugins/check_fping
@@ -177,7 +178,6 @@ NP-VERSION-FILE
177/plugins/check_ntp 178/plugins/check_ntp
178/plugins/check_ntp_peer 179/plugins/check_ntp_peer
179/plugins/check_ntp_time 180/plugins/check_ntp_time
180/plugins/check_nwstat
181/plugins/check_pgsql 181/plugins/check_pgsql
182/plugins/check_ping 182/plugins/check_ping
183/plugins/check_pop 183/plugins/check_pop
@@ -222,7 +222,7 @@ NP-VERSION-FILE
222/plugins/tests/Makefile 222/plugins/tests/Makefile
223/plugins/tests/Makefile.in 223/plugins/tests/Makefile.in
224/plugins/tests/test_utils 224/plugins/tests/test_utils
225/plugins/tests/test_disk 225/plugins/tests/test_check_disk
226/plugins/tests/test_check_swap 226/plugins/tests/test_check_swap
227/plugins/tests/.deps 227/plugins/tests/.deps
228/plugins/tests/.dirstamp 228/plugins/tests/.dirstamp
diff --git a/REQUIREMENTS b/REQUIREMENTS
index f3b1c01d..551fdb1a 100644
--- a/REQUIREMENTS
+++ b/REQUIREMENTS
@@ -87,10 +87,6 @@ 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_nwstat:
91 - Requires MRTGEXT NLM for Novell Servers
92 http://forge.novell.com/modules/xfmod/project/?mrtgext
93
94check_nt: 90check_nt:
95 - Requires NSClient to run on the NT server to monitor 91 - Requires NSClient to run on the NT server to monitor
96 http://nsclient.ready2run.nl/ 92 http://nsclient.ready2run.nl/
diff --git a/configure.ac b/configure.ac
index 204fc6e3..ce140218 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,10 +181,10 @@ fi
181 181
182# Finally, define tests if we use libtap 182# Finally, define tests if we use libtap
183if test "$enable_libtap" = "yes" ; then 183if test "$enable_libtap" = "yes" ; then
184 EXTRA_TEST="test_utils test_disk test_tcp test_cmd test_base64" 184 EXTRA_TEST="test_utils test_tcp test_cmd test_base64"
185 AC_SUBST(EXTRA_TEST) 185 AC_SUBST(EXTRA_TEST)
186 186
187 EXTRA_PLUGIN_TESTS="tests/test_check_swap" 187 EXTRA_PLUGIN_TESTS="tests/test_check_swap tests/test_check_disk"
188 AC_SUBST(EXTRA_PLUGIN_TESTS) 188 AC_SUBST(EXTRA_PLUGIN_TESTS)
189fi 189fi
190 190
@@ -796,7 +796,7 @@ elif ps axwo 'stat comm vsz rss user uid pid ppid etime args' 2>/dev/null | \
796then 796then
797 ac_cv_ps_varlist="[procstat,&procuid,&procpid,&procppid,&procvsz,&procrss,&procpcpu,procetime,procprog,&pos]" 797 ac_cv_ps_varlist="[procstat,&procuid,&procpid,&procppid,&procvsz,&procrss,&procpcpu,procetime,procprog,&pos]"
798 ac_cv_ps_command="$PATH_TO_PS axwo 'stat uid pid ppid vsz rss pcpu etime comm args'" 798 ac_cv_ps_command="$PATH_TO_PS axwo 'stat uid pid ppid vsz rss pcpu etime comm args'"
799 ac_cv_ps_format="%s %d %d %d %d %d %f %s %s %n" 799 ac_cv_ps_format="%s %u %d %d %d %d %f %s %s %n"
800 ac_cv_ps_cols=10 800 ac_cv_ps_cols=10
801 AC_MSG_RESULT([$ac_cv_ps_command]) 801 AC_MSG_RESULT([$ac_cv_ps_command])
802 802
@@ -1519,21 +1519,47 @@ then
1519fi 1519fi
1520 1520
1521AC_PATH_PROG(PATH_TO_FPING,fping) 1521AC_PATH_PROG(PATH_TO_FPING,fping)
1522AC_PATH_PROG(PATH_TO_FPING6,fping6)
1523 1522
1524AC_ARG_WITH(fping_command, 1523AC_ARG_WITH(fping_command,
1525 ACX_HELP_STRING([--with-fping-command=PATH], 1524 ACX_HELP_STRING([--with-fping-command=PATH],
1526 [Path to fping command]), PATH_TO_FPING=$withval) 1525 [Path to fping command]), PATH_TO_FPING=$withval)
1527AC_ARG_WITH(fping6_command, 1526if test -n "$PATH_TO_FPING"; then
1528 ACX_HELP_STRING([--with-fping6-command=PATH],
1529 [Path to fping6 command]), PATH_TO_FPING6=$withval)
1530
1531if test -n "$PATH_TO_FPING"
1532then
1533 AC_DEFINE_UNQUOTED(PATH_TO_FPING,"$PATH_TO_FPING",[path to fping]) 1527 AC_DEFINE_UNQUOTED(PATH_TO_FPING,"$PATH_TO_FPING",[path to fping])
1534 EXTRAS="$EXTRAS check_fping\$(EXEEXT)" 1528 EXTRAS="$EXTRAS check_fping\$(EXEEXT)"
1535 if test x"$with_ipv6" != xno && test -n "$PATH_TO_FPING6"; then 1529
1536 AC_DEFINE_UNQUOTED(PATH_TO_FPING6,"$PATH_TO_FPING6",[path to fping6]) 1530 if test -z "$($PATH_TO_FPING --version)" ; then
1531 AC_MSG_NOTICE([failed to get version of fping])
1532 else
1533 FPING_MAJOR_VERSION="$($PATH_TO_FPING --version | sed 's/.*fping: Version //' | sed 's/\..*//')"
1534 FPING_MINOR_VERSION="$($PATH_TO_FPING --version | sed 's/.*fping: Version //' | sed 's/.*\.//')"
1535
1536 if test $FPING_MAJOR_VERSION -eq 5 ; then
1537 if test $FPING_MINOR_VERSION -ge 3 ; then
1538 AC_DEFINE(FPING_VERSION_5_3_OR_HIGHER, "true", [fping is of version 5.3 or higher])
1539 AC_MSG_NOTICE([fping is of version 5.3 or higher])
1540 AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher])
1541 AC_MSG_NOTICE([fping is of version 5.2 or higher])
1542 elif test $FPING_MINOR_VERSION -ge 2 ; then
1543 AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher])
1544 AC_MSG_NOTICE([fping is of version 5.2 or higher])
1545 else
1546 AC_MSG_NOTICE([fping is of a version lower then 5.2])
1547 fi
1548
1549 elif $FPING_MAJOR_VERSION > 5 ; then
1550 AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher])
1551 AC_MSG_NOTICE([fping is of version 5.2 or higher])
1552 AC_DEFINE(FPING_VERSION_5_3_OR_HIGHER, "true", [fping is of version 5.2 or higher])
1553 AC_MSG_NOTICE([fping is of version 5.3 or higher])
1554 fi
1555
1556 if test "`fping --version | sed 's/.*fping: Version //'`" = "5.2" ; then
1557 AC_DEFINE(FPING_VERSION, "5.2", [the version of fping available])
1558 AC_MSG_NOTICE([fping version: 5.2])
1559 elif test "`fping --version | sed 's/.*fping: Version //'`" = "5.3"; then
1560 AC_DEFINE(FPING_VERSION, "5.3", [the version of fping available])
1561 AC_MSG_NOTICE([fping version: 5.3])
1562 fi
1537 fi 1563 fi
1538else 1564else
1539 AC_MSG_WARN([Get fping from http://www.fping.com in order to make check_fping plugin]) 1565 AC_MSG_WARN([Get fping from http://www.fping.com in order to make check_fping plugin])
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e41201c4..a9f3ff40 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -7,10 +7,9 @@ noinst_LIBRARIES = libmonitoringplug.a
7AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ 7AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
8 -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins 8 -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
9 9
10libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c 10libmonitoringplug_a_SOURCES = utils_base.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c
11 11
12EXTRA_DIST = utils_base.h \ 12EXTRA_DIST = utils_base.h \
13 utils_disk.h \
14 utils_tcp.h \ 13 utils_tcp.h \
15 utils_cmd.h \ 14 utils_cmd.h \
16 parse_ini.h \ 15 parse_ini.h \
diff --git a/lib/output.c b/lib/output.c
index 61fbf832..b398c2ad 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -13,9 +13,11 @@
13 13
14// == Global variables 14// == Global variables
15static mp_output_format output_format = MP_FORMAT_DEFAULT; 15static mp_output_format output_format = MP_FORMAT_DEFAULT;
16static mp_output_detail_level level_of_detail = MP_DETAIL_ALL;
16 17
17// == Prototypes == 18// == Prototypes ==
18static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation); 19static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
20 unsigned int indentation);
19static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck); 21static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
20 22
21// == Implementation == 23// == Implementation ==
@@ -57,7 +59,9 @@ static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
57 * It sets useful defaults 59 * It sets useful defaults
58 */ 60 */
59mp_check mp_check_init(void) { 61mp_check mp_check_init(void) {
60 mp_check check = {0}; 62 mp_check check = {
63 .evaluation_function = &mp_eval_check_default,
64 };
61 return check; 65 return check;
62} 66}
63 67
@@ -120,7 +124,8 @@ void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata
120 */ 124 */
121int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) { 125int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
122 if (subcheck.output == NULL) { 126 if (subcheck.output == NULL) {
123 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "Sub check output is NULL"); 127 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__,
128 "Sub check output is NULL");
124 } 129 }
125 130
126 mp_subcheck_list *tmp = NULL; 131 mp_subcheck_list *tmp = NULL;
@@ -193,16 +198,33 @@ char *get_subcheck_summary(mp_check check) {
193 return result; 198 return result;
194} 199}
195 200
201mp_state_enum mp_compute_subcheck_state(const mp_subcheck subcheck) {
202 if (subcheck.evaluation_function == NULL) {
203 return mp_eval_subcheck_default(subcheck);
204 }
205 return subcheck.evaluation_function(subcheck);
206}
207
196/* 208/*
197 * Generate the result state of a mp_subcheck object based on it's own state and it's subchecks states 209 * Generate the result state of a mp_subcheck object based on its own state and its subchecks
210 * states
198 */ 211 */
199mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) { 212mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck) {
200 if (check.state_set_explicitly) { 213 if (subcheck.evaluation_function != NULL) {
201 return check.state; 214 return subcheck.evaluation_function(subcheck);
202 } 215 }
203 216
204 mp_subcheck_list *scl = check.subchecks; 217 if (subcheck.state_set_explicitly) {
205 mp_state_enum result = check.default_state; 218 return subcheck.state;
219 }
220
221 mp_subcheck_list *scl = subcheck.subchecks;
222
223 if (scl == NULL) {
224 return subcheck.default_state;
225 }
226
227 mp_state_enum result = STATE_OK;
206 228
207 while (scl != NULL) { 229 while (scl != NULL) {
208 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck)); 230 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
@@ -212,10 +234,18 @@ mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) {
212 return result; 234 return result;
213} 235}
214 236
237mp_state_enum mp_compute_check_state(const mp_check check) {
238 // just a safety check
239 if (check.evaluation_function == NULL) {
240 return mp_eval_check_default(check);
241 }
242 return check.evaluation_function(check);
243}
244
215/* 245/*
216 * Generate the result state of a mp_check object based on it's own state and it's subchecks states 246 * Generate the result state of a mp_check object based on it's own state and it's subchecks states
217 */ 247 */
218mp_state_enum mp_compute_check_state(const mp_check check) { 248mp_state_enum mp_eval_check_default(const mp_check check) {
219 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here 249 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
220 250
221 mp_subcheck_list *scl = check.subchecks; 251 mp_subcheck_list *scl = check.subchecks;
@@ -247,7 +277,11 @@ char *mp_fmt_output(mp_check check) {
247 mp_subcheck_list *subchecks = check.subchecks; 277 mp_subcheck_list *subchecks = check.subchecks;
248 278
249 while (subchecks != NULL) { 279 while (subchecks != NULL) {
250 asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); 280 if (level_of_detail == MP_DETAIL_ALL ||
281 mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) {
282 asprintf(&result, "%s\n%s", result,
283 fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1));
284 }
251 subchecks = subchecks->next; 285 subchecks = subchecks->next;
252 } 286 }
253 287
@@ -258,7 +292,8 @@ char *mp_fmt_output(mp_check check) {
258 if (pd_string == NULL) { 292 if (pd_string == NULL) {
259 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); 293 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
260 } else { 294 } else {
261 asprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck)); 295 asprintf(&pd_string, "%s %s", pd_string,
296 fmt_subcheck_perfdata(subchecks->subcheck));
262 } 297 }
263 298
264 subchecks = subchecks->next; 299 subchecks = subchecks->next;
@@ -327,19 +362,21 @@ static char *generate_indentation_string(unsigned int indentation) {
327/* 362/*
328 * Helper function to generate the output string of mp_subcheck 363 * Helper function to generate the output string of mp_subcheck
329 */ 364 */
330static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation) { 365static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
366 unsigned int indentation) {
331 char *result = NULL; 367 char *result = NULL;
332 mp_subcheck_list *subchecks = NULL; 368 mp_subcheck_list *subchecks = NULL;
333 369
334 switch (output_format) { 370 switch (output_format) {
335 case MP_FORMAT_MULTI_LINE: 371 case MP_FORMAT_MULTI_LINE:
336 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), 372 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation),
337 check.output); 373 state_text(mp_compute_subcheck_state(check)), check.output);
338 374
339 subchecks = check.subchecks; 375 subchecks = check.subchecks;
340 376
341 while (subchecks != NULL) { 377 while (subchecks != NULL) {
342 asprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); 378 asprintf(&result, "%s\n%s", result,
379 fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
343 subchecks = subchecks->next; 380 subchecks = subchecks->next;
344 } 381 }
345 return result; 382 return result;
@@ -539,3 +576,27 @@ parsed_output_format mp_parse_output_format(char *format_string) {
539void mp_set_format(mp_output_format format) { output_format = format; } 576void mp_set_format(mp_output_format format) { output_format = format; }
540 577
541mp_output_format mp_get_format(void) { return output_format; } 578mp_output_format mp_get_format(void) { return output_format; }
579
580void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; }
581
582mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; }
583
584mp_state_enum mp_eval_ok(mp_check overall) {
585 (void)overall;
586 return STATE_OK;
587}
588
589mp_state_enum mp_eval_warning(mp_check overall) {
590 (void)overall;
591 return STATE_WARNING;
592}
593
594mp_state_enum mp_eval_critical(mp_check overall) {
595 (void)overall;
596 return STATE_CRITICAL;
597}
598
599mp_state_enum mp_eval_unknown(mp_check overall) {
600 (void)overall;
601 return STATE_UNKNOWN;
602}
diff --git a/lib/output.h b/lib/output.h
index 2bdfa074..c63c8e3f 100644
--- a/lib/output.h
+++ b/lib/output.h
@@ -7,15 +7,21 @@
7/* 7/*
8 * A partial check result 8 * A partial check result
9 */ 9 */
10typedef struct { 10typedef struct mp_subcheck mp_subcheck;
11struct mp_subcheck {
11 mp_state_enum state; // OK, Warning, Critical ... set explicitly 12 mp_state_enum state; // OK, Warning, Critical ... set explicitly
12 mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly 13 mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly
13 bool state_set_explicitly; // was the state set explicitly (or should it be derived from subchecks) 14 bool state_set_explicitly; // was the state set explicitly (or should it be derived from
15 // subchecks)
14 16
15 char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP connection to..") 17 char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP
16 pd_list *perfdata; // Performance data for this check 18 // connection to..")
19 pd_list *perfdata; // Performance data for this check
17 struct subcheck_list *subchecks; // subchecks deeper in the hierarchy 20 struct subcheck_list *subchecks; // subchecks deeper in the hierarchy
18} mp_subcheck; 21
22 // the evaluation_functions computes the state of subcheck
23 mp_state_enum (*evaluation_function)(mp_subcheck);
24};
19 25
20/* 26/*
21 * A list of subchecks, used in subchecks and the main check 27 * A list of subchecks, used in subchecks and the main check
@@ -38,8 +44,18 @@ typedef enum output_format {
38/* 44/*
39 * Format related functions 45 * Format related functions
40 */ 46 */
41 void mp_set_format(mp_output_format format); 47void mp_set_format(mp_output_format format);
42 mp_output_format mp_get_format(void); 48mp_output_format mp_get_format(void);
49
50// Output detail level
51
52typedef enum output_detail_level {
53 MP_DETAIL_ALL,
54 MP_DETAIL_NON_OK_ONLY,
55} mp_output_detail_level;
56
57void mp_set_level_of_detail(mp_output_detail_level level);
58mp_output_detail_level mp_get_level_of_detail(void);
43 59
44/* 60/*
45 * The main state object of a plugin. Exists only ONCE per plugin. 61 * The main state object of a plugin. Exists only ONCE per plugin.
@@ -47,10 +63,14 @@ typedef enum output_format {
47 * The final result is always derived from the children and the "worst" state 63 * The final result is always derived from the children and the "worst" state
48 * in the first layer of subchecks 64 * in the first layer of subchecks
49 */ 65 */
50typedef struct { 66typedef struct mp_check mp_check;
51 char *summary; // Overall summary, if not set a summary will be automatically generated 67struct mp_check {
68 char *summary; // Overall summary, if not set a summary will be automatically generated
52 mp_subcheck_list *subchecks; 69 mp_subcheck_list *subchecks;
53} mp_check; 70
71 // the evaluation_functions computes the state of check
72 mp_state_enum (*evaluation_function)(mp_check);
73};
54 74
55mp_check mp_check_init(void); 75mp_check mp_check_init(void);
56mp_subcheck mp_subcheck_init(void); 76mp_subcheck mp_subcheck_init(void);
@@ -68,6 +88,13 @@ void mp_add_summary(mp_check check[static 1], char *summary);
68mp_state_enum mp_compute_check_state(mp_check); 88mp_state_enum mp_compute_check_state(mp_check);
69mp_state_enum mp_compute_subcheck_state(mp_subcheck); 89mp_state_enum mp_compute_subcheck_state(mp_subcheck);
70 90
91mp_state_enum mp_eval_ok(mp_check overall);
92mp_state_enum mp_eval_warning(mp_check overall);
93mp_state_enum mp_eval_critical(mp_check overall);
94mp_state_enum mp_eval_unknown(mp_check overall);
95mp_state_enum mp_eval_check_default(mp_check check);
96mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck);
97
71typedef struct { 98typedef struct {
72 bool parsing_success; 99 bool parsing_success;
73 mp_output_format output_format; 100 mp_output_format output_format;
diff --git a/lib/perfdata.c b/lib/perfdata.c
index 661756c5..b87de7e0 100644
--- a/lib/perfdata.c
+++ b/lib/perfdata.c
@@ -33,7 +33,7 @@ char *pd_value_to_string(const mp_perfdata_value pd) {
33char *pd_to_string(mp_perfdata pd) { 33char *pd_to_string(mp_perfdata pd) {
34 assert(pd.label != NULL); 34 assert(pd.label != NULL);
35 char *result = NULL; 35 char *result = NULL;
36 asprintf(&result, "%s=", pd.label); 36 asprintf(&result, "'%s'=", pd.label);
37 37
38 asprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); 38 asprintf(&result, "%s%s", result, pd_value_to_string(pd.value));
39 39
@@ -257,6 +257,10 @@ mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) {
257 return pd; 257 return pd;
258} 258}
259 259
260mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) { return mp_set_pd_value_long_long(pd, (long long)value); }
261
262mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); }
263
260mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); } 264mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); }
261 265
262mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } 266mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); }
@@ -288,6 +292,10 @@ mp_perfdata_value mp_create_pd_value_double(double value) {
288 292
289mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); } 293mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); }
290 294
295mp_perfdata_value mp_create_pd_value_char(char value) { return mp_create_pd_value_long_long((long long)value); }
296
297mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
298
291mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); } 299mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); }
292 300
293mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } 301mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
@@ -514,3 +522,84 @@ perfdata_value_parser_wrapper parse_pd_value(const char *input) {
514 } 522 }
515 return result; 523 return result;
516} 524}
525
526mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value) {
527 perfdata.max = value;
528 perfdata.max_present = true;
529 return perfdata;
530}
531
532mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value) {
533 perfdata.min = value;
534 perfdata.min_present = true;
535 return perfdata;
536}
537
538double mp_get_pd_value(mp_perfdata_value value) {
539 assert(value.type != PD_TYPE_NONE);
540 switch (value.type) {
541 case PD_TYPE_DOUBLE:
542 return value.pd_double;
543 case PD_TYPE_INT:
544 return (double)value.pd_int;
545 case PD_TYPE_UINT:
546 return (double)value.pd_uint;
547 default:
548 return 0; // just to make the compiler happy
549 }
550}
551
552mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right) {
553 if (left.type == right.type) {
554 switch (left.type) {
555 case PD_TYPE_DOUBLE:
556 left.pd_double *= right.pd_double;
557 return left;
558 case PD_TYPE_INT:
559 left.pd_int *= right.pd_int;
560 return left;
561 case PD_TYPE_UINT:
562 left.pd_uint *= right.pd_uint;
563 return left;
564 default:
565 // what to here?
566 return left;
567 }
568 }
569
570 // Different types, oh boy, just do the lazy thing for now and switch to double
571 switch (left.type) {
572 case PD_TYPE_INT:
573 left.pd_double = (double)left.pd_int;
574 left.type = PD_TYPE_DOUBLE;
575 break;
576 case PD_TYPE_UINT:
577 left.pd_double = (double)left.pd_uint;
578 left.type = PD_TYPE_DOUBLE;
579 break;
580 }
581
582 switch (right.type) {
583 case PD_TYPE_INT:
584 right.pd_double = (double)right.pd_int;
585 right.type = PD_TYPE_DOUBLE;
586 break;
587 case PD_TYPE_UINT:
588 right.pd_double = (double)right.pd_uint;
589 right.type = PD_TYPE_DOUBLE;
590 break;
591 }
592
593 left.pd_double *= right.pd_double;
594 return left;
595}
596
597mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor) {
598 if (!range.end_infinity) {
599 range.end = mp_pd_value_multiply(range.end, factor);
600 }
601 if (!range.start_infinity) {
602 range.start = mp_pd_value_multiply(range.start, factor);
603 }
604 return range;
605}
diff --git a/lib/perfdata.h b/lib/perfdata.h
index 74583ee5..7fd908a9 100644
--- a/lib/perfdata.h
+++ b/lib/perfdata.h
@@ -155,6 +155,8 @@ mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
155 _Generic((V), \ 155 _Generic((V), \
156 float: mp_create_pd_value_float, \ 156 float: mp_create_pd_value_float, \
157 double: mp_create_pd_value_double, \ 157 double: mp_create_pd_value_double, \
158 char: mp_create_pd_value_char, \
159 unsigned char: mp_create_pd_value_u_char, \
158 int: mp_create_pd_value_int, \ 160 int: mp_create_pd_value_int, \
159 unsigned int: mp_create_pd_value_u_int, \ 161 unsigned int: mp_create_pd_value_u_int, \
160 long: mp_create_pd_value_long, \ 162 long: mp_create_pd_value_long, \
@@ -164,6 +166,8 @@ mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
164 166
165mp_perfdata_value mp_create_pd_value_float(float); 167mp_perfdata_value mp_create_pd_value_float(float);
166mp_perfdata_value mp_create_pd_value_double(double); 168mp_perfdata_value mp_create_pd_value_double(double);
169mp_perfdata_value mp_create_pd_value_char(char);
170mp_perfdata_value mp_create_pd_value_u_char(unsigned char);
167mp_perfdata_value mp_create_pd_value_int(int); 171mp_perfdata_value mp_create_pd_value_int(int);
168mp_perfdata_value mp_create_pd_value_u_int(unsigned int); 172mp_perfdata_value mp_create_pd_value_u_int(unsigned int);
169mp_perfdata_value mp_create_pd_value_long(long); 173mp_perfdata_value mp_create_pd_value_long(long);
@@ -171,6 +175,11 @@ mp_perfdata_value mp_create_pd_value_u_long(unsigned long);
171mp_perfdata_value mp_create_pd_value_long_long(long long); 175mp_perfdata_value mp_create_pd_value_long_long(long long);
172mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long); 176mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long);
173 177
178mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value);
179mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value);
180
181double mp_get_pd_value(mp_perfdata_value value);
182
174/* 183/*
175 * Free the memory used by a pd_list 184 * Free the memory used by a pd_list
176 */ 185 */
@@ -178,6 +187,13 @@ void pd_list_free(pd_list[1]);
178 187
179int cmp_perfdata_value(mp_perfdata_value, mp_perfdata_value); 188int cmp_perfdata_value(mp_perfdata_value, mp_perfdata_value);
180 189
190// ================
191// Helper functions
192// ================
193
194mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right);
195mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor);
196
181// ================= 197// =================
182// String formatters 198// String formatters
183// ================= 199// =================
diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am
index 9be94f6d..7798a72e 100644
--- a/lib/tests/Makefile.am
+++ b/lib/tests/Makefile.am
@@ -8,9 +8,9 @@ check_PROGRAMS = @EXTRA_TEST@
8AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ 8AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
9 -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins 9 -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
10 10
11EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output 11EXTRA_PROGRAMS = test_utils test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output
12 12
13np_test_scripts = test_base64.t test_cmd.t test_disk.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t test_generic_output.t 13np_test_scripts = test_base64.t test_cmd.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t test_generic_output.t
14np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini 14np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini
15EXTRA_DIST = $(np_test_scripts) $(np_test_files) var 15EXTRA_DIST = $(np_test_scripts) $(np_test_files) var
16 16
@@ -29,7 +29,7 @@ AM_CFLAGS = -g -I$(top_srcdir)/lib -I$(top_srcdir)/gl $(tap_cflags)
29AM_LDFLAGS = $(tap_ldflags) -ltap 29AM_LDFLAGS = $(tap_ldflags) -ltap
30LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO) 30LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO)
31 31
32SOURCES = test_utils.c test_disk.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c test_generic_output.c 32SOURCES = test_utils.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c test_generic_output.c
33 33
34test: ${noinst_PROGRAMS} 34test: ${noinst_PROGRAMS}
35 perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS) 35 perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS)
diff --git a/lib/tests/test_disk.t b/lib/tests/test_disk.t
deleted file mode 100755
index da84dfdf..00000000
--- a/lib/tests/test_disk.t
+++ /dev/null
@@ -1,6 +0,0 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_disk") {
4 plan skip_all => "./test_disk not compiled - please enable libtap library to test";
5}
6exec "./test_disk";
diff --git a/lib/thresholds.c b/lib/thresholds.c
index ddefae37..de2b9315 100644
--- a/lib/thresholds.c
+++ b/lib/thresholds.c
@@ -51,9 +51,21 @@ mp_state_enum mp_get_pd_status(mp_perfdata perfdata) {
51 } 51 }
52 if (perfdata.warn_present) { 52 if (perfdata.warn_present) {
53 if (mp_check_range(perfdata.value, perfdata.warn)) { 53 if (mp_check_range(perfdata.value, perfdata.warn)) {
54 return STATE_CRITICAL; 54 return STATE_WARNING;
55 } 55 }
56 } 56 }
57 57
58 return STATE_OK; 58 return STATE_OK;
59} 59}
60
61mp_thresholds mp_thresholds_set_warn(mp_thresholds thlds, mp_range warn) {
62 thlds.warning = warn;
63 thlds.warning_is_set = true;
64 return thlds;
65}
66
67mp_thresholds mp_thresholds_set_crit(mp_thresholds thlds, mp_range crit) {
68 thlds.critical = crit;
69 thlds.critical_is_set = true;
70 return thlds;
71}
diff --git a/lib/thresholds.h b/lib/thresholds.h
index 4e7defee..5f9f9247 100644
--- a/lib/thresholds.h
+++ b/lib/thresholds.h
@@ -24,5 +24,8 @@ mp_perfdata mp_pd_set_thresholds(mp_perfdata /* pd */, mp_thresholds /* th */);
24 24
25mp_state_enum mp_get_pd_status(mp_perfdata /* pd */); 25mp_state_enum mp_get_pd_status(mp_perfdata /* pd */);
26 26
27mp_thresholds mp_thresholds_set_warn(mp_thresholds thlds, mp_range warn);
28mp_thresholds mp_thresholds_set_crit(mp_thresholds thlds, mp_range crit);
29
27char *fmt_threshold_warning(thresholds th); 30char *fmt_threshold_warning(thresholds th);
28char *fmt_threshold_critical(thresholds th); 31char *fmt_threshold_critical(thresholds th);
diff --git a/lib/utils_base.c b/lib/utils_base.c
index ff9540c7..c49a473f 100644
--- a/lib/utils_base.c
+++ b/lib/utils_base.c
@@ -225,27 +225,15 @@ bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
225 if (my_range.end_infinity == false && my_range.start_infinity == false) { 225 if (my_range.end_infinity == false && my_range.start_infinity == false) {
226 // range: .........|---inside---|........... 226 // range: .........|---inside---|...........
227 // value 227 // value
228 if ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)) { 228 is_inside = ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0));
229 is_inside = true;
230 } else {
231 is_inside = false;
232 }
233 } else if (my_range.start_infinity == false && my_range.end_infinity == true) { 229 } else if (my_range.start_infinity == false && my_range.end_infinity == true) {
234 // range: .........|---inside--------- 230 // range: .........|---inside---------
235 // value 231 // value
236 if (cmp_perfdata_value(my_range.start, value) < 0) { 232 is_inside = (cmp_perfdata_value(my_range.start, value) < 0);
237 is_inside = true;
238 } else {
239 is_inside = false;
240 }
241 } else if (my_range.start_infinity == true && my_range.end_infinity == false) { 233 } else if (my_range.start_infinity == true && my_range.end_infinity == false) {
242 // range: -inside--------|.................... 234 // range: -inside--------|....................
243 // value 235 // value
244 if (cmp_perfdata_value(value, my_range.end) == -1) { 236 is_inside = (cmp_perfdata_value(value, my_range.end) == -1);
245 is_inside = true;
246 } else {
247 is_inside = false;
248 }
249 } else { 237 } else {
250 // range from -inf to inf, so always inside 238 // range from -inf to inf, so always inside
251 is_inside = true; 239 is_inside = true;
diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c
index 18350ac0..9b222409 100644
--- a/lib/utils_cmd.c
+++ b/lib/utils_cmd.c
@@ -346,7 +346,7 @@ int cmd_run_array(char *const *argv, output *out, output *err, int flags) {
346 return _cmd_close(fd); 346 return _cmd_close(fd);
347} 347}
348 348
349int cmd_file_read(char *filename, output *out, int flags) { 349int cmd_file_read(const char *filename, output *out, int flags) {
350 int fd; 350 int fd;
351 if (out) 351 if (out)
352 memset(out, 0, sizeof(output)); 352 memset(out, 0, sizeof(output));
diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h
index d00069c9..728ece23 100644
--- a/lib/utils_cmd.h
+++ b/lib/utils_cmd.h
@@ -20,7 +20,7 @@ typedef struct output output;
20/** prototypes **/ 20/** prototypes **/
21int cmd_run(const char *, output *, output *, int); 21int cmd_run(const char *, output *, output *, int);
22int cmd_run_array(char *const *, output *, output *, int); 22int cmd_run_array(char *const *, output *, output *, int);
23int cmd_file_read(char *, output *, int); 23int cmd_file_read(const char *, output *, int);
24 24
25/* only multi-threaded plugins need to bother with this */ 25/* only multi-threaded plugins need to bother with this */
26void cmd_init(void); 26void cmd_init(void);
diff --git a/lib/utils_disk.c b/lib/utils_disk.c
deleted file mode 100644
index 2b761f5e..00000000
--- a/lib/utils_disk.c
+++ /dev/null
@@ -1,255 +0,0 @@
1/*****************************************************************************
2 *
3 * Library for check_disk
4 *
5 * License: GPL
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains utilities for check_disk. These are tested by libtap
11 *
12 *
13 * This program is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 *
27 *****************************************************************************/
28
29#include "common.h"
30#include "utils_disk.h"
31#include "gl/fsusage.h"
32#include <string.h>
33
34void np_add_name(struct name_list **list, const char *name) {
35 struct name_list *new_entry;
36 new_entry = (struct name_list *)malloc(sizeof *new_entry);
37 new_entry->name = (char *)name;
38 new_entry->next = *list;
39 *list = new_entry;
40}
41
42/* @brief Initialises a new regex at the begin of list via regcomp(3)
43 *
44 * @details if the regex fails to compile the error code of regcomp(3) is returned
45 * and list is not modified, otherwise list is modified to point to the new
46 * element
47 * @param list Pointer to a linked list of regex_list elements
48 * @param regex the string containing the regex which should be inserted into the list
49 * @param clags the cflags parameter for regcomp(3)
50 */
51int np_add_regex(struct regex_list **list, const char *regex, int cflags) {
52 struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry);
53
54 if (new_entry == NULL) {
55 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
56 }
57
58 int regcomp_result = regcomp(&new_entry->regex, regex, cflags);
59
60 if (!regcomp_result) {
61 // regcomp succeeded
62 new_entry->next = *list;
63 *list = new_entry;
64
65 return 0;
66 } else {
67 // regcomp failed
68 free(new_entry);
69
70 return regcomp_result;
71 }
72}
73
74/* Initialises a new parameter at the end of list */
75struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name) {
76 struct parameter_list *current = *list;
77 struct parameter_list *new_path;
78 new_path = (struct parameter_list *)malloc(sizeof *new_path);
79 new_path->name = (char *)malloc(strlen(name) + 1);
80 new_path->best_match = NULL;
81 new_path->name_next = NULL;
82 new_path->name_prev = NULL;
83 new_path->freespace_bytes = NULL;
84 new_path->freespace_units = NULL;
85 new_path->freespace_percent = NULL;
86 new_path->usedspace_bytes = NULL;
87 new_path->usedspace_units = NULL;
88 new_path->usedspace_percent = NULL;
89 new_path->usedinodes_percent = NULL;
90 new_path->freeinodes_percent = NULL;
91 new_path->group = NULL;
92 new_path->dfree_pct = -1;
93 new_path->dused_pct = -1;
94 new_path->total = 0;
95 new_path->available = 0;
96 new_path->available_to_root = 0;
97 new_path->used = 0;
98 new_path->dused_units = 0;
99 new_path->dfree_units = 0;
100 new_path->dtotal_units = 0;
101 new_path->inodes_total = 0;
102 new_path->inodes_free = 0;
103 new_path->inodes_free_to_root = 0;
104 new_path->inodes_used = 0;
105 new_path->dused_inodes_percent = 0;
106 new_path->dfree_inodes_percent = 0;
107
108 strcpy(new_path->name, name);
109
110 if (current == NULL) {
111 *list = new_path;
112 new_path->name_prev = NULL;
113 } else {
114 while (current->name_next) {
115 current = current->name_next;
116 }
117 current->name_next = new_path;
118 new_path->name_prev = current;
119 }
120 return new_path;
121}
122
123/* Delete a given parameter from list and return pointer to next element*/
124struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev) {
125 if (item == NULL) {
126 return NULL;
127 }
128 struct parameter_list *next;
129
130 if (item->name_next)
131 next = item->name_next;
132 else
133 next = NULL;
134
135 if (next)
136 next->name_prev = prev;
137
138 if (prev)
139 prev->name_next = next;
140
141 if (item->name) {
142 free(item->name);
143 }
144 free(item);
145
146 return next;
147}
148
149/* returns a pointer to the struct found in the list */
150struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name) {
151 struct parameter_list *temp_list;
152 for (temp_list = list; temp_list; temp_list = temp_list->name_next) {
153 if (!strcmp(temp_list->name, name))
154 return temp_list;
155 }
156
157 return NULL;
158}
159
160void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact) {
161 struct parameter_list *d;
162 for (d = desired; d; d = d->name_next) {
163 if (!d->best_match) {
164 struct mount_entry *me;
165 size_t name_len = strlen(d->name);
166 size_t best_match_len = 0;
167 struct mount_entry *best_match = NULL;
168 struct fs_usage fsp;
169
170 /* set best match if path name exactly matches a mounted device name */
171 for (me = mount_list; me; me = me->me_next) {
172 if (strcmp(me->me_devname, d->name) == 0) {
173 if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) {
174 best_match = me;
175 }
176 }
177 }
178
179 /* set best match by directory name if no match was found by devname */
180 if (!best_match) {
181 for (me = mount_list; me; me = me->me_next) {
182 size_t len = strlen(me->me_mountdir);
183 if ((!exact &&
184 (best_match_len <= len && len <= name_len && (len == 1 || strncmp(me->me_mountdir, d->name, len) == 0))) ||
185 (exact && strcmp(me->me_mountdir, d->name) == 0)) {
186 if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) {
187 best_match = me;
188 best_match_len = len;
189 }
190 }
191 }
192 }
193
194 if (best_match) {
195 d->best_match = best_match;
196 } else {
197 d->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */
198 }
199 }
200 }
201}
202
203/* Returns true if name is in list */
204bool np_find_name(struct name_list *list, const char *name) {
205 const struct name_list *n;
206
207 if (list == NULL || name == NULL) {
208 return false;
209 }
210 for (n = list; n; n = n->next) {
211 if (!strcmp(name, n->name)) {
212 return true;
213 }
214 }
215 return false;
216}
217
218/* Returns true if name is in list */
219bool np_find_regmatch(struct regex_list *list, const char *name) {
220 int len;
221 regmatch_t m;
222
223 if (name == NULL) {
224 return false;
225 }
226
227 len = strlen(name);
228
229 for (; list; list = list->next) {
230 /* Emulate a full match as if surrounded with ^( )$
231 by checking whether the match spans the whole name */
232 if (!regexec(&list->regex, name, 1, &m, 0) && m.rm_so == 0 && m.rm_eo == len) {
233 return true;
234 }
235 }
236
237 return false;
238}
239
240bool np_seen_name(struct name_list *list, const char *name) {
241 const struct name_list *s;
242 for (s = list; s; s = s->next) {
243 if (!strcmp(s->name, name)) {
244 return true;
245 }
246 }
247 return false;
248}
249
250bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) {
251 if (regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0 || regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0) {
252 return true;
253 }
254 return false;
255}
diff --git a/lib/utils_disk.h b/lib/utils_disk.h
deleted file mode 100644
index c5e81dc1..00000000
--- a/lib/utils_disk.h
+++ /dev/null
@@ -1,48 +0,0 @@
1/* Header file for utils_disk */
2
3#include "mountlist.h"
4#include "utils_base.h"
5#include "regex.h"
6
7struct name_list {
8 char *name;
9 struct name_list *next;
10};
11
12struct regex_list {
13 regex_t regex;
14 struct regex_list *next;
15};
16
17struct parameter_list {
18 char *name;
19 thresholds *freespace_bytes;
20 thresholds *freespace_units;
21 thresholds *freespace_percent;
22 thresholds *usedspace_bytes;
23 thresholds *usedspace_units;
24 thresholds *usedspace_percent;
25 thresholds *usedinodes_percent;
26 thresholds *freeinodes_percent;
27 char *group;
28 struct mount_entry *best_match;
29 struct parameter_list *name_next;
30 struct parameter_list *name_prev;
31 uintmax_t total, available, available_to_root, used, inodes_free, inodes_free_to_root, inodes_used, inodes_total;
32 double dfree_pct, dused_pct;
33 uint64_t dused_units, dfree_units, dtotal_units;
34 double dused_inodes_percent, dfree_inodes_percent;
35};
36
37void np_add_name(struct name_list **list, const char *name);
38bool np_find_name(struct name_list *list, const char *name);
39bool np_seen_name(struct name_list *list, const char *name);
40int np_add_regex(struct regex_list **list, const char *regex, int cflags);
41bool np_find_regmatch(struct regex_list *list, const char *name);
42struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name);
43struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name);
44struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev);
45
46int search_parameter_list(struct parameter_list *list, const char *name);
47void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact);
48bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re);
diff --git a/lib/utils_tcp.h b/lib/utils_tcp.h
index d5999e9b..a7d83c59 100644
--- a/lib/utils_tcp.h
+++ b/lib/utils_tcp.h
@@ -11,6 +11,7 @@
11 * server. 11 * server.
12 */ 12 */
13enum np_match_result { 13enum np_match_result {
14 NP_MATCH_NONE,
14 NP_MATCH_FAILURE, 15 NP_MATCH_FAILURE,
15 NP_MATCH_SUCCESS, 16 NP_MATCH_SUCCESS,
16 NP_MATCH_RETRY 17 NP_MATCH_RETRY
diff --git a/plugins-root/Makefile.am b/plugins-root/Makefile.am
index a80229e2..b6342909 100644
--- a/plugins-root/Makefile.am
+++ b/plugins-root/Makefile.am
@@ -24,7 +24,9 @@ noinst_PROGRAMS = check_dhcp check_icmp @EXTRAS_ROOT@
24 24
25EXTRA_PROGRAMS = pst3 25EXTRA_PROGRAMS = pst3
26 26
27EXTRA_DIST = t pst3.c 27EXTRA_DIST = t pst3.c \
28 check_icmp.d \
29 check_dhcp.d
28 30
29BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a 31BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a
30NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) 32NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS)
@@ -82,6 +84,7 @@ install-exec-local: $(noinst_PROGRAMS)
82# the actual targets 84# the actual targets
83check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO) 85check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO)
84check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO) 86check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO)
87check_icmp_SOURCES = check_icmp.c check_icmp.d/check_icmp_helpers.c
85 88
86# -m64 needed at compiler and linker phase 89# -m64 needed at compiler and linker phase
87pst3_CFLAGS = @PST3CFLAGS@ 90pst3_CFLAGS = @PST3CFLAGS@
@@ -89,7 +92,7 @@ pst3_LDFLAGS = @PST3CFLAGS@
89# pst3 must not use monitoringplug/gnulib includes! 92# pst3 must not use monitoringplug/gnulib includes!
90pst3_CPPFLAGS = 93pst3_CPPFLAGS =
91 94
92check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) 95check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS)
93check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS) 96check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS)
94 97
95clean-local: 98clean-local:
diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c
index 6802232e..daed9cb0 100644
--- a/plugins-root/check_dhcp.c
+++ b/plugins-root/check_dhcp.c
@@ -4,7 +4,7 @@
4 * 4 *
5 * License: GPL 5 * License: GPL
6 * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)
7 * Copyright (c) 2001-2023 Monitoring Plugins Development Team 7 * Copyright (c) 2001-2025 Monitoring Plugins Development Team
8 * 8 *
9 * Description: 9 * Description:
10 * 10 *
@@ -34,13 +34,17 @@
34 *****************************************************************************/ 34 *****************************************************************************/
35 35
36const char *progname = "check_dhcp"; 36const char *progname = "check_dhcp";
37const char *copyright = "2001-2024"; 37const char *copyright = "2001-2025";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "common.h" 40#include "../plugins/common.h"
41#include "netutils.h" 41#include "../plugins/utils.h"
42#include "utils.h" 42#include "./check_dhcp.d/config.h"
43#include "../lib/output.h"
44#include "../lib/utils_base.h"
43 45
46#include "states.h"
47#include <stdint.h>
44#include <ctype.h> 48#include <ctype.h>
45#include <stdio.h> 49#include <stdio.h>
46#include <stdlib.h> 50#include <stdlib.h>
@@ -111,8 +115,9 @@ static long mac_addr_dlpi(const char *, int, u_char *);
111 115
112/**** Common definitions ****/ 116/**** Common definitions ****/
113 117
114#define OK 0 118#define OK 0
115#define ERROR -1 119#define ERROR -1
120#define MAC_ADDR_LEN 6
116 121
117/**** DHCP definitions ****/ 122/**** DHCP definitions ****/
118 123
@@ -149,12 +154,6 @@ typedef struct dhcp_offer_struct {
149 struct dhcp_offer_struct *next; 154 struct dhcp_offer_struct *next;
150} dhcp_offer; 155} dhcp_offer;
151 156
152typedef struct requested_server_struct {
153 struct in_addr server_address;
154 bool answered;
155 struct requested_server_struct *next;
156} requested_server;
157
158#define BOOTREQUEST 1 157#define BOOTREQUEST 1
159#define BOOTREPLY 2 158#define BOOTREPLY 2
160 159
@@ -186,65 +185,60 @@ typedef struct requested_server_struct {
186#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ 185#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */
187#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ 186#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */
188 187
189static bool unicast = false; /* unicast mode: mimic a DHCP relay */
190static bool exclusive = false; /* exclusive mode aka "rogue DHCP server detection" */
191static struct in_addr my_ip; /* our address (required for relay) */
192static struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
193static unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
194static unsigned char *user_specified_mac = NULL;
195
196static char network_interface_name[IFNAMSIZ] = "eth0";
197
198static uint32_t packet_xid = 0;
199
200static uint32_t dhcp_lease_time = 0;
201static uint32_t dhcp_renewal_time = 0;
202static uint32_t dhcp_rebinding_time = 0;
203
204static int dhcpoffer_timeout = 2;
205
206static dhcp_offer *dhcp_offer_list = NULL;
207static requested_server *requested_server_list = NULL;
208
209static int valid_responses = 0; /* number of valid DHCPOFFERs we received */
210static int requested_servers = 0;
211static int requested_responses = 0;
212
213static bool request_specific_address = false;
214static bool received_requested_address = false;
215static int verbose = 0; 188static int verbose = 0;
216static struct in_addr requested_address;
217 189
218static int process_arguments(int, char **); 190typedef struct process_arguments_wrapper {
219static int call_getopt(int, char **); 191 int error;
220static int validate_arguments(int); 192 check_dhcp_config config;
193} process_arguments_wrapper;
194
195static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
221void print_usage(void); 196void print_usage(void);
222static void print_help(void); 197static void print_help(void);
223 198
224static void resolve_host(const char *in, struct in_addr *out); 199static void resolve_host(const char * /*in*/, struct in_addr * /*out*/);
225static unsigned char *mac_aton(const char *); 200static unsigned char *mac_aton(const char * /*string*/);
226static void print_hardware_address(const unsigned char *); 201static void print_hardware_address(const unsigned char * /*address*/);
227static int get_hardware_address(int, char *); 202static int get_hardware_address(int /*sock*/, char * /*interface_name*/, unsigned char *client_hardware_address);
228static int get_ip_address(int, char *); 203
229 204typedef struct get_ip_address_wrapper {
230static int send_dhcp_discover(int); 205 int error;
231static int get_dhcp_offer(int); 206 struct in_addr my_ip;
232 207} get_ip_address_wrapper;
233static int get_results(void); 208static get_ip_address_wrapper get_ip_address(int /*sock*/, char * /*interface_name*/);
234 209
235static int add_dhcp_offer(struct in_addr, dhcp_packet *); 210typedef struct send_dhcp_discover_wrapper {
236static int free_dhcp_offer_list(void); 211 int error;
237static int free_requested_server_list(void); 212 uint32_t packet_xid;
238 213} send_dhcp_discover_wrapper;
239static int create_dhcp_socket(void); 214static send_dhcp_discover_wrapper send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address,
240static int close_dhcp_socket(int); 215 bool request_specific_address, struct in_addr my_ip,
241static int send_dhcp_packet(void *, int, int, struct sockaddr_in *); 216 unsigned char *client_hardware_address);
242static int receive_dhcp_packet(void *, int, int, int, struct sockaddr_in *); 217typedef struct get_dhcp_offer_wrapper {
218 int error;
219 int valid_responses;
220 dhcp_offer *dhcp_offer_list;
221} get_dhcp_offer_wrapper;
222static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list,
223 const unsigned char *client_hardware_address);
224
225static mp_subcheck get_results(bool exclusive, int requested_servers, struct in_addr requested_address, bool request_specific_address,
226 requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list);
227
228typedef struct add_dhcp_offer_wrapper {
229 int error;
230 dhcp_offer *dhcp_offer_list;
231} add_dhcp_offer_wrapper;
232static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/, dhcp_packet * /*offer_packet*/, dhcp_offer *dhcp_offer_list);
233static int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list);
234static int free_requested_server_list(requested_server *requested_server_list);
235
236static int create_dhcp_socket(bool /*unicast*/, char *network_interface_name);
237static int close_dhcp_socket(int /*sock*/);
238static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, struct sockaddr_in * /*dest*/);
239static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, int /*timeout*/, struct sockaddr_in * /*address*/);
243 240
244int main(int argc, char **argv) { 241int main(int argc, char **argv) {
245 int dhcp_socket;
246 int result = STATE_UNKNOWN;
247
248 setlocale(LC_ALL, ""); 242 setlocale(LC_ALL, "");
249 bindtextdomain(PACKAGE, LOCALEDIR); 243 bindtextdomain(PACKAGE, LOCALEDIR);
250 textdomain(PACKAGE); 244 textdomain(PACKAGE);
@@ -252,43 +246,80 @@ int main(int argc, char **argv) {
252 /* Parse extra opts if any */ 246 /* Parse extra opts if any */
253 argv = np_extra_opts(&argc, argv, progname); 247 argv = np_extra_opts(&argc, argv, progname);
254 248
255 if (process_arguments(argc, argv) != OK) { 249 process_arguments_wrapper tmp = process_arguments(argc, argv);
250
251 if (tmp.error != OK) {
256 usage4(_("Could not parse arguments")); 252 usage4(_("Could not parse arguments"));
257 } 253 }
258 254
255 check_dhcp_config config = tmp.config;
256 if (config.output_format_is_set) {
257 mp_set_format(config.output_format);
258 }
259
259 /* create socket for DHCP communications */ 260 /* create socket for DHCP communications */
260 dhcp_socket = create_dhcp_socket(); 261 int dhcp_socket = create_dhcp_socket(config.unicast_mode, config.network_interface_name);
261 262
262 /* get hardware address of client machine */ 263 /* get hardware address of client machine */
263 if (user_specified_mac != NULL) 264 unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
264 memcpy(client_hardware_address, user_specified_mac, 6); 265 if (config.user_specified_mac != NULL) {
265 else 266 memcpy(client_hardware_address, config.user_specified_mac, MAC_ADDR_LEN);
266 get_hardware_address(dhcp_socket, network_interface_name); 267 } else {
268 get_hardware_address(dhcp_socket, config.network_interface_name, client_hardware_address);
269 }
267 270
268 if (unicast) /* get IP address of client machine */ 271 struct in_addr my_ip = {0};
269 get_ip_address(dhcp_socket, network_interface_name); 272
273 if (config.unicast_mode) { /* get IP address of client machine */
274 get_ip_address_wrapper tmp_get_ip = get_ip_address(dhcp_socket, config.network_interface_name);
275 if (tmp_get_ip.error == OK) {
276 my_ip = tmp_get_ip.my_ip;
277 } else {
278 // TODO failed to get own IP
279 die(STATE_UNKNOWN, "Failed to retrieve my own IP address in unicast mode");
280 }
281 }
270 282
271 /* send DHCPDISCOVER packet */ 283 /* send DHCPDISCOVER packet */
272 send_dhcp_discover(dhcp_socket); 284 send_dhcp_discover_wrapper disco_res = send_dhcp_discover(dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address,
285 config.request_specific_address, my_ip, client_hardware_address);
286
287 if (disco_res.error != OK) {
288 // DO something?
289 die(STATE_UNKNOWN, "Failed to send DHCP discover");
290 }
273 291
274 /* wait for a DHCPOFFER packet */ 292 /* wait for a DHCPOFFER packet */
275 get_dhcp_offer(dhcp_socket); 293 get_dhcp_offer_wrapper offer_res =
294 get_dhcp_offer(dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address);
295
296 int valid_responses = 0;
297 dhcp_offer *dhcp_offer_list = NULL;
298 if (offer_res.error == OK) {
299 valid_responses = offer_res.valid_responses;
300 dhcp_offer_list = offer_res.dhcp_offer_list;
301 } else {
302 die(STATE_UNKNOWN, "Failed to get DHCP offers");
303 }
276 304
277 /* close socket we created */ 305 /* close socket we created */
278 close_dhcp_socket(dhcp_socket); 306 close_dhcp_socket(dhcp_socket);
279 307
280 /* determine state/plugin output to return */ 308 mp_check overall = mp_check_init();
281 result = get_results();
282 309
310 /* determine state/plugin output to return */
311 mp_subcheck sc_res = get_results(config.exclusive_mode, config.num_of_requested_servers, config.requested_address,
312 config.request_specific_address, config.requested_server_list, valid_responses, dhcp_offer_list);
313 mp_add_subcheck_to_check(&overall, sc_res);
283 /* free allocated memory */ 314 /* free allocated memory */
284 free_dhcp_offer_list(); 315 free_dhcp_offer_list(dhcp_offer_list);
285 free_requested_server_list(); 316 free_requested_server_list(config.requested_server_list);
286 317
287 return result; 318 mp_exit(overall);
288} 319}
289 320
290/* determines hardware address on client machine */ 321/* determines hardware address on client machine */
291static int get_hardware_address(int sock, char *interface_name) { 322int get_hardware_address(int sock, char *interface_name, unsigned char *client_hardware_address) {
292 323
293#if defined(__linux__) 324#if defined(__linux__)
294 struct ifreq ifr; 325 struct ifreq ifr;
@@ -302,7 +333,7 @@ static int get_hardware_address(int sock, char *interface_name) {
302 exit(STATE_UNKNOWN); 333 exit(STATE_UNKNOWN);
303 } 334 }
304 335
305 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, 6); 336 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, MAC_ADDR_LEN);
306 337
307#elif defined(__bsd__) 338#elif defined(__bsd__)
308 /* King 2004 see ACKNOWLEDGEMENTS */ 339 /* King 2004 see ACKNOWLEDGEMENTS */
@@ -358,8 +389,9 @@ static int get_hardware_address(int sock, char *interface_name) {
358 int i; 389 int i;
359 p = interface_name + strlen(interface_name) - 1; 390 p = interface_name + strlen(interface_name) - 1;
360 for (i = strlen(interface_name) - 1; i > 0; p--) { 391 for (i = strlen(interface_name) - 1; i > 0; p--) {
361 if (isalpha(*p)) 392 if (isalpha(*p)) {
362 break; 393 break;
394 }
363 } 395 }
364 p++; 396 p++;
365 if (p != interface_name) { 397 if (p != interface_name) {
@@ -393,14 +425,15 @@ static int get_hardware_address(int sock, char *interface_name) {
393 exit(STATE_UNKNOWN); 425 exit(STATE_UNKNOWN);
394#endif 426#endif
395 427
396 if (verbose) 428 if (verbose) {
397 print_hardware_address(client_hardware_address); 429 print_hardware_address(client_hardware_address);
430 }
398 431
399 return OK; 432 return OK;
400} 433}
401 434
402/* determines IP address of the client interface */ 435/* determines IP address of the client interface */
403static int get_ip_address(int sock, char *interface_name) { 436get_ip_address_wrapper get_ip_address(int sock, char *interface_name) {
404#if defined(SIOCGIFADDR) 437#if defined(SIOCGIFADDR)
405 struct ifreq ifr; 438 struct ifreq ifr;
406 439
@@ -412,28 +445,28 @@ static int get_ip_address(int sock, char *interface_name) {
412 exit(STATE_UNKNOWN); 445 exit(STATE_UNKNOWN);
413 } 446 }
414 447
415 my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
416
417#else 448#else
418 printf(_("Error: Cannot get interface IP address on this platform.\n")); 449 printf(_("Error: Cannot get interface IP address on this platform.\n"));
419 exit(STATE_UNKNOWN); 450 exit(STATE_UNKNOWN);
420#endif 451#endif
421 452
422 if (verbose) 453 get_ip_address_wrapper result = {
423 printf(_("Pretending to be relay client %s\n"), inet_ntoa(my_ip)); 454 .error = OK,
455 .my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr,
456 };
424 457
425 return OK; 458 if (verbose) {
459 printf(_("Pretending to be relay client %s\n"), inet_ntoa(result.my_ip));
460 }
461
462 return result;
426} 463}
427 464
428/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ 465/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
429static int send_dhcp_discover(int sock) { 466static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address,
430 dhcp_packet discover_packet; 467 bool request_specific_address, struct in_addr my_ip,
431 struct sockaddr_in sockaddr_broadcast; 468 unsigned char *client_hardware_address) {
432 unsigned short opts; 469 dhcp_packet discover_packet = {0};
433
434 /* clear the packet data structure */
435 bzero(&discover_packet, sizeof(discover_packet));
436
437 /* boot request flag (backward compatible with BOOTP servers) */ 470 /* boot request flag (backward compatible with BOOTP servers) */
438 discover_packet.op = BOOTREQUEST; 471 discover_packet.op = BOOTREQUEST;
439 472
@@ -443,12 +476,15 @@ static int send_dhcp_discover(int sock) {
443 /* length of our hardware address */ 476 /* length of our hardware address */
444 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; 477 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH;
445 478
479 send_dhcp_discover_wrapper result = {
480 .error = OK,
481 };
446 /* 482 /*
447 * transaction ID is supposed to be random. 483 * transaction ID is supposed to be random.
448 */ 484 */
449 srand(time(NULL) ^ getpid()); 485 srand(time(NULL) ^ getpid());
450 packet_xid = random(); 486 result.packet_xid = random();
451 discover_packet.xid = htonl(packet_xid); 487 discover_packet.xid = htonl(result.packet_xid);
452 488
453 /*discover_packet.secs=htons(65535);*/ 489 /*discover_packet.secs=htons(65535);*/
454 discover_packet.secs = 0xFF; 490 discover_packet.secs = 0xFF;
@@ -468,7 +504,7 @@ static int send_dhcp_discover(int sock) {
468 discover_packet.options[2] = '\x53'; 504 discover_packet.options[2] = '\x53';
469 discover_packet.options[3] = '\x63'; 505 discover_packet.options[3] = '\x63';
470 506
471 opts = 4; 507 unsigned short opts = 4;
472 /* DHCP message type is embedded in options field */ 508 /* DHCP message type is embedded in options field */
473 discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ 509 discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
474 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ 510 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */
@@ -484,17 +520,19 @@ static int send_dhcp_discover(int sock) {
484 discover_packet.options[opts++] = (char)DHCP_OPTION_END; 520 discover_packet.options[opts++] = (char)DHCP_OPTION_END;
485 521
486 /* unicast fields */ 522 /* unicast fields */
487 if (unicast) 523 if (unicast) {
488 discover_packet.giaddr.s_addr = my_ip.s_addr; 524 discover_packet.giaddr.s_addr = my_ip.s_addr;
525 }
489 526
490 /* see RFC 1542, 4.1.1 */ 527 /* see RFC 1542, 4.1.1 */
491 discover_packet.hops = unicast ? 1 : 0; 528 discover_packet.hops = unicast ? 1 : 0;
492 529
493 /* send the DHCPDISCOVER packet to broadcast address */ 530 /* send the DHCPDISCOVER packet to broadcast address */
494 sockaddr_broadcast.sin_family = AF_INET; 531 struct sockaddr_in sockaddr_broadcast = {
495 sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT); 532 .sin_family = AF_INET,
496 sockaddr_broadcast.sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST; 533 .sin_port = htons(DHCP_SERVER_PORT),
497 bzero(&sockaddr_broadcast.sin_zero, sizeof(sockaddr_broadcast.sin_zero)); 534 .sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST,
535 };
498 536
499 if (verbose) { 537 if (verbose) {
500 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); 538 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port));
@@ -508,56 +546,56 @@ static int send_dhcp_discover(int sock) {
508 /* send the DHCPDISCOVER packet out */ 546 /* send the DHCPDISCOVER packet out */
509 send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast); 547 send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast);
510 548
511 if (verbose) 549 if (verbose) {
512 printf("\n\n"); 550 printf("\n\n");
551 }
513 552
514 return OK; 553 return result;
515} 554}
516 555
517/* waits for a DHCPOFFER message from one or more DHCP servers */ 556/* waits for a DHCPOFFER message from one or more DHCP servers */
518static int get_dhcp_offer(int sock) { 557get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list,
519 dhcp_packet offer_packet; 558 const unsigned char *client_hardware_address) {
520 struct sockaddr_in source;
521 struct sockaddr_in via;
522 int result = OK;
523 int responses = 0;
524 int x;
525 time_t start_time; 559 time_t start_time;
526 time_t current_time;
527
528 time(&start_time); 560 time(&start_time);
529 561
562 int result = OK;
563 int responses = 0;
564 int valid_responses = 0;
530 /* receive as many responses as we can */ 565 /* receive as many responses as we can */
531 for (responses = 0, valid_responses = 0;;) { 566 for (;;) {
532 567 time_t current_time;
533 time(&current_time); 568 time(&current_time);
534 if ((current_time - start_time) >= dhcpoffer_timeout) 569 if ((current_time - start_time) >= dhcpoffer_timeout) {
535 break; 570 break;
571 }
536 572
537 if (verbose) 573 if (verbose) {
538 printf("\n\n"); 574 printf("\n\n");
575 }
539 576
540 bzero(&source, sizeof(source)); 577 struct sockaddr_in source = {0};
541 bzero(&via, sizeof(via)); 578 dhcp_packet offer_packet = {0};
542 bzero(&offer_packet, sizeof(offer_packet));
543 579
544 result = OK; 580 result = OK;
545 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); 581 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source);
546 582
547 if (result != OK) { 583 if (result != OK) {
548 if (verbose) 584 if (verbose) {
549 printf(_("Result=ERROR\n")); 585 printf(_("Result=ERROR\n"));
586 }
550 587
551 continue; 588 continue;
552 } else { 589 }
553 if (verbose) 590 if (verbose) {
554 printf(_("Result=OK\n")); 591 printf(_("Result=OK\n"));
555
556 responses++;
557 } 592 }
558 593
594 responses++;
595
559 /* The "source" is either a server or a relay. */ 596 /* The "source" is either a server or a relay. */
560 /* Save a copy of "source" into "via" even if it's via itself */ 597 /* Save a copy of "source" into "via" even if it's via itself */
598 struct sockaddr_in via = {0};
561 memcpy(&via, &source, sizeof(source)); 599 memcpy(&via, &source, sizeof(source));
562 600
563 if (verbose) { 601 if (verbose) {
@@ -568,30 +606,37 @@ static int get_dhcp_offer(int sock) {
568 606
569 /* check packet xid to see if its the same as the one we used in the discover packet */ 607 /* check packet xid to see if its the same as the one we used in the discover packet */
570 if (ntohl(offer_packet.xid) != packet_xid) { 608 if (ntohl(offer_packet.xid) != packet_xid) {
571 if (verbose) 609 if (verbose) {
572 printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid), packet_xid); 610 printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid),
611 packet_xid);
612 }
573 613
574 continue; 614 continue;
575 } 615 }
576 616
577 /* check hardware address */ 617 /* check hardware address */
578 result = OK; 618 result = OK;
579 if (verbose) 619 if (verbose) {
580 printf("DHCPOFFER chaddr: "); 620 printf("DHCPOFFER chaddr: ");
621 }
581 622
582 for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { 623 for (int i = 0; i < ETHERNET_HARDWARE_ADDRESS_LENGTH; i++) {
583 if (verbose) 624 if (verbose) {
584 printf("%02X", (unsigned char)offer_packet.chaddr[x]); 625 printf("%02X", offer_packet.chaddr[i]);
626 }
585 627
586 if (offer_packet.chaddr[x] != client_hardware_address[x]) 628 if (offer_packet.chaddr[i] != client_hardware_address[i]) {
587 result = ERROR; 629 result = ERROR;
630 }
588 } 631 }
589 if (verbose) 632 if (verbose) {
590 printf("\n"); 633 printf("\n");
634 }
591 635
592 if (result == ERROR) { 636 if (result == ERROR) {
593 if (verbose) 637 if (verbose) {
594 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n")); 638 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n"));
639 }
595 640
596 continue; 641 continue;
597 } 642 }
@@ -603,7 +648,12 @@ static int get_dhcp_offer(int sock) {
603 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); 648 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr));
604 } 649 }
605 650
606 add_dhcp_offer(source.sin_addr, &offer_packet); 651 add_dhcp_offer_wrapper add_res = add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list);
652 if (add_res.error != OK) {
653 // TODO
654 } else {
655 dhcp_offer_list = add_res.dhcp_offer_list;
656 }
607 657
608 valid_responses++; 658 valid_responses++;
609 } 659 }
@@ -613,104 +663,101 @@ static int get_dhcp_offer(int sock) {
613 printf(_("Valid responses for this machine: %d\n"), valid_responses); 663 printf(_("Valid responses for this machine: %d\n"), valid_responses);
614 } 664 }
615 665
616 return OK; 666 get_dhcp_offer_wrapper ret_val = {
667 .error = OK,
668 .valid_responses = valid_responses,
669 .dhcp_offer_list = dhcp_offer_list,
670 };
671 return ret_val;
617} 672}
618 673
619/* sends a DHCP packet */ 674/* sends a DHCP packet */
620static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { 675int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) {
621 int result; 676 int result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
622
623 result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
624 677
625 if (verbose) 678 if (verbose) {
626 printf(_("send_dhcp_packet result: %d\n"), result); 679 printf(_("send_dhcp_packet result: %d\n"), result);
680 }
627 681
628 if (result < 0) 682 if (result < 0) {
629 return ERROR; 683 return ERROR;
684 }
630 685
631 return OK; 686 return OK;
632} 687}
633 688
634/* receives a DHCP packet */ 689/* receives a DHCP packet */
635static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { 690int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) {
636 struct timeval tv;
637 fd_set readfds;
638 fd_set oobfds;
639 int recv_result;
640 socklen_t address_size;
641 struct sockaddr_in source_address;
642 int nfound;
643
644 /* wait for data to arrive (up time timeout) */ 691 /* wait for data to arrive (up time timeout) */
645 tv.tv_sec = timeout; 692 struct timeval timeout_val = {
646 tv.tv_usec = 0; 693 .tv_sec = timeout,
694 .tv_usec = 0,
695 };
696 fd_set readfds;
647 FD_ZERO(&readfds); 697 FD_ZERO(&readfds);
698 fd_set oobfds;
648 FD_ZERO(&oobfds); 699 FD_ZERO(&oobfds);
649 FD_SET(sock, &readfds); 700 FD_SET(sock, &readfds);
650 FD_SET(sock, &oobfds); 701 FD_SET(sock, &oobfds);
651 nfound = select(sock + 1, &readfds, NULL, &oobfds, &tv); 702 int nfound = select(sock + 1, &readfds, NULL, &oobfds, &timeout_val);
652 703
653 /* make sure some data has arrived */ 704 /* make sure some data has arrived */
654 if (!FD_ISSET(sock, &readfds)) { 705 if (!FD_ISSET(sock, &readfds)) {
655 if (verbose) 706 if (verbose) {
656 printf(_("No (more) data received (nfound: %d)\n"), nfound); 707 printf(_("No (more) data received (nfound: %d)\n"), nfound);
708 }
657 return ERROR; 709 return ERROR;
658 } 710 }
659 711
660 else { 712 struct sockaddr_in source_address = {0};
661 bzero(&source_address, sizeof(source_address)); 713 socklen_t address_size = sizeof(source_address);
662 address_size = sizeof(source_address); 714 int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size);
663 recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); 715 if (verbose) {
664 if (verbose) 716 printf("recv_result: %d\n", recv_result);
665 printf("recv_result: %d\n", recv_result); 717 }
666
667 if (recv_result == -1) {
668 if (verbose) {
669 printf(_("recvfrom() failed, "));
670 printf("errno: (%d) -> %s\n", errno, strerror(errno));
671 }
672 return ERROR;
673 } else {
674 if (verbose) {
675 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
676 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
677 }
678 718
679 memcpy(address, &source_address, sizeof(source_address)); 719 if (recv_result == -1) {
680 return OK; 720 if (verbose) {
721 printf(_("recvfrom() failed, "));
722 printf("errno: (%d) -> %s\n", errno, strerror(errno));
681 } 723 }
724 return ERROR;
725 }
726 if (verbose) {
727 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
728 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
682 } 729 }
683 730
731 memcpy(address, &source_address, sizeof(source_address));
684 return OK; 732 return OK;
685} 733}
686 734
687/* creates a socket for DHCP communication */ 735/* creates a socket for DHCP communication */
688static int create_dhcp_socket(void) { 736int create_dhcp_socket(bool unicast, char *network_interface_name) {
689 struct sockaddr_in myname;
690 struct ifreq interface;
691 int sock;
692 int flag = 1;
693
694 /* Set up the address we're going to bind to. */ 737 /* Set up the address we're going to bind to. */
695 bzero(&myname, sizeof(myname));
696 myname.sin_family = AF_INET;
697 /* listen to DHCP server port if we're in unicast mode */ 738 /* listen to DHCP server port if we're in unicast mode */
698 myname.sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT); 739 struct sockaddr_in myname = {
699 myname.sin_addr.s_addr = unicast ? my_ip.s_addr : INADDR_ANY; 740 .sin_family = AF_INET,
700 bzero(&myname.sin_zero, sizeof(myname.sin_zero)); 741 .sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT),
742 // TODO previously the next line was trying to use our own IP, we was not set
743 // until some point later, so it was removed. Recheck whether it is actually
744 // necessary/useful
745 .sin_addr.s_addr = INADDR_ANY,
746 };
701 747
702 /* create a socket for DHCP communications */ 748 /* create a socket for DHCP communications */
703 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 749 int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
704 if (sock < 0) { 750 if (sock < 0) {
705 printf(_("Error: Could not create socket!\n")); 751 printf(_("Error: Could not create socket!\n"));
706 exit(STATE_UNKNOWN); 752 exit(STATE_UNKNOWN);
707 } 753 }
708 754
709 if (verbose) 755 if (verbose) {
710 printf("DHCP socket: %d\n", sock); 756 printf("DHCP socket: %d\n", sock);
757 }
711 758
712 /* set the reuse address flag so we don't get errors when restarting */ 759 /* set the reuse address flag so we don't get errors when restarting */
713 flag = 1; 760 int flag = 1;
714 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { 761 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) {
715 printf(_("Error: Could not set reuse address option on DHCP socket!\n")); 762 printf(_("Error: Could not set reuse address option on DHCP socket!\n"));
716 exit(STATE_UNKNOWN); 763 exit(STATE_UNKNOWN);
@@ -722,6 +769,7 @@ static int create_dhcp_socket(void) {
722 exit(STATE_UNKNOWN); 769 exit(STATE_UNKNOWN);
723 } 770 }
724 771
772 struct ifreq interface;
725 /* bind socket to interface */ 773 /* bind socket to interface */
726#if defined(__linux__) 774#if defined(__linux__)
727 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); 775 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1);
@@ -746,105 +794,116 @@ static int create_dhcp_socket(void) {
746} 794}
747 795
748/* closes DHCP socket */ 796/* closes DHCP socket */
749static int close_dhcp_socket(int sock) { 797int close_dhcp_socket(int sock) {
750
751 close(sock); 798 close(sock);
752
753 return OK; 799 return OK;
754} 800}
755 801
756/* adds a requested server address to list in memory */ 802/* adds a requested server address to list in memory */
757static int add_requested_server(struct in_addr server_address) { 803int add_requested_server(struct in_addr server_address, int *requested_servers, requested_server **requested_server_list) {
758 requested_server *new_server; 804 requested_server *new_server = (requested_server *)malloc(sizeof(requested_server));
759 805 if (new_server == NULL) {
760 new_server = (requested_server *)malloc(sizeof(requested_server));
761 if (new_server == NULL)
762 return ERROR; 806 return ERROR;
807 }
763 808
764 new_server->server_address = server_address; 809 new_server->server_address = server_address;
765 new_server->answered = false; 810 new_server->answered = false;
766 811
767 new_server->next = requested_server_list; 812 new_server->next = *requested_server_list;
768 requested_server_list = new_server; 813 *requested_server_list = new_server;
769 814
770 requested_servers++; 815 *requested_servers += 1;
771 816
772 if (verbose) 817 if (verbose) {
773 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); 818 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address));
819 }
774 820
775 return OK; 821 return OK;
776} 822}
777 823
778/* adds a DHCP OFFER to list in memory */ 824/* adds a DHCP OFFER to list in memory */
779static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { 825add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet, dhcp_offer *dhcp_offer_list) {
826 if (offer_packet == NULL) {
827 add_dhcp_offer_wrapper tmp = {
828 .error = ERROR,
829 };
830 return tmp;
831 }
832
833 uint32_t dhcp_lease_time = 0;
834 uint32_t dhcp_renewal_time = 0;
835 uint32_t dhcp_rebinding_time = 0;
780 dhcp_offer *new_offer; 836 dhcp_offer *new_offer;
781 int x;
782 unsigned option_type;
783 unsigned option_length;
784 struct in_addr serv_ident = {0}; 837 struct in_addr serv_ident = {0};
785
786 if (offer_packet == NULL)
787 return ERROR;
788
789 /* process all DHCP options present in the packet */ 838 /* process all DHCP options present in the packet */
790 for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH - 1;) { 839 for (int dchp_opt_idx = 4; dchp_opt_idx < MAX_DHCP_OPTIONS_LENGTH - 1;) {
791 840
792 if ((int)offer_packet->options[x] == -1) 841 if ((int)offer_packet->options[dchp_opt_idx] == -1) {
793 break; 842 break;
843 }
794 844
795 /* get option type */ 845 /* get option type */
796 option_type = offer_packet->options[x++]; 846 unsigned option_type = offer_packet->options[dchp_opt_idx++];
797 847
798 /* get option length */ 848 /* get option length */
799 option_length = offer_packet->options[x++]; 849 unsigned option_length = offer_packet->options[dchp_opt_idx++];
800 850
801 if (verbose) 851 if (verbose) {
802 printf("Option: %d (0x%02X)\n", option_type, option_length); 852 printf("Option: %d (0x%02X)\n", option_type, option_length);
853 }
803 854
804 /* get option data */ 855 /* get option data */
805 switch (option_type) { 856 switch (option_type) {
806 case DHCP_OPTION_LEASE_TIME: 857 case DHCP_OPTION_LEASE_TIME:
807 memcpy(&dhcp_lease_time, &offer_packet->options[x], sizeof(dhcp_lease_time)); 858 memcpy(&dhcp_lease_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_lease_time));
808 dhcp_lease_time = ntohl(dhcp_lease_time); 859 dhcp_lease_time = ntohl(dhcp_lease_time);
809 break; 860 break;
810 case DHCP_OPTION_RENEWAL_TIME: 861 case DHCP_OPTION_RENEWAL_TIME:
811 memcpy(&dhcp_renewal_time, &offer_packet->options[x], sizeof(dhcp_renewal_time)); 862 memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_renewal_time));
812 dhcp_renewal_time = ntohl(dhcp_renewal_time); 863 dhcp_renewal_time = ntohl(dhcp_renewal_time);
813 break; 864 break;
814 case DHCP_OPTION_REBINDING_TIME: 865 case DHCP_OPTION_REBINDING_TIME:
815 memcpy(&dhcp_rebinding_time, &offer_packet->options[x], sizeof(dhcp_rebinding_time)); 866 memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_rebinding_time));
816 dhcp_rebinding_time = ntohl(dhcp_rebinding_time); 867 dhcp_rebinding_time = ntohl(dhcp_rebinding_time);
817 break; 868 break;
818 case DHCP_OPTION_SERVER_IDENTIFIER: 869 case DHCP_OPTION_SERVER_IDENTIFIER:
819 memcpy(&serv_ident.s_addr, &offer_packet->options[x], sizeof(serv_ident.s_addr)); 870 memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx], sizeof(serv_ident.s_addr));
820 break; 871 break;
821 } 872 }
822 873
823 /* skip option data we're ignoring */ 874 /* skip option data we're ignoring */
824 if (option_type == 0) /* "pad" option, see RFC 2132 (3.1) */ 875 if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */
825 x += 1; 876 dchp_opt_idx += 1;
826 else 877 } else {
827 x += option_length; 878 dchp_opt_idx += option_length;
879 }
828 } 880 }
829 881
830 if (verbose) { 882 if (verbose) {
831 if (dhcp_lease_time == DHCP_INFINITE_TIME) 883 if (dhcp_lease_time == DHCP_INFINITE_TIME) {
832 printf(_("Lease Time: Infinite\n")); 884 printf(_("Lease Time: Infinite\n"));
833 else 885 } else {
834 printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time); 886 printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time);
835 if (dhcp_renewal_time == DHCP_INFINITE_TIME) 887 }
888 if (dhcp_renewal_time == DHCP_INFINITE_TIME) {
836 printf(_("Renewal Time: Infinite\n")); 889 printf(_("Renewal Time: Infinite\n"));
837 else 890 } else {
838 printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time); 891 printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time);
839 if (dhcp_rebinding_time == DHCP_INFINITE_TIME) 892 }
893 if (dhcp_rebinding_time == DHCP_INFINITE_TIME) {
840 printf(_("Rebinding Time: Infinite\n")); 894 printf(_("Rebinding Time: Infinite\n"));
895 }
841 printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time); 896 printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time);
842 } 897 }
843 898
844 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); 899 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer));
845 900
846 if (new_offer == NULL) 901 if (new_offer == NULL) {
847 return ERROR; 902 add_dhcp_offer_wrapper tmp = {
903 .error = ERROR,
904 };
905 return tmp;
906 }
848 907
849 /* 908 /*
850 * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the 909 * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the
@@ -874,15 +933,18 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) {
874 new_offer->next = dhcp_offer_list; 933 new_offer->next = dhcp_offer_list;
875 dhcp_offer_list = new_offer; 934 dhcp_offer_list = new_offer;
876 935
877 return OK; 936 add_dhcp_offer_wrapper result = {
937 .error = OK,
938 .dhcp_offer_list = dhcp_offer_list,
939 };
940
941 return result;
878} 942}
879 943
880/* frees memory allocated to DHCP OFFER list */ 944/* frees memory allocated to DHCP OFFER list */
881static int free_dhcp_offer_list(void) { 945int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list) {
882 dhcp_offer *this_offer;
883 dhcp_offer *next_offer; 946 dhcp_offer *next_offer;
884 947 for (dhcp_offer *this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
885 for (this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
886 next_offer = this_offer->next; 948 next_offer = this_offer->next;
887 free(this_offer); 949 free(this_offer);
888 } 950 }
@@ -891,11 +953,9 @@ static int free_dhcp_offer_list(void) {
891} 953}
892 954
893/* frees memory allocated to requested server list */ 955/* frees memory allocated to requested server list */
894static int free_requested_server_list(void) { 956int free_requested_server_list(requested_server *requested_server_list) {
895 requested_server *this_server;
896 requested_server *next_server; 957 requested_server *next_server;
897 958 for (requested_server *this_server = requested_server_list; this_server != NULL; this_server = next_server) {
898 for (this_server = requested_server_list; this_server != NULL; this_server = next_server) {
899 next_server = this_server->next; 959 next_server = this_server->next;
900 free(this_server); 960 free(this_server);
901 } 961 }
@@ -904,39 +964,60 @@ static int free_requested_server_list(void) {
904} 964}
905 965
906/* gets state and plugin output to return */ 966/* gets state and plugin output to return */
907static int get_results(void) { 967mp_subcheck get_results(bool exclusive, const int requested_servers, const struct in_addr requested_address, bool request_specific_address,
908 dhcp_offer *temp_offer, *undesired_offer = NULL; 968 requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list) {
909 requested_server *temp_server; 969 mp_subcheck sc_dhcp_results = mp_subcheck_init();
910 int result; 970 sc_dhcp_results = mp_set_subcheck_default_state(sc_dhcp_results, STATE_OK);
911 uint32_t max_lease_time = 0;
912 971
913 received_requested_address = false; 972 /* we didn't receive any DHCPOFFERs */
914 973 if (dhcp_offer_list == NULL) {
915 /* checks responses from requested servers */ 974 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
916 requested_responses = 0; 975 xasprintf(&sc_dhcp_results.output, "%s", "No DHCPOFFERs were received");
917 if (requested_servers > 0) { 976 return sc_dhcp_results;
977 }
918 978
919 for (temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { 979 if (valid_responses == 0) {
980 // No valid responses at all, so early exit here
981 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
982 xasprintf(&sc_dhcp_results.output, "No valid responses received");
983 return sc_dhcp_results;
984 }
920 985
921 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 986 if (valid_responses == 1) {
987 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFER", valid_responses);
988 } else {
989 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFERs", valid_responses);
990 }
922 991
992 bool received_requested_address = false;
993 dhcp_offer *undesired_offer = NULL;
994 uint32_t max_lease_time = 0;
995 /* checks responses from requested servers */
996 int requested_responses = 0;
997 if (requested_servers > 0) {
998 for (requested_server *temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) {
999 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
923 /* get max lease time we were offered */ 1000 /* get max lease time we were offered */
924 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) 1001 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) {
925 max_lease_time = temp_offer->lease_time; 1002 max_lease_time = temp_offer->lease_time;
1003 }
926 1004
927 /* see if we got the address we requested */ 1005 /* see if we got the address we requested */
928 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) 1006 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) {
929 received_requested_address = true; 1007 received_requested_address = true;
1008 }
930 1009
931 /* see if the servers we wanted a response from talked to us or not */ 1010 /* see if the servers we wanted a response from, talked to us or not */
932 if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { 1011 if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) {
933 if (verbose) { 1012 if (verbose) {
934 printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); 1013 printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address));
935 printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address)); 1014 printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address));
936 if (temp_server->answered) 1015 if (temp_server->answered) {
937 printf(_(" (duplicate)")); 1016 printf(_(" (duplicate)"));
1017 }
938 printf(_("\n")); 1018 printf(_("\n"));
939 } 1019 }
1020
940 if (!temp_server->answered) { 1021 if (!temp_server->answered) {
941 requested_responses++; 1022 requested_responses++;
942 temp_server->answered = true; 1023 temp_server->answered = true;
@@ -947,94 +1028,115 @@ static int get_results(void) {
947 } 1028 }
948 1029
949 /* exclusive mode: check for undesired offers */ 1030 /* exclusive mode: check for undesired offers */
950 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1031 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
951 if (!temp_offer->desired) { 1032 if (!temp_offer->desired) {
952 undesired_offer = temp_offer; /* Checks only for the first undesired offer */ 1033 undesired_offer = temp_offer; /* Checks only for the first undesired offer */
953 break; /* no further checks needed */ 1034 break; /* no further checks needed */
954 } 1035 }
955 } 1036 }
956 }
957 1037
958 /* else check and see if we got our requested address from any server */ 1038 mp_subcheck sc_rqust_srvs = mp_subcheck_init();
959 else { 1039 xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded", requested_responses, requested_servers);
960 1040
961 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1041 if (requested_responses == requested_servers) {
1042 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_OK);
1043 } else if (requested_responses == 0) {
1044 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_CRITICAL);
1045 } else if (requested_responses < requested_servers) {
1046 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1047 } else {
1048 // We received more(!) responses than we asked for?
1049 // This case shouldn't happen, but is here for completion
1050 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1051 }
1052 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqust_srvs);
962 1053
1054 } else {
1055 /* else check and see if we got our requested address from any server */
1056 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
963 /* get max lease time we were offered */ 1057 /* get max lease time we were offered */
964 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) 1058 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) {
965 max_lease_time = temp_offer->lease_time; 1059 max_lease_time = temp_offer->lease_time;
1060 }
966 1061
967 /* see if we got the address we requested */ 1062 /* see if we got the address we requested */
968 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) 1063 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) {
969 received_requested_address = true; 1064 received_requested_address = true;
1065 }
970 } 1066 }
971 } 1067 }
972 1068
973 result = STATE_OK; 1069 if (max_lease_time == DHCP_INFINITE_TIME) {
974 if (valid_responses == 0) 1070 xasprintf(&sc_dhcp_results.output, "%s, max lease time = Infinity", sc_dhcp_results.output);
975 result = STATE_CRITICAL; 1071 } else {
976 else if (requested_servers > 0 && requested_responses == 0) 1072 xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds", sc_dhcp_results.output, max_lease_time);
977 result = STATE_CRITICAL;
978 else if (requested_responses < requested_servers)
979 result = STATE_WARNING;
980 else if (request_specific_address && !received_requested_address)
981 result = STATE_WARNING;
982
983 if (exclusive && undesired_offer)
984 result = STATE_CRITICAL;
985
986 if (result == 0) /* garrett honeycutt 2005 */
987 printf("OK: ");
988 else if (result == 1)
989 printf("WARNING: ");
990 else if (result == 2)
991 printf("CRITICAL: ");
992 else if (result == 3)
993 printf("UNKNOWN: ");
994
995 /* we didn't receive any DHCPOFFERs */
996 if (dhcp_offer_list == NULL) {
997 printf(_("No DHCPOFFERs were received.\n"));
998 return result;
999 } 1073 }
1000 1074
1001 printf(_("Received %d DHCPOFFER(s)"), valid_responses); 1075 if (exclusive) {
1076 mp_subcheck sc_rogue_server = mp_subcheck_init();
1002 1077
1003 if (exclusive && undesired_offer) { 1078 if (undesired_offer != NULL) {
1004 printf(_(", Rogue DHCP Server detected! Server %s"), inet_ntoa(undesired_offer->server_address)); 1079 // We wanted to get a DHCPOFFER exclusively from one machine, but another one
1005 printf(_(" offered %s \n"), inet_ntoa(undesired_offer->offered_address)); 1080 // sent one (too)
1006 return result; 1081 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_CRITICAL);
1007 }
1008 1082
1009 if (requested_servers > 0) 1083 // Get the addresses for printout
1010 printf(_(", %s%d of %d requested servers responded"), ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, 1084 // 1.address of the sending server
1011 requested_servers); 1085 char server_address[INET_ADDRSTRLEN];
1086 const char *server_address_transformed =
1087 inet_ntop(AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address));
1012 1088
1013 if (request_specific_address) 1089 if (server_address != server_address_transformed) {
1014 printf(_(", requested address (%s) was %soffered"), inet_ntoa(requested_address), (received_requested_address) ? "" : _("not ")); 1090 die(STATE_UNKNOWN, "inet_ntop failed");
1091 }
1015 1092
1016 printf(_(", max lease time = ")); 1093 // 2.address offered
1017 if (max_lease_time == DHCP_INFINITE_TIME) 1094 char offered_address[INET_ADDRSTRLEN];
1018 printf(_("Infinity")); 1095 const char *offered_address_transformed =
1019 else 1096 inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address, sizeof(offered_address));
1020 printf("%lu sec", (unsigned long)max_lease_time);
1021 1097
1022 printf(".\n"); 1098 if (offered_address != offered_address_transformed) {
1099 die(STATE_UNKNOWN, "inet_ntop failed");
1100 }
1023 1101
1024 return result; 1102 xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s", server_address, offered_address);
1103 } else {
1104 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_OK);
1105 xasprintf(&sc_rogue_server.output, "No Rogue DHCP Server detected");
1106 }
1107 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rogue_server);
1108 }
1109
1110 if (request_specific_address) {
1111 mp_subcheck sc_rqustd_addr = mp_subcheck_init();
1112
1113 if (received_requested_address) {
1114 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_OK);
1115 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered", inet_ntoa(requested_address));
1116 } else {
1117 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_WARNING);
1118 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered", inet_ntoa(requested_address));
1119 }
1120
1121 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqustd_addr);
1122 }
1123
1124 return sc_dhcp_results;
1025} 1125}
1026 1126
1027/* process command-line arguments */ 1127/* process command-line arguments */
1028static int process_arguments(int argc, char **argv) { 1128process_arguments_wrapper process_arguments(int argc, char **argv) {
1029 if (argc < 1) 1129 if (argc < 1) {
1030 return ERROR; 1130 process_arguments_wrapper tmp = {
1131 .error = ERROR,
1132 };
1133 return tmp;
1134 }
1031 1135
1032 call_getopt(argc, argv); 1136 enum {
1033 return validate_arguments(argc); 1137 output_format_index = CHAR_MAX + 1,
1034} 1138 };
1035 1139
1036static int call_getopt(int argc, char **argv) {
1037 extern int optind;
1038 int option_index = 0; 1140 int option_index = 0;
1039 static struct option long_options[] = {{"serverip", required_argument, 0, 's'}, 1141 static struct option long_options[] = {{"serverip", required_argument, 0, 's'},
1040 {"requestedip", required_argument, 0, 'r'}, 1142 {"requestedip", required_argument, 0, 'r'},
@@ -1046,61 +1148,55 @@ static int call_getopt(int argc, char **argv) {
1046 {"verbose", no_argument, 0, 'v'}, 1148 {"verbose", no_argument, 0, 'v'},
1047 {"version", no_argument, 0, 'V'}, 1149 {"version", no_argument, 0, 'V'},
1048 {"help", no_argument, 0, 'h'}, 1150 {"help", no_argument, 0, 'h'},
1151 {"output-format", required_argument, 0, output_format_index},
1049 {0, 0, 0, 0}}; 1152 {0, 0, 0, 0}};
1050 1153
1051 int c = 0; 1154 check_dhcp_config config = check_dhcp_config_init();
1155 int option_char = 0;
1052 while (true) { 1156 while (true) {
1053 c = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); 1157 option_char = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index);
1054 1158
1055 if (c == -1 || c == EOF || c == 1) 1159 if (option_char == -1 || option_char == EOF || option_char == 1) {
1056 break; 1160 break;
1161 }
1057 1162
1058 switch (c) { 1163 switch (option_char) {
1059
1060 case 's': /* DHCP server address */ 1164 case 's': /* DHCP server address */
1061 resolve_host(optarg, &dhcp_ip); 1165 resolve_host(optarg, &config.dhcp_ip);
1062 add_requested_server(dhcp_ip); 1166 add_requested_server(config.dhcp_ip, &config.num_of_requested_servers, &config.requested_server_list);
1063 break; 1167 break;
1064 1168
1065 case 'r': /* address we are requested from DHCP servers */ 1169 case 'r': /* address we are requested from DHCP servers */
1066 resolve_host(optarg, &requested_address); 1170 resolve_host(optarg, &config.requested_address);
1067 request_specific_address = true; 1171 config.request_specific_address = true;
1068 break; 1172 break;
1069 1173
1070 case 't': /* timeout */ 1174 case 't': /* timeout */
1071 1175 if (atoi(optarg) > 0) {
1072 /* 1176 config.dhcpoffer_timeout = atoi(optarg);
1073 if(is_intnonneg(optarg)) 1177 }
1074 */
1075 if (atoi(optarg) > 0)
1076 dhcpoffer_timeout = atoi(optarg);
1077 /*
1078 else
1079 usage("Time interval must be a nonnegative integer\n");
1080 */
1081 break; 1178 break;
1082 1179
1083 case 'm': /* MAC address */ 1180 case 'm': /* MAC address */
1084 1181 if ((config.user_specified_mac = mac_aton(optarg)) == NULL) {
1085 if ((user_specified_mac = mac_aton(optarg)) == NULL)
1086 usage("Cannot parse MAC address.\n"); 1182 usage("Cannot parse MAC address.\n");
1087 if (verbose) 1183 }
1088 print_hardware_address(user_specified_mac); 1184 if (verbose) {
1089 1185 print_hardware_address(config.user_specified_mac);
1186 }
1090 break; 1187 break;
1091 1188
1092 case 'i': /* interface name */ 1189 case 'i': /* interface name */
1093 1190 strncpy(config.network_interface_name, optarg, sizeof(config.network_interface_name) - 1);
1094 strncpy(network_interface_name, optarg, sizeof(network_interface_name) - 1); 1191 config.network_interface_name[sizeof(config.network_interface_name) - 1] = '\x0';
1095 network_interface_name[sizeof(network_interface_name) - 1] = '\x0';
1096
1097 break; 1192 break;
1098 1193
1099 case 'u': /* unicast testing */ 1194 case 'u': /* unicast testing */
1100 unicast = true; 1195 config.unicast_mode = true;
1101 break; 1196 break;
1197
1102 case 'x': /* exclusive testing aka "rogue DHCP server detection" */ 1198 case 'x': /* exclusive testing aka "rogue DHCP server detection" */
1103 exclusive = true; 1199 config.exclusive_mode = true;
1104 break; 1200 break;
1105 1201
1106 case 'V': /* version */ 1202 case 'V': /* version */
@@ -1114,6 +1210,18 @@ static int call_getopt(int argc, char **argv) {
1114 case 'v': /* verbose */ 1210 case 'v': /* verbose */
1115 verbose = 1; 1211 verbose = 1;
1116 break; 1212 break;
1213 case output_format_index: {
1214 parsed_output_format parser = mp_parse_output_format(optarg);
1215 if (!parser.parsing_success) {
1216 // TODO List all available formats here, maybe add anothoer usage function
1217 printf("Invalid output format: %s\n", optarg);
1218 exit(STATE_UNKNOWN);
1219 }
1220
1221 config.output_format_is_set = true;
1222 config.output_format = parser.output_format;
1223 break;
1224 }
1117 case '?': /* help */ 1225 case '?': /* help */
1118 usage5(); 1226 usage5();
1119 break; 1227 break;
@@ -1122,15 +1230,16 @@ static int call_getopt(int argc, char **argv) {
1122 break; 1230 break;
1123 } 1231 }
1124 } 1232 }
1125 return optind;
1126}
1127 1233
1128static int validate_arguments(int argc) { 1234 if (argc - optind > 0) {
1129
1130 if (argc - optind > 0)
1131 usage(_("Got unexpected non-option argument")); 1235 usage(_("Got unexpected non-option argument"));
1236 }
1132 1237
1133 return OK; 1238 process_arguments_wrapper result = {
1239 .config = config,
1240 .error = OK,
1241 };
1242 return result;
1134} 1243}
1135 1244
1136#if defined(__sun__) || defined(__solaris__) || defined(__hpux__) 1245#if defined(__sun__) || defined(__solaris__) || defined(__hpux__)
@@ -1249,7 +1358,7 @@ static int dl_bind(int fd, int sap, u_char *addr) {
1249 * 1358 *
1250 ***********************************************************************/ 1359 ***********************************************************************/
1251 1360
1252static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { 1361long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1253 int fd; 1362 int fd;
1254 u_char mac_addr[25]; 1363 u_char mac_addr[25];
1255 1364
@@ -1268,51 +1377,53 @@ static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1268#endif 1377#endif
1269 1378
1270/* resolve host name or die (TODO: move this to netutils.c!) */ 1379/* resolve host name or die (TODO: move this to netutils.c!) */
1271static void resolve_host(const char *in, struct in_addr *out) { 1380void resolve_host(const char *name, struct in_addr *out) {
1272 struct addrinfo hints, *ai; 1381 struct addrinfo hints = {
1382 .ai_family = PF_INET,
1383 };
1384 struct addrinfo *addr_info;
1273 1385
1274 memset(&hints, 0, sizeof(hints)); 1386 if (getaddrinfo(name, NULL, &hints, &addr_info) != 0) {
1275 hints.ai_family = PF_INET;
1276 if (getaddrinfo(in, NULL, &hints, &ai) != 0)
1277 usage_va(_("Invalid hostname/address - %s"), optarg); 1387 usage_va(_("Invalid hostname/address - %s"), optarg);
1388 }
1278 1389
1279 memcpy(out, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(*out)); 1390 memcpy(out, &((struct sockaddr_in *)addr_info->ai_addr)->sin_addr, sizeof(*out));
1280 freeaddrinfo(ai); 1391 freeaddrinfo(addr_info);
1281} 1392}
1282 1393
1283/* parse MAC address string, return 6 bytes (unterminated) or NULL */ 1394/* parse MAC address string, return 6 bytes (unterminated) or NULL */
1284static unsigned char *mac_aton(const char *string) { 1395unsigned char *mac_aton(const char *string) {
1285 static unsigned char result[6]; 1396 static unsigned char result[MAC_ADDR_LEN];
1286 char tmp[3]; 1397 char tmp[3];
1287 unsigned i, j; 1398 unsigned byte_counter = 0;
1288 1399
1289 for (i = 0, j = 0; string[i] != '\0' && j < sizeof(result); i++) { 1400 for (int i = 0; string[i] != '\0' && byte_counter < sizeof(result); i++) {
1290 /* ignore ':' and any other non-hex character */ 1401 /* ignore ':' and any other non-hex character */
1291 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) 1402 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) {
1292 continue; 1403 continue;
1404 }
1293 tmp[0] = string[i]; 1405 tmp[0] = string[i];
1294 tmp[1] = string[i + 1]; 1406 tmp[1] = string[i + 1];
1295 tmp[2] = '\0'; 1407 tmp[2] = '\0';
1296 result[j] = strtol(tmp, (char **)NULL, 16); 1408 result[byte_counter] = strtol(tmp, (char **)NULL, 16);
1297 i++; 1409 i++;
1298 j++; 1410 byte_counter++;
1299 } 1411 }
1300 1412
1301 return (j == 6) ? result : NULL; 1413 return (byte_counter == MAC_ADDR_LEN) ? result : NULL;
1302} 1414}
1303 1415
1304static void print_hardware_address(const unsigned char *address) { 1416void print_hardware_address(const unsigned char *address) {
1305 int i;
1306 1417
1307 printf(_("Hardware address: ")); 1418 printf(_("Hardware address: "));
1308 for (i = 0; i < 5; i++) 1419 for (int addr_idx = 0; addr_idx < MAC_ADDR_LEN; addr_idx++) {
1309 printf("%2.2x:", address[i]); 1420 printf("%2.2x:", address[addr_idx]);
1310 printf("%2.2x", address[i]); 1421 }
1311 putchar('\n'); 1422 putchar('\n');
1312} 1423}
1313 1424
1314/* print usage help */ 1425/* print usage help */
1315static void print_help(void) { 1426void print_help(void) {
1316 1427
1317 print_revision(progname, NP_VERSION); 1428 print_revision(progname, NP_VERSION);
1318 1429
@@ -1328,6 +1439,7 @@ static void print_help(void) {
1328 printf(UT_HELP_VRSN); 1439 printf(UT_HELP_VRSN);
1329 printf(UT_EXTRA_OPTS); 1440 printf(UT_EXTRA_OPTS);
1330 1441
1442 printf(UT_OUTPUT_FORMAT);
1331 printf(UT_VERBOSE); 1443 printf(UT_VERBOSE);
1332 1444
1333 printf(" %s\n", "-s, --serverip=IPADDRESS"); 1445 printf(" %s\n", "-s, --serverip=IPADDRESS");
@@ -1346,7 +1458,6 @@ static void print_help(void) {
1346 printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); 1458 printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s"));
1347 1459
1348 printf(UT_SUPPORT); 1460 printf(UT_SUPPORT);
1349 return;
1350} 1461}
1351 1462
1352void print_usage(void) { 1463void print_usage(void) {
@@ -1354,6 +1465,4 @@ void print_usage(void) {
1354 printf("%s\n", _("Usage:")); 1465 printf("%s\n", _("Usage:"));
1355 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname); 1466 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname);
1356 printf(" [-i interface] [-m mac]\n"); 1467 printf(" [-i interface] [-m mac]\n");
1357
1358 return;
1359} 1468}
diff --git a/plugins-root/check_dhcp.d/config.h b/plugins-root/check_dhcp.d/config.h
new file mode 100644
index 00000000..f189068b
--- /dev/null
+++ b/plugins-root/check_dhcp.d/config.h
@@ -0,0 +1,50 @@
1#pragma once
2
3#include "../../config.h"
4#include "../lib/states.h"
5#include <stdbool.h>
6#include <netinet/in.h>
7#include "net/if.h"
8#include "output.h"
9
10typedef struct requested_server_struct {
11 struct in_addr server_address;
12 bool answered;
13 struct requested_server_struct *next;
14} requested_server;
15
16typedef struct check_dhcp_config {
17 bool unicast_mode; /* unicast mode: mimic a DHCP relay */
18 bool exclusive_mode; /* exclusive mode aka "rogue DHCP server detection" */
19 int num_of_requested_servers;
20 struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
21 struct in_addr requested_address;
22 bool request_specific_address;
23
24 int dhcpoffer_timeout;
25 unsigned char *user_specified_mac;
26 char network_interface_name[IFNAMSIZ];
27 requested_server *requested_server_list;
28
29 mp_output_format output_format;
30 bool output_format_is_set;
31} check_dhcp_config;
32
33check_dhcp_config check_dhcp_config_init(void) {
34 check_dhcp_config tmp = {
35 .unicast_mode = false,
36 .exclusive_mode = false,
37 .num_of_requested_servers = 0,
38 .dhcp_ip = {0},
39 .requested_address = {0},
40 .request_specific_address = false,
41
42 .dhcpoffer_timeout = 2,
43 .user_specified_mac = NULL,
44 .network_interface_name = "eth0",
45 .requested_server_list = NULL,
46
47 .output_format_is_set = false,
48 };
49 return tmp;
50}
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index dcaceddb..d46d2ccc 100644
--- a/plugins-root/check_icmp.c
+++ b/plugins-root/check_icmp.c
@@ -46,6 +46,8 @@ const char *email = "devel@monitoring-plugins.org";
46#include "../plugins/common.h" 46#include "../plugins/common.h"
47#include "netutils.h" 47#include "netutils.h"
48#include "utils.h" 48#include "utils.h"
49#include "output.h"
50#include "perfdata.h"
49 51
50#if HAVE_SYS_SOCKIO_H 52#if HAVE_SYS_SOCKIO_H
51# include <sys/sockio.h> 53# include <sys/sockio.h>
@@ -65,6 +67,17 @@ const char *email = "devel@monitoring-plugins.org";
65#include <netinet/icmp6.h> 67#include <netinet/icmp6.h>
66#include <arpa/inet.h> 68#include <arpa/inet.h>
67#include <math.h> 69#include <math.h>
70#include <netdb.h>
71#include <sys/types.h>
72#include <unistd.h>
73#include <stdint.h>
74#include <sys/socket.h>
75#include <assert.h>
76#include <sys/select.h>
77
78#include "../lib/states.h"
79#include "./check_icmp.d/config.h"
80#include "./check_icmp.d/check_icmp_helpers.h"
68 81
69/** sometimes undefined system macros (quite a few, actually) **/ 82/** sometimes undefined system macros (quite a few, actually) **/
70#ifndef MAXTTL 83#ifndef MAXTTL
@@ -96,56 +109,8 @@ const char *email = "devel@monitoring-plugins.org";
96# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 109# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
97#endif 110#endif
98 111
99typedef unsigned short range_t; /* type for get_range() -- unimplemented */
100
101typedef struct rta_host {
102 unsigned short id; /* id in **table, and icmp pkts */
103 char *name; /* arg used for adding this host */
104 char *msg; /* icmp error message, if any */
105 struct sockaddr_storage saddr_in; /* the address of this host */
106 struct sockaddr_storage error_addr; /* stores address of error replies */
107 unsigned long long time_waited; /* total time waited, in usecs */
108 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
109 unsigned char icmp_type, icmp_code; /* type and code from errors */
110 unsigned short flags; /* control/status flags */
111 double rta; /* measured RTA */
112 int rta_status; // check result for RTA checks
113 double rtmax; /* max rtt */
114 double rtmin; /* min rtt */
115 double jitter; /* measured jitter */
116 int jitter_status; // check result for Jitter checks
117 double jitter_max; /* jitter rtt maximum */
118 double jitter_min; /* jitter rtt minimum */
119 double EffectiveLatency;
120 double mos; /* Mean opnion score */
121 int mos_status; // check result for MOS checks
122 double score; /* score */
123 int score_status; // check result for score checks
124 u_int last_tdiff;
125 u_int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
126 unsigned char pl; /* measured packet loss */
127 int pl_status; // check result for packet loss checks
128 struct rta_host *next; /* linked list */
129 int order_status; // check result for packet order checks
130} rta_host;
131
132#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */ 112#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
133 113
134/* threshold structure. all values are maximum allowed, exclusive */
135typedef struct threshold {
136 unsigned char pl; /* max allowed packet loss in percent */
137 unsigned int rta; /* roundtrip time average, microseconds */
138 double jitter; /* jitter time average, microseconds */
139 double mos; /* MOS */
140 double score; /* Score */
141} threshold;
142
143/* the data structure */
144typedef struct icmp_ping_data {
145 struct timeval stime; /* timestamp (saved in protocol struct as well) */
146 unsigned short ping_id;
147} icmp_ping_data;
148
149typedef union ip_hdr { 114typedef union ip_hdr {
150 struct ip ip; 115 struct ip ip;
151 struct ip6_hdr ip6; 116 struct ip6_hdr ip6;
@@ -158,24 +123,6 @@ typedef union icmp_packet {
158 u_short *cksum_in; 123 u_short *cksum_in;
159} icmp_packet; 124} icmp_packet;
160 125
161/* the different modes of this program are as follows:
162 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
163 * MODE_HOSTCHECK: Return immediately upon any sign of life
164 * In addition, sends packets to ALL addresses assigned
165 * to this host (as returned by gethostbyname() or
166 * gethostbyaddr() and expects one host only to be checked at
167 * a time. Therefore, any packet response what so ever will
168 * count as a sign of life, even when received outside
169 * crit.rta limit. Do not misspell any additional IP's.
170 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
171 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
172 * tcp and udp args does this)
173 */
174#define MODE_RTA 0
175#define MODE_HOSTCHECK 1
176#define MODE_ALL 2
177#define MODE_ICMP 3
178
179enum enum_threshold_mode { 126enum enum_threshold_mode {
180 const_rta_mode, 127 const_rta_mode,
181 const_packet_loss_mode, 128 const_packet_loss_mode,
@@ -186,89 +133,487 @@ enum enum_threshold_mode {
186 133
187typedef enum enum_threshold_mode threshold_mode; 134typedef enum enum_threshold_mode threshold_mode;
188 135
189/* the different ping types we can do
190 * TODO: investigate ARP ping as well */
191#define HAVE_ICMP 1
192#define HAVE_UDP 2
193#define HAVE_TCP 4
194#define HAVE_ARP 8
195
196#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
197#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
198#define IP_HDR_SIZE 20
199#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
200#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
201
202/* various target states */
203#define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
204#define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
205#define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
206#define TSTATE_UNREACH 0x08
207
208/** prototypes **/ 136/** prototypes **/
209void print_help(void); 137void print_help();
210void print_usage(void); 138void print_usage(void);
211static u_int get_timevar(const char *); 139
212static u_int get_timevaldiff(struct timeval *, struct timeval *); 140/* Time related */
213static in_addr_t get_ip_address(const char *); 141typedef struct {
214static int wait_for_reply(int, u_int); 142 int error_code;
215static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *); 143 time_t time_range;
216static int send_icmp_ping(int, struct rta_host *); 144} get_timevar_wrapper;
217static int get_threshold(char *str, threshold *th); 145static get_timevar_wrapper get_timevar(const char *str);
218static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode); 146static time_t get_timevaldiff(struct timeval earlier, struct timeval later);
219static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode); 147static time_t get_timevaldiff_to_now(struct timeval earlier);
220static void run_checks(void); 148
221static void set_source_ip(char *); 149static in_addr_t get_ip_address(const char *ifname);
222static int add_target(char *); 150static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family);
223static int add_target_ip(char *, struct sockaddr_storage *); 151
224static int handle_random_icmp(unsigned char *, struct sockaddr_storage *); 152/* Receiving data */
225static void parse_address(struct sockaddr_storage *, char *, int); 153static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval,
226static unsigned short icmp_checksum(uint16_t *, size_t); 154 unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
227static void finish(int); 155 ping_target **table, unsigned short packets,
228static void crash(const char *, ...); 156 unsigned short number_of_targets, check_icmp_state *program_state);
229 157
230/** external **/ 158typedef struct {
231extern int optind; 159 sa_family_t recv_proto;
232extern char *optarg; 160 ssize_t received;
233extern char **environ; 161} recvfrom_wto_wrapper;
162static recvfrom_wto_wrapper recvfrom_wto(check_icmp_socket_set sockset, void *buf, unsigned int len,
163 struct sockaddr *saddr, time_t *timeout,
164 struct timeval *received_timestamp);
165static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
166 time_t *target_interval, uint16_t sender_id, ping_target **table,
167 unsigned short packets, unsigned short number_of_targets,
168 check_icmp_state *program_state);
169
170/* Sending data */
171static int send_icmp_ping(check_icmp_socket_set sockset, ping_target *host,
172 unsigned short icmp_pkt_size, uint16_t sender_id,
173 check_icmp_state *program_state);
174
175/* Threshold related */
176typedef struct {
177 int errorcode;
178 check_icmp_threshold threshold;
179} get_threshold_wrapper;
180static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold);
181
182typedef struct {
183 int errorcode;
184 check_icmp_threshold warn;
185 check_icmp_threshold crit;
186} get_threshold2_wrapper;
187static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
188 check_icmp_threshold crit, threshold_mode mode);
189
190typedef struct {
191 int errorcode;
192 check_icmp_threshold result;
193} parse_threshold2_helper_wrapper;
194static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
195 size_t length,
196 check_icmp_threshold thr,
197 threshold_mode mode);
198
199/* main test function */
200static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
201 check_icmp_execution_mode mode, time_t max_completion_time,
202 struct timeval prog_start, ping_target **table, unsigned short packets,
203 check_icmp_socket_set sockset, unsigned short number_of_targets,
204 check_icmp_state *program_state);
205mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
206 check_icmp_threshold warn, check_icmp_threshold crit);
207
208typedef struct {
209 int targets_ok;
210 int targets_warn;
211 mp_subcheck sc_host;
212} evaluate_host_wrapper;
213evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
214 check_icmp_mode_switches modes, check_icmp_threshold warn,
215 check_icmp_threshold crit);
216
217/* Target acquisition */
218typedef struct {
219 int error_code;
220 check_icmp_target_container host;
221 bool has_v4;
222 bool has_v6;
223} add_host_wrapper;
224static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
225 sa_family_t enforced_proto);
226
227typedef struct {
228 int error_code;
229 ping_target *targets;
230 unsigned int number_of_targets;
231 bool has_v4;
232 bool has_v6;
233} add_target_wrapper;
234static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode,
235 sa_family_t enforced_proto);
236
237typedef struct {
238 int error_code;
239 ping_target *target;
240} add_target_ip_wrapper;
241static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address);
242
243static void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size);
244
245static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size);
246
247/* End of run function */
248static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive,
249 check_icmp_threshold warn, check_icmp_threshold crit,
250 unsigned short number_of_targets, check_icmp_state *program_state,
251 check_icmp_target_container host_list[], unsigned short number_of_hosts,
252 mp_check overall[static 1]);
253
254/* Error exit */
255static void crash(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
234 256
235/** global variables **/ 257/** global variables **/
236static struct rta_host **table, *cursor, *list; 258static int debug = 0;
237 259
238static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 260extern unsigned int timeout;
239static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 261
240 262/** the working code **/
241static int mode, protocols, sockets, debug = 0, timeout = 10; 263static inline unsigned short targets_alive(unsigned short targets, unsigned short targets_down) {
242static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; 264 return targets - targets_down;
243static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; 265}
244 266static inline unsigned int icmp_pkts_en_route(unsigned int icmp_sent, unsigned int icmp_recv,
245static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0; 267 unsigned int icmp_lost) {
246#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost)) 268 return icmp_sent - (icmp_recv + icmp_lost);
247static unsigned short targets_down = 0, targets = 0, packets = 0; 269}
248#define targets_alive (targets - targets_down) 270
249static unsigned int retry_interval, pkt_interval, target_interval; 271// Create configuration from cli parameters
250static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK; 272typedef struct {
251static pid_t pid; 273 int errorcode;
252static struct timezone tz; 274 check_icmp_config config;
253static struct timeval prog_start; 275} check_icmp_config_wrapper;
254static unsigned long long max_completion_time = 0; 276check_icmp_config_wrapper process_arguments(int argc, char **argv) {
255static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */ 277 /* get calling name the old-fashioned way for portability instead
256static int min_hosts_alive = -1; 278 * of relying on the glibc-ism __progname */
257static float pkt_backoff_factor = 1.5; 279 char *ptr = strrchr(argv[0], '/');
258static float target_backoff_factor = 1.5; 280 if (ptr) {
259static bool rta_mode = false; 281 progname = &ptr[1];
260static bool pl_mode = false; 282 } else {
261static bool jitter_mode = false; 283 progname = argv[0];
262static bool score_mode = false; 284 }
263static bool mos_mode = false; 285
264static bool order_mode = false; 286 check_icmp_config_wrapper result = {
287 .errorcode = OK,
288 .config = check_icmp_config_init(),
289 };
290
291 /* use the pid to mark packets as ours */
292 /* Some systems have 32-bit pid_t so mask off only 16 bits */
293 result.config.sender_id = getpid() & 0xffff;
294
295 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
296 result.config.mode = MODE_ICMP;
297 } else if (!strcmp(progname, "check_host")) {
298 result.config.mode = MODE_HOSTCHECK;
299 result.config.number_of_packets = 5;
300 result.config.crit.rta = result.config.warn.rta = 1000000;
301 result.config.crit.pl = result.config.warn.pl = 100;
302 } else if (!strcmp(progname, "check_rta_multi")) {
303 result.config.mode = MODE_ALL;
304 result.config.target_interval = 0;
305 result.config.number_of_packets = 5;
306 }
307 /* support "--help" and "--version" */
308 if (argc == 2) {
309 if (!strcmp(argv[1], "--help")) {
310 strcpy(argv[1], "-h");
311 }
312 if (!strcmp(argv[1], "--version")) {
313 strcpy(argv[1], "-V");
314 }
315 }
316
317 sa_family_t enforced_ai_family = AF_UNSPEC;
318
319 enum {
320 output_format_index = CHAR_MAX + 1,
321 };
322
323 struct option longopts[] = {
324 {"version", no_argument, 0, 'V'},
325 {"help", no_argument, 0, 'h'},
326 {"verbose", no_argument, 0, 'v'},
327 {"Host", required_argument, 0, 'H'},
328 {"ipv4-only", no_argument, 0, '4'},
329 {"ipv6-only", no_argument, 0, '6'},
330 {"warning", required_argument, 0, 'w'},
331 {"critical", required_argument, 0, 'c'},
332 {"rta-mode-thresholds", required_argument, 0, 'R'},
333 {"packet-loss-mode-thresholds", required_argument, 0, 'P'},
334 {"jitter-mode-thresholds", required_argument, 0, 'J'},
335 {"mos-mode-thresholds", required_argument, 0, 'M'},
336 {"score-mode-thresholds", required_argument, 0, 'S'},
337 {"out-of-order-packets", no_argument, 0, 'O'},
338 {"number-of-packets", required_argument, 0, 'n'},
339 {"number-of-packets", required_argument, 0, 'p'},
340 {"packet-interval", required_argument, 0, 'i'},
341 {"target-interval", required_argument, 0, 'I'},
342 {"minimal-host-alive", required_argument, 0, 'm'},
343 {"outgoing-ttl", required_argument, 0, 'l'},
344 {"size", required_argument, 0, 'b'},
345 {"output-format", required_argument, 0, output_format_index},
346 {},
347 };
348
349 // Parse protocol arguments first
350 // and count hosts here
351 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
352 for (int i = 1; i < argc; i++) {
353 long int arg;
354 while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) {
355 switch (arg) {
356
357 case '4':
358 if (enforced_ai_family != AF_UNSPEC) {
359 crash("Multiple protocol versions not supported");
360 }
361 enforced_ai_family = AF_INET;
362 break;
363 case '6':
364 if (enforced_ai_family != AF_UNSPEC) {
365 crash("Multiple protocol versions not supported");
366 }
367 enforced_ai_family = AF_INET6;
368 break;
369 case 'H': {
370 result.config.number_of_hosts++;
371 break;
372 }
373 case 'h': /* help */
374 // Trigger help here to avoid adding hosts before that (and doing DNS queries)
375 print_help();
376 exit(STATE_UNKNOWN);
377 break;
378 case 'v':
379 debug++;
380 break;
381 }
382 }
383 }
384
385 char **tmp = &argv[optind];
386 while (*tmp) {
387 result.config.number_of_hosts++;
388 tmp++;
389 }
390
391 // Sanity check: if hostmode is selected,only a single host is allowed
392 if (result.config.mode == MODE_HOSTCHECK && result.config.number_of_hosts > 1) {
393 usage("check_host only allows a single host");
394 }
395
396 // Allocate hosts
397 result.config.hosts =
398 calloc(result.config.number_of_hosts, sizeof(check_icmp_target_container));
399 if (result.config.hosts == NULL) {
400 crash("failed to allocate memory");
401 }
402
403 /* Reset argument scanning */
404 optind = 1;
405
406 int host_counter = 0;
407 /* parse the arguments */
408 for (int i = 1; i < argc; i++) {
409 long int arg;
410 while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) {
411 switch (arg) {
412 case 'b': {
413 long size = strtol(optarg, NULL, 0);
414 if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
415 size < MAX_PING_DATA) {
416 result.config.icmp_data_size = (unsigned short)size;
417 } else {
418 usage_va("ICMP data length must be between: %lu and %lu",
419 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
420 MAX_PING_DATA - 1);
421 }
422 } break;
423 case 'i': {
424 // packet_interval was unused and is now removed
425 } break;
426 case 'I': {
427 get_timevar_wrapper parsed_time = get_timevar(optarg);
428
429 if (parsed_time.error_code == OK) {
430 result.config.target_interval = parsed_time.time_range;
431 } else {
432 crash("failed to parse target interval");
433 }
434 } break;
435 case 'w': {
436 get_threshold_wrapper warn = get_threshold(optarg, result.config.warn);
437 if (warn.errorcode == OK) {
438 result.config.warn = warn.threshold;
439 } else {
440 crash("failed to parse warning threshold");
441 }
442 } break;
443 case 'c': {
444 get_threshold_wrapper crit = get_threshold(optarg, result.config.crit);
445 if (crit.errorcode == OK) {
446 result.config.crit = crit.threshold;
447 } else {
448 crash("failed to parse critical threshold");
449 }
450 } break;
451 case 'n':
452 case 'p':
453 result.config.number_of_packets = (unsigned short)strtoul(optarg, NULL, 0);
454 if (result.config.number_of_packets > 20) {
455 errno = 0;
456 crash("packets is > 20 (%d)", result.config.number_of_packets);
457 }
458 break;
459 case 't':
460 // WARNING Deprecated since execution time is determined by the other factors
461 break;
462 case 'H': {
463 add_host_wrapper host_add_result =
464 add_host(optarg, result.config.mode, enforced_ai_family);
465 if (host_add_result.error_code == OK) {
466 result.config.hosts[host_counter] = host_add_result.host;
467 host_counter++;
468
469 if (result.config.targets != NULL) {
470 result.config.number_of_targets += ping_target_list_append(
471 result.config.targets, host_add_result.host.target_list);
472 } else {
473 result.config.targets = host_add_result.host.target_list;
474 result.config.number_of_targets += host_add_result.host.number_of_targets;
475 }
476
477 if (host_add_result.has_v4) {
478 result.config.need_v4 = true;
479 }
480 if (host_add_result.has_v6) {
481 result.config.need_v6 = true;
482 }
483 } else {
484 crash("Failed to add host, unable to parse it correctly");
485 }
486 } break;
487 case 'l':
488 result.config.ttl = strtoul(optarg, NULL, 0);
489 break;
490 case 'm':
491 result.config.min_hosts_alive = (int)strtoul(optarg, NULL, 0);
492 break;
493 case 's': /* specify source IP address */
494 result.config.source_ip = optarg;
495 break;
496 case 'V': /* version */
497 print_revision(progname, NP_VERSION);
498 exit(STATE_UNKNOWN);
499 case 'R': /* RTA mode */ {
500 get_threshold2_wrapper rta_th = get_threshold2(
501 optarg, strlen(optarg), result.config.warn, result.config.crit, const_rta_mode);
502
503 if (rta_th.errorcode != OK) {
504 crash("Failed to parse RTA threshold");
505 }
506
507 result.config.warn = rta_th.warn;
508 result.config.crit = rta_th.crit;
509 result.config.modes.rta_mode = true;
510 } break;
511 case 'P': /* packet loss mode */ {
512 get_threshold2_wrapper pl_th =
513 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
514 const_packet_loss_mode);
515 if (pl_th.errorcode != OK) {
516 crash("Failed to parse packet loss threshold");
517 }
518
519 result.config.warn = pl_th.warn;
520 result.config.crit = pl_th.crit;
521 result.config.modes.pl_mode = true;
522 } break;
523 case 'J': /* jitter mode */ {
524 get_threshold2_wrapper jitter_th =
525 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
526 const_jitter_mode);
527 if (jitter_th.errorcode != OK) {
528 crash("Failed to parse jitter threshold");
529 }
530
531 result.config.warn = jitter_th.warn;
532 result.config.crit = jitter_th.crit;
533 result.config.modes.jitter_mode = true;
534 } break;
535 case 'M': /* MOS mode */ {
536 get_threshold2_wrapper mos_th = get_threshold2(
537 optarg, strlen(optarg), result.config.warn, result.config.crit, const_mos_mode);
538 if (mos_th.errorcode != OK) {
539 crash("Failed to parse MOS threshold");
540 }
541
542 result.config.warn = mos_th.warn;
543 result.config.crit = mos_th.crit;
544 result.config.modes.mos_mode = true;
545 } break;
546 case 'S': /* score mode */ {
547 get_threshold2_wrapper score_th =
548 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
549 const_score_mode);
550 if (score_th.errorcode != OK) {
551 crash("Failed to parse score threshold");
552 }
553
554 result.config.warn = score_th.warn;
555 result.config.crit = score_th.crit;
556 result.config.modes.score_mode = true;
557 } break;
558 case 'O': /* out of order mode */
559 result.config.modes.order_mode = true;
560 break;
561 case output_format_index: {
562 parsed_output_format parser = mp_parse_output_format(optarg);
563 if (!parser.parsing_success) {
564 // TODO List all available formats here, maybe add anothoer usage function
565 printf("Invalid output format: %s\n", optarg);
566 exit(STATE_UNKNOWN);
567 }
568
569 result.config.output_format_is_set = true;
570 result.config.output_format = parser.output_format;
571 break;
572 }
573 }
574 }
575 }
576
577 argv = &argv[optind];
578 while (*argv) {
579 add_target(*argv, result.config.mode, enforced_ai_family);
580 argv++;
581 }
582
583 if (!result.config.number_of_targets) {
584 errno = 0;
585 crash("No hosts to check");
586 }
587
588 /* stupid users should be able to give whatever thresholds they want
589 * (nothing will break if they do), but some anal plugin maintainer
590 * will probably add some printf() thing here later, so it might be
591 * best to at least show them where to do it. ;) */
592 if (result.config.warn.pl > result.config.crit.pl) {
593 result.config.warn.pl = result.config.crit.pl;
594 }
595 if (result.config.warn.rta > result.config.crit.rta) {
596 result.config.warn.rta = result.config.crit.rta;
597 }
598 if (result.config.warn.jitter > result.config.crit.jitter) {
599 result.config.crit.jitter = result.config.warn.jitter;
600 }
601 if (result.config.warn.mos < result.config.crit.mos) {
602 result.config.warn.mos = result.config.crit.mos;
603 }
604 if (result.config.warn.score < result.config.crit.score) {
605 result.config.warn.score = result.config.crit.score;
606 }
607
608 return result;
609}
265 610
266/** code start **/ 611/** code start **/
267static void crash(const char *fmt, ...) { 612static void crash(const char *fmt, ...) {
268 va_list ap;
269 613
270 printf("%s: ", progname); 614 printf("%s: ", progname);
271 615
616 va_list ap;
272 va_start(ap, fmt); 617 va_start(ap, fmt);
273 vprintf(fmt, ap); 618 vprintf(fmt, ap);
274 va_end(ap); 619 va_end(ap);
@@ -385,18 +730,20 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm
385 return msg; 730 return msg;
386} 731}
387 732
388static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { 733static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
389 struct icmp p, sent_icmp; 734 time_t *target_interval, const uint16_t sender_id,
390 struct rta_host *host = NULL; 735 ping_target **table, unsigned short packets,
391 736 const unsigned short number_of_targets,
392 memcpy(&p, packet, sizeof(p)); 737 check_icmp_state *program_state) {
393 if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { 738 struct icmp icmp_packet;
739 memcpy(&icmp_packet, packet, sizeof(icmp_packet));
740 if (icmp_packet.icmp_type == ICMP_ECHO && ntohs(icmp_packet.icmp_id) == sender_id) {
394 /* echo request from us to us (pinging localhost) */ 741 /* echo request from us to us (pinging localhost) */
395 return 0; 742 return 0;
396 } 743 }
397 744
398 if (debug) { 745 if (debug) {
399 printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr); 746 printf("handle_random_icmp(%p, %p)\n", (void *)&icmp_packet, (void *)addr);
400 } 747 }
401 748
402 /* only handle a few types, since others can't possibly be replies to 749 /* only handle a few types, since others can't possibly be replies to
@@ -409,14 +756,17 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
409 * TIMXCEED actually sends a proper icmp response we will have passed 756 * TIMXCEED actually sends a proper icmp response we will have passed
410 * too many hops to have a hope of reaching it later, in which case it 757 * too many hops to have a hope of reaching it later, in which case it
411 * indicates overconfidence in the network, poor routing or both. */ 758 * indicates overconfidence in the network, poor routing or both. */
412 if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) { 759 if (icmp_packet.icmp_type != ICMP_UNREACH && icmp_packet.icmp_type != ICMP_TIMXCEED &&
760 icmp_packet.icmp_type != ICMP_SOURCEQUENCH && icmp_packet.icmp_type != ICMP_PARAMPROB) {
413 return 0; 761 return 0;
414 } 762 }
415 763
416 /* might be for us. At least it holds the original package (according 764 /* might be for us. At least it holds the original package (according
417 * to RFC 792). If it isn't, just ignore it */ 765 * to RFC 792). If it isn't, just ignore it */
766 struct icmp sent_icmp;
418 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); 767 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp));
419 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || ntohs(sent_icmp.icmp_seq) >= targets * packets) { 768 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != sender_id ||
769 ntohs(sent_icmp.icmp_seq) >= number_of_targets * packets) {
420 if (debug) { 770 if (debug) {
421 printf("Packet is no response to a packet we sent\n"); 771 printf("Packet is no response to a packet we sent\n");
422 } 772 }
@@ -424,14 +774,15 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
424 } 774 }
425 775
426 /* it is indeed a response for us */ 776 /* it is indeed a response for us */
427 host = table[ntohs(sent_icmp.icmp_seq) / packets]; 777 ping_target *host = table[ntohs(sent_icmp.icmp_seq) / packets];
428 if (debug) { 778 if (debug) {
429 char address[INET6_ADDRSTRLEN]; 779 char address[INET6_ADDRSTRLEN];
430 parse_address(addr, address, sizeof(address)); 780 parse_address(addr, address, sizeof(address));
431 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", get_icmp_error_msg(p.icmp_type, p.icmp_code), address, host->name); 781 printf("Received \"%s\" from %s for ICMP ECHO sent.\n",
782 get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address);
432 } 783 }
433 784
434 icmp_lost++; 785 program_state->icmp_lost++;
435 host->icmp_lost++; 786 host->icmp_lost++;
436 /* don't spend time on lost hosts any more */ 787 /* don't spend time on lost hosts any more */
437 if (host->flags & FLAG_LOST_CAUSE) { 788 if (host->flags & FLAG_LOST_CAUSE) {
@@ -440,305 +791,104 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
440 791
441 /* source quench means we're sending too fast, so increase the 792 /* source quench means we're sending too fast, so increase the
442 * interval and mark this packet lost */ 793 * interval and mark this packet lost */
443 if (p.icmp_type == ICMP_SOURCEQUENCH) { 794 if (icmp_packet.icmp_type == ICMP_SOURCEQUENCH) {
444 pkt_interval *= pkt_backoff_factor; 795 *target_interval = (unsigned int)((double)*target_interval * TARGET_BACKOFF_FACTOR);
445 target_interval *= target_backoff_factor;
446 } else { 796 } else {
447 targets_down++; 797 program_state->targets_down++;
448 host->flags |= FLAG_LOST_CAUSE; 798 host->flags |= FLAG_LOST_CAUSE;
449 } 799 }
450 host->icmp_type = p.icmp_type; 800 host->icmp_type = icmp_packet.icmp_type;
451 host->icmp_code = p.icmp_code; 801 host->icmp_code = icmp_packet.icmp_code;
452 host->error_addr = *addr; 802 host->error_addr = *addr;
453 803
454 return 0; 804 return 0;
455} 805}
456 806
457void parse_address(struct sockaddr_storage *addr, char *address, int size) { 807void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size) {
458 switch (address_family) { 808 switch (addr->ss_family) {
459 case AF_INET: 809 case AF_INET:
460 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size); 810 inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, dst, size);
461 break; 811 break;
462 case AF_INET6: 812 case AF_INET6:
463 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size); 813 inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, dst, size);
464 break; 814 break;
815 default:
816 assert(false);
465 } 817 }
466} 818}
467 819
468int main(int argc, char **argv) { 820int main(int argc, char **argv) {
469 int i;
470 char *ptr;
471 long int arg;
472 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
473 int result;
474 struct rta_host *host;
475#ifdef HAVE_SIGACTION
476 struct sigaction sig_action;
477#endif
478#ifdef SO_TIMESTAMP
479 int on = 1;
480#endif
481 char *source_ip = NULL;
482 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
483 setlocale(LC_ALL, ""); 821 setlocale(LC_ALL, "");
484 bindtextdomain(PACKAGE, LOCALEDIR); 822 bindtextdomain(PACKAGE, LOCALEDIR);
485 textdomain(PACKAGE); 823 textdomain(PACKAGE);
486 824
487 /* we only need to be setsuid when we get the sockets, so do 825 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
488 * that before pointer magic (esp. on network data) */ 826 environ = NULL;
489 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
490
491 address_family = -1;
492 int icmp_proto = IPPROTO_ICMP;
493 827
494 /* get calling name the old-fashioned way for portability instead 828 /* Parse extra opts if any */
495 * of relying on the glibc-ism __progname */ 829 argv = np_extra_opts(&argc, argv, progname);
496 ptr = strrchr(argv[0], '/');
497 if (ptr) {
498 progname = &ptr[1];
499 } else {
500 progname = argv[0];
501 }
502 830
503 /* now set defaults. Use progname to set them initially (allows for 831 check_icmp_config_wrapper tmp_config = process_arguments(argc, argv);
504 * superfast check_host program when target host is up */
505 cursor = list = NULL;
506 table = NULL;
507
508 mode = MODE_RTA;
509 /* Default critical thresholds */
510 crit.rta = 500000;
511 crit.pl = 80;
512 crit.jitter = 50;
513 crit.mos = 3;
514 crit.score = 70;
515 /* Default warning thresholds */
516 warn.rta = 200000;
517 warn.pl = 40;
518 warn.jitter = 40;
519 warn.mos = 3.5;
520 warn.score = 80;
521
522 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
523 pkt_interval = 80000; /* 80 msec packet interval by default */
524 packets = 5;
525 832
526 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) { 833 if (tmp_config.errorcode != OK) {
527 mode = MODE_ICMP; 834 crash("failed to parse config");
528 protocols = HAVE_ICMP;
529 } else if (!strcmp(progname, "check_host")) {
530 mode = MODE_HOSTCHECK;
531 pkt_interval = 1000000;
532 packets = 5;
533 crit.rta = warn.rta = 1000000;
534 crit.pl = warn.pl = 100;
535 } else if (!strcmp(progname, "check_rta_multi")) {
536 mode = MODE_ALL;
537 target_interval = 0;
538 pkt_interval = 50000;
539 packets = 5;
540 } 835 }
541 836
542 /* support "--help" and "--version" */ 837 const check_icmp_config config = tmp_config.config;
543 if (argc == 2) {
544 if (!strcmp(argv[1], "--help")) {
545 strcpy(argv[1], "-h");
546 }
547 if (!strcmp(argv[1], "--version")) {
548 strcpy(argv[1], "-V");
549 }
550 }
551 838
552 /* Parse protocol arguments first */ 839 if (config.output_format_is_set) {
553 for (i = 1; i < argc; i++) { 840 mp_set_format(config.output_format);
554 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
555 switch (arg) {
556 case '4':
557 if (address_family != -1) {
558 crash("Multiple protocol versions not supported");
559 }
560 address_family = AF_INET;
561 break;
562 case '6':
563#ifdef USE_IPV6
564 if (address_family != -1) {
565 crash("Multiple protocol versions not supported");
566 }
567 address_family = AF_INET6;
568#else
569 usage(_("IPv6 support not available\n"));
570#endif
571 break;
572 }
573 }
574 } 841 }
575 842
576 /* Reset argument scanning */ 843 check_icmp_socket_set sockset = {
577 optind = 1; 844 .socket4 = -1,
845 .socket6 = -1,
846 };
578 847
579 unsigned long size; 848 if (config.need_v4) {
580 bool err; 849 sockset.socket4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
581 /* parse the arguments */ 850 if (sockset.socket4 == -1) {
582 for (i = 1; i < argc; i++) { 851 crash("Failed to obtain ICMP v4 socket");
583 while ((arg = getopt(argc, argv, opts_str)) != EOF) { 852 }
584 switch (arg) {
585 case 'v':
586 debug++;
587 break;
588 case 'b':
589 size = strtol(optarg, NULL, 0);
590 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) {
591 icmp_data_size = size;
592 icmp_pkt_size = size + ICMP_MINLEN;
593 } else {
594 usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data),
595 MAX_PING_DATA - 1);
596 }
597 break;
598 case 'i':
599 pkt_interval = get_timevar(optarg);
600 break;
601 case 'I':
602 target_interval = get_timevar(optarg);
603 break;
604 case 'w':
605 get_threshold(optarg, &warn);
606 break;
607 case 'c':
608 get_threshold(optarg, &crit);
609 break;
610 case 'n':
611 case 'p':
612 packets = strtoul(optarg, NULL, 0);
613 break;
614 case 't':
615 timeout = strtoul(optarg, NULL, 0);
616 if (!timeout) {
617 timeout = 10;
618 }
619 break;
620 case 'H':
621 add_target(optarg);
622 break;
623 case 'l':
624 ttl = (int)strtoul(optarg, NULL, 0);
625 break;
626 case 'm':
627 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
628 break;
629 case 'd': /* implement later, for cluster checks */
630 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
631 if (ptr) {
632 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
633 }
634 break;
635 case 's': /* specify source IP address */
636 source_ip = optarg;
637 break;
638 case 'V': /* version */
639 print_revision(progname, NP_VERSION);
640 exit(STATE_UNKNOWN);
641 case 'h': /* help */
642 print_help();
643 exit(STATE_UNKNOWN);
644 break;
645 case 'R': /* RTA mode */
646 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_rta_mode);
647 if (!err) {
648 crash("Failed to parse RTA threshold");
649 }
650 853
651 rta_mode = true; 854 if (config.source_ip) {
652 break;
653 case 'P': /* packet loss mode */
654 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_packet_loss_mode);
655 if (!err) {
656 crash("Failed to parse packet loss threshold");
657 }
658 855
659 pl_mode = true; 856 struct in_addr tmp = {};
660 break; 857 int error_code = inet_pton(AF_INET, config.source_ip, &tmp);
661 case 'J': /* jitter mode */ 858 if (error_code == 1) {
662 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode); 859 set_source_ip(config.source_ip, sockset.socket4, AF_INET);
663 if (!err) { 860 } else {
664 crash("Failed to parse jitter threshold"); 861 // just try this mindlessly if it's not a v4 address
665 } 862 set_source_ip(config.source_ip, sockset.socket6, AF_INET6);
863 }
864 }
666 865
667 jitter_mode = true; 866#ifdef SO_TIMESTAMP
668 break; 867 if (sockset.socket4 != -1) {
669 case 'M': /* MOS mode */ 868 int on = 1;
670 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode); 869 if (setsockopt(sockset.socket4, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
671 if (!err) { 870 if (debug) {
672 crash("Failed to parse MOS threshold"); 871 printf("Warning: no SO_TIMESTAMP support\n");
673 } 872 }
674 873 }
675 mos_mode = true; 874 }
676 break; 875 if (sockset.socket6 != -1) {
677 case 'S': /* score mode */ 876 int on = 1;
678 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode); 877 if (setsockopt(sockset.socket6, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
679 if (!err) { 878 if (debug) {
680 crash("Failed to parse score threshold"); 879 printf("Warning: no SO_TIMESTAMP support\n");
681 } 880 }
682
683 score_mode = true;
684 break;
685 case 'O': /* out of order mode */
686 order_mode = true;
687 break;
688 } 881 }
689 } 882 }
883#endif // SO_TIMESTAMP
690 } 884 }
691 885
692 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ 886 if (config.need_v6) {
693 environ = NULL; 887 sockset.socket6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
694 888 if (sockset.socket6 == -1) {
695 /* use the pid to mark packets as ours */ 889 crash("Failed to obtain ICMP v6 socket");
696 /* Some systems have 32-bit pid_t so mask off only 16 bits */
697 pid = getpid() & 0xffff;
698 /* printf("pid = %u\n", pid); */
699
700 /* Parse extra opts if any */
701 argv = np_extra_opts(&argc, argv, progname);
702
703 argv = &argv[optind];
704 while (*argv) {
705 add_target(*argv);
706 argv++;
707 }
708
709 if (!targets) {
710 errno = 0;
711 crash("No hosts to check");
712 }
713
714 // add_target might change address_family
715 switch (address_family) {
716 case AF_INET:
717 icmp_proto = IPPROTO_ICMP;
718 break;
719 case AF_INET6:
720 icmp_proto = IPPROTO_ICMPV6;
721 break;
722 default:
723 crash("Address family not supported");
724 }
725 if ((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1) {
726 sockets |= HAVE_ICMP;
727 } else {
728 icmp_sockerrno = errno;
729 }
730
731 if (source_ip) {
732 set_source_ip(source_ip);
733 }
734
735#ifdef SO_TIMESTAMP
736 if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
737 if (debug) {
738 printf("Warning: no SO_TIMESTAMP support\n");
739 } 890 }
740 } 891 }
741#endif // SO_TIMESTAMP
742 892
743 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ 893 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
744 if (setuid(getuid()) == -1) { 894 if (setuid(getuid()) == -1) {
@@ -746,186 +896,179 @@ int main(int argc, char **argv) {
746 return 1; 896 return 1;
747 } 897 }
748 898
749 if (!sockets) { 899 if (sockset.socket4) {
750 if (icmp_sock == -1) { 900 int result = setsockopt(sockset.socket4, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl));
751 errno = icmp_sockerrno;
752 crash("Failed to obtain ICMP socket");
753 return -1;
754 }
755 /* if(udp_sock == -1) { */
756 /* errno = icmp_sockerrno; */
757 /* crash("Failed to obtain UDP socket"); */
758 /* return -1; */
759 /* } */
760 /* if(tcp_sock == -1) { */
761 /* errno = icmp_sockerrno; */
762 /* crash("Failed to obtain TCP socker"); */
763 /* return -1; */
764 /* } */
765 }
766 if (!ttl) {
767 ttl = 64;
768 }
769
770 if (icmp_sock) {
771 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
772 if (debug) { 901 if (debug) {
773 if (result == -1) { 902 if (result == -1) {
774 printf("setsockopt failed\n"); 903 printf("setsockopt failed\n");
775 } else { 904 } else {
776 printf("ttl set to %u\n", ttl); 905 printf("ttl set to %lu\n", config.ttl);
777 } 906 }
778 } 907 }
779 } 908 }
780 909
781 /* stupid users should be able to give whatever thresholds they want 910 if (sockset.socket6) {
782 * (nothing will break if they do), but some anal plugin maintainer 911 int result = setsockopt(sockset.socket6, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl));
783 * will probably add some printf() thing here later, so it might be 912 if (debug) {
784 * best to at least show them where to do it. ;) */ 913 if (result == -1) {
785 if (warn.pl > crit.pl) { 914 printf("setsockopt failed\n");
786 warn.pl = crit.pl; 915 } else {
787 } 916 printf("ttl set to %lu\n", config.ttl);
788 if (warn.rta > crit.rta) { 917 }
789 warn.rta = crit.rta; 918 }
790 }
791 if (warn_down > crit_down) {
792 crit_down = warn_down;
793 }
794 if (warn.jitter > crit.jitter) {
795 crit.jitter = warn.jitter;
796 }
797 if (warn.mos < crit.mos) {
798 warn.mos = crit.mos;
799 }
800 if (warn.score < crit.score) {
801 warn.score = crit.score;
802 }
803
804#ifdef HAVE_SIGACTION
805 sig_action.sa_sigaction = NULL;
806 sig_action.sa_handler = finish;
807 sigfillset(&sig_action.sa_mask);
808 sig_action.sa_flags = SA_NODEFER | SA_RESTART;
809 sigaction(SIGINT, &sig_action, NULL);
810 sigaction(SIGHUP, &sig_action, NULL);
811 sigaction(SIGTERM, &sig_action, NULL);
812 sigaction(SIGALRM, &sig_action, NULL);
813#else /* HAVE_SIGACTION */
814 signal(SIGINT, finish);
815 signal(SIGHUP, finish);
816 signal(SIGTERM, finish);
817 signal(SIGALRM, finish);
818#endif /* HAVE_SIGACTION */
819 if (debug) {
820 printf("Setting alarm timeout to %u seconds\n", timeout);
821 } 919 }
822 alarm(timeout);
823 920
824 /* make sure we don't wait any longer than necessary */ 921 /* make sure we don't wait any longer than necessary */
825 gettimeofday(&prog_start, &tz); 922 struct timeval prog_start;
826 max_completion_time = ((targets * packets * pkt_interval) + (targets * target_interval)) + (targets * packets * crit.rta) + crit.rta; 923 gettimeofday(&prog_start, NULL);
924
925 time_t max_completion_time =
926 (config.target_interval * config.number_of_targets) +
927 (config.crit.rta * config.number_of_targets * config.number_of_packets) + config.crit.rta;
827 928
828 if (debug) { 929 if (debug) {
829 printf("packets: %u, targets: %u\n" 930 printf("packets: %u, targets: %u\n"
830 "target_interval: %0.3f, pkt_interval %0.3f\n" 931 "target_interval: %0.3f\n"
831 "crit.rta: %0.3f\n" 932 "crit.rta: %0.3f\n"
832 "max_completion_time: %0.3f\n", 933 "max_completion_time: %0.3f\n",
833 packets, targets, (float)target_interval / 1000, (float)pkt_interval / 1000, (float)crit.rta / 1000, 934 config.number_of_packets, config.number_of_targets,
935 (float)config.target_interval / 1000, (float)config.crit.rta / 1000,
834 (float)max_completion_time / 1000); 936 (float)max_completion_time / 1000);
835 } 937 }
836 938
837 if (debug) { 939 if (debug) {
838 if (max_completion_time > (u_int)timeout * 1000000) { 940 if (max_completion_time > (timeout * 1000000)) {
839 printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout); 941 printf("max_completion_time: %ld timeout: %u\n", max_completion_time, timeout);
840 printf("Timeout must be at least %llu\n", max_completion_time / 1000000 + 1); 942 printf("Timeout must be at least %ld\n", (max_completion_time / 1000000) + 1);
841 } 943 }
842 } 944 }
843 945
844 if (debug) { 946 if (debug) {
845 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl); 947 printf("crit = {%ld, %u%%}, warn = {%ld, %u%%}\n", config.crit.rta, config.crit.pl,
846 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n", pkt_interval, target_interval, retry_interval); 948 config.warn.rta, config.warn.pl);
847 printf("icmp_pkt_size: %u timeout: %u\n", icmp_pkt_size, timeout); 949 printf("target_interval: %ld\n", config.target_interval);
950 printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_data_size + ICMP_MINLEN, timeout);
848 } 951 }
849 952
850 if (packets > 20) { 953 if (config.min_hosts_alive < -1) {
851 errno = 0; 954 errno = 0;
852 crash("packets is > 20 (%d)", packets); 955 crash("minimum alive hosts is negative (%i)", config.min_hosts_alive);
853 } 956 }
854 957
855 if (min_hosts_alive < -1) { 958 // Build an index table of all targets
856 errno = 0; 959 ping_target *host = config.targets;
857 crash("minimum alive hosts is negative (%i)", min_hosts_alive); 960 ping_target **table = malloc(sizeof(ping_target *) * config.number_of_targets);
858 }
859
860 host = list;
861 table = malloc(sizeof(struct rta_host *) * targets);
862 if (!table) { 961 if (!table) {
863 crash("main(): malloc failed for host table"); 962 crash("main(): malloc failed for host table");
864 } 963 }
865 964
866 i = 0; 965 unsigned short target_index = 0;
867 while (host) { 966 while (host) {
868 host->id = i * packets; 967 host->id = target_index * config.number_of_packets;
869 table[i] = host; 968 table[target_index] = host;
870 host = host->next; 969 host = host->next;
871 i++; 970 target_index++;
872 } 971 }
873 972
874 run_checks(); 973 time_t target_interval = config.target_interval;
974
975 check_icmp_state program_state = check_icmp_state_init();
976
977 run_checks(config.icmp_data_size, &target_interval, config.sender_id, config.mode,
978 max_completion_time, prog_start, table, config.number_of_packets, sockset,
979 config.number_of_targets, &program_state);
875 980
876 errno = 0; 981 errno = 0;
877 finish(0);
878 982
879 return (0); 983 mp_check overall = mp_check_init();
880} 984 finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit,
985 config.number_of_targets, &program_state, config.hosts, config.number_of_hosts,
986 &overall);
881 987
882static void run_checks(void) { 988 if (sockset.socket4) {
883 u_int i, t; 989 close(sockset.socket4);
884 u_int final_wait, time_passed; 990 }
991 if (sockset.socket6) {
992 close(sockset.socket6);
993 }
994
995 mp_exit(overall);
996}
885 997
998static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval,
999 const uint16_t sender_id, const check_icmp_execution_mode mode,
1000 const time_t max_completion_time, const struct timeval prog_start,
1001 ping_target **table, const unsigned short packets,
1002 const check_icmp_socket_set sockset, const unsigned short number_of_targets,
1003 check_icmp_state *program_state) {
886 /* this loop might actually violate the pkt_interval or target_interval 1004 /* this loop might actually violate the pkt_interval or target_interval
887 * settings, but only if there aren't any packets on the wire which 1005 * settings, but only if there aren't any packets on the wire which
888 * indicates that the target can handle an increased packet rate */ 1006 * indicates that the target can handle an increased packet rate */
889 for (i = 0; i < packets; i++) { 1007 for (unsigned int packet_index = 0; packet_index < packets; packet_index++) {
890 for (t = 0; t < targets; t++) { 1008 for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) {
891 /* don't send useless packets */ 1009 /* don't send useless packets */
892 if (!targets_alive) { 1010 if (!targets_alive(number_of_targets, program_state->targets_down)) {
893 finish(0); 1011 return;
894 } 1012 }
895 if (table[t]->flags & FLAG_LOST_CAUSE) { 1013 if (table[target_index]->flags & FLAG_LOST_CAUSE) {
896 if (debug) { 1014 if (debug) {
897 printf("%s is a lost cause. not sending any more\n", table[t]->name); 1015
1016 char address[INET6_ADDRSTRLEN];
1017 parse_address(&table[target_index]->address, address, sizeof(address));
1018 printf("%s is a lost cause. not sending any more\n", address);
898 } 1019 }
899 continue; 1020 continue;
900 } 1021 }
901 1022
902 /* we're still in the game, so send next packet */ 1023 /* we're still in the game, so send next packet */
903 (void)send_icmp_ping(icmp_sock, table[t]); 1024 (void)send_icmp_ping(sockset, table[target_index], icmp_pkt_size, sender_id,
904 wait_for_reply(icmp_sock, target_interval); 1025 program_state);
1026
1027 /* wrap up if all targets are declared dead */
1028 if (targets_alive(number_of_targets, program_state->targets_down) ||
1029 get_timevaldiff(prog_start, prog_start) < max_completion_time ||
1030 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1031 wait_for_reply(sockset, *target_interval, icmp_pkt_size, target_interval, sender_id,
1032 table, packets, number_of_targets, program_state);
1033 }
1034 }
1035 if (targets_alive(number_of_targets, program_state->targets_down) ||
1036 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1037 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1038 wait_for_reply(sockset, number_of_targets, icmp_pkt_size, target_interval, sender_id,
1039 table, packets, number_of_targets, program_state);
905 } 1040 }
906 wait_for_reply(icmp_sock, pkt_interval * targets);
907 } 1041 }
908 1042
909 if (icmp_pkts_en_route && targets_alive) { 1043 if (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
910 time_passed = get_timevaldiff(NULL, NULL); 1044 program_state->icmp_lost) &&
911 final_wait = max_completion_time - time_passed; 1045 targets_alive(number_of_targets, program_state->targets_down)) {
1046 time_t time_passed = get_timevaldiff_to_now(prog_start);
1047 time_t final_wait = max_completion_time - time_passed;
912 1048
913 if (debug) { 1049 if (debug) {
914 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time); 1050 printf("time_passed: %ld final_wait: %ld max_completion_time: %ld\n", time_passed,
1051 final_wait, max_completion_time);
915 } 1052 }
916 if (time_passed > max_completion_time) { 1053 if (time_passed > max_completion_time) {
917 if (debug) { 1054 if (debug) {
918 printf("Time passed. Finishing up\n"); 1055 printf("Time passed. Finishing up\n");
919 } 1056 }
920 finish(0); 1057 return;
921 } 1058 }
922 1059
923 /* catch the packets that might come in within the timeframe, but 1060 /* catch the packets that might come in within the timeframe, but
924 * haven't yet */ 1061 * haven't yet */
925 if (debug) { 1062 if (debug) {
926 printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000); 1063 printf("Waiting for %ld micro-seconds (%0.3f msecs)\n", final_wait,
1064 (float)final_wait / 1000);
1065 }
1066 if (targets_alive(number_of_targets, program_state->targets_down) ||
1067 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1068 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1069 wait_for_reply(sockset, final_wait, icmp_pkt_size, target_interval, sender_id, table,
1070 packets, number_of_targets, program_state);
927 } 1071 }
928 wait_for_reply(icmp_sock, final_wait);
929 } 1072 }
930} 1073}
931 1074
@@ -939,18 +1082,11 @@ static void run_checks(void) {
939 * both: 1082 * both:
940 * icmp echo reply : the rest 1083 * icmp echo reply : the rest
941 */ 1084 */
942static int wait_for_reply(int sock, u_int t) { 1085static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_interval,
943 int n, hlen; 1086 unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
944 static unsigned char buf[65536]; 1087 ping_target **table, const unsigned short packets,
945 struct sockaddr_storage resp_addr; 1088 const unsigned short number_of_targets, check_icmp_state *program_state) {
946 union ip_hdr *ip;
947 union icmp_packet packet; 1089 union icmp_packet packet;
948 struct rta_host *host;
949 struct icmp_ping_data data;
950 struct timeval wait_start, now;
951 u_int tdiff, i, per_pkt_wait;
952 double jitter_tmp;
953
954 if (!(packet.buf = malloc(icmp_pkt_size))) { 1090 if (!(packet.buf = malloc(icmp_pkt_size))) {
955 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1091 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
956 return -1; /* might be reached if we're in debug mode */ 1092 return -1; /* might be reached if we're in debug mode */
@@ -959,177 +1095,174 @@ static int wait_for_reply(int sock, u_int t) {
959 memset(packet.buf, 0, icmp_pkt_size); 1095 memset(packet.buf, 0, icmp_pkt_size);
960 1096
961 /* if we can't listen or don't have anything to listen to, just return */ 1097 /* if we can't listen or don't have anything to listen to, just return */
962 if (!t || !icmp_pkts_en_route) { 1098 if (!time_interval || !icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
1099 program_state->icmp_lost)) {
963 free(packet.buf); 1100 free(packet.buf);
964 return 0; 1101 return 0;
965 } 1102 }
966 1103
967 gettimeofday(&wait_start, &tz); 1104 // Get current time stamp
1105 struct timeval wait_start;
1106 gettimeofday(&wait_start, NULL);
968 1107
969 i = t; 1108 struct sockaddr_storage resp_addr;
970 per_pkt_wait = t / icmp_pkts_en_route; 1109 time_t per_pkt_wait =
971 while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) { 1110 time_interval / icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
972 t = per_pkt_wait; 1111 program_state->icmp_lost);
973 1112 static unsigned char buf[65536];
974 /* wrap up if all targets are declared dead */ 1113 union ip_hdr *ip_header;
975 if (!targets_alive || get_timevaldiff(&prog_start, NULL) >= max_completion_time || (mode == MODE_HOSTCHECK && targets_down)) { 1114 struct timeval packet_received_timestamp;
976 finish(0); 1115 while (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
977 } 1116 program_state->icmp_lost) &&
1117 get_timevaldiff_to_now(wait_start) < time_interval) {
1118 time_t loop_time_interval = per_pkt_wait;
978 1119
979 /* reap responses until we hit a timeout */ 1120 /* reap responses until we hit a timeout */
980 n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now); 1121 recvfrom_wto_wrapper recv_foo =
981 if (!n) { 1122 recvfrom_wto(sockset, buf, sizeof(buf), (struct sockaddr *)&resp_addr,
1123 &loop_time_interval, &packet_received_timestamp);
1124 if (!recv_foo.received) {
982 if (debug > 1) { 1125 if (debug > 1) {
983 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait); 1126 printf("recvfrom_wto() timed out during a %ld usecs wait\n", per_pkt_wait);
984 } 1127 }
985 continue; /* timeout for this one, so keep trying */ 1128 continue; /* timeout for this one, so keep trying */
986 } 1129 }
987 if (n < 0) { 1130
1131 if (recv_foo.received < 0) {
988 if (debug) { 1132 if (debug) {
989 printf("recvfrom_wto() returned errors\n"); 1133 printf("recvfrom_wto() returned errors\n");
990 } 1134 }
991 free(packet.buf); 1135 free(packet.buf);
992 return n; 1136 return (int)recv_foo.received;
993 } 1137 }
994 1138
995 // FIXME: with ipv6 we don't have an ip header here 1139 if (recv_foo.recv_proto != AF_INET6) {
996 if (address_family != AF_INET6) { 1140 ip_header = (union ip_hdr *)buf;
997 ip = (union ip_hdr *)buf;
998 1141
999 if (debug > 1) { 1142 if (debug > 1) {
1000 char address[INET6_ADDRSTRLEN]; 1143 char address[INET6_ADDRSTRLEN];
1001 parse_address(&resp_addr, address, sizeof(address)); 1144 parse_address(&resp_addr, address, sizeof(address));
1002 printf("received %u bytes from %s\n", address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), address); 1145 printf("received %u bytes from %s\n",
1146 address_family == AF_INET6 ? ntohs(ip_header->ip6.ip6_plen)
1147 : ntohs(ip_header->ip.ip_len),
1148 address);
1003 } 1149 }
1004 } 1150 }
1005 1151
1006 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ 1152 int hlen = (recv_foo.recv_proto == AF_INET6) ? 0 : ip_header->ip.ip_hl << 2;
1007 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 1153
1008 /* alpha headers are decidedly broken. Using an ansi compiler, 1154 if (recv_foo.received < (hlen + ICMP_MINLEN)) {
1009 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
1010 * off the bottom 4 bits */
1011 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
1012 /* #else */
1013 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
1014 /* #endif */
1015
1016 if (n < (hlen + ICMP_MINLEN)) {
1017 char address[INET6_ADDRSTRLEN]; 1155 char address[INET6_ADDRSTRLEN];
1018 parse_address(&resp_addr, address, sizeof(address)); 1156 parse_address(&resp_addr, address, sizeof(address));
1019 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, hlen + icmp_pkt_size, address); 1157 crash("received packet too short for ICMP (%ld bytes, expected %d) from %s\n",
1158 recv_foo.received, hlen + icmp_pkt_size, address);
1020 } 1159 }
1021 /* else if(debug) { */
1022 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
1023 /* hlen, ntohs(ip->ip_len) - hlen, */
1024 /* sizeof(struct ip), icmp_pkt_size); */
1025 /* } */
1026
1027 /* check the response */ 1160 /* check the response */
1028
1029 memcpy(packet.buf, buf + hlen, icmp_pkt_size); 1161 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
1030 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
1031 : sizeof(struct icmp));*/
1032 1162
1033 if ((address_family == PF_INET && (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || 1163 if ((recv_foo.recv_proto == AF_INET &&
1034 ntohs(packet.icp->icmp_seq) >= targets * packets)) || 1164 (ntohs(packet.icp->icmp_id) != sender_id || packet.icp->icmp_type != ICMP_ECHOREPLY ||
1035 (address_family == PF_INET6 && (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || 1165 ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) ||
1036 ntohs(packet.icp6->icmp6_seq) >= targets * packets))) { 1166 (recv_foo.recv_proto == AF_INET6 &&
1167 (ntohs(packet.icp6->icmp6_id) != sender_id ||
1168 packet.icp6->icmp6_type != ICMP6_ECHO_REPLY ||
1169 ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) {
1037 if (debug > 2) { 1170 if (debug > 2) {
1038 printf("not a proper ICMP_ECHOREPLY\n"); 1171 printf("not a proper ICMP_ECHOREPLY\n");
1039 } 1172 }
1040 handle_random_icmp(buf + hlen, &resp_addr); 1173
1174 handle_random_icmp(buf + hlen, &resp_addr, target_interval, sender_id, table, packets,
1175 number_of_targets, program_state);
1176
1041 continue; 1177 continue;
1042 } 1178 }
1043 1179
1044 /* this is indeed a valid response */ 1180 /* this is indeed a valid response */
1045 if (address_family == PF_INET) { 1181 ping_target *target;
1182 struct icmp_ping_data data;
1183 if (address_family == AF_INET) {
1046 memcpy(&data, packet.icp->icmp_data, sizeof(data)); 1184 memcpy(&data, packet.icp->icmp_data, sizeof(data));
1047 if (debug > 2) { 1185 if (debug > 2) {
1048 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), 1186 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data),
1049 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum); 1187 ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq),
1188 packet.icp->icmp_cksum);
1050 } 1189 }
1051 host = table[ntohs(packet.icp->icmp_seq) / packets]; 1190 target = table[ntohs(packet.icp->icmp_seq) / packets];
1052 } else { 1191 } else {
1053 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); 1192 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
1054 if (debug > 2) { 1193 if (debug > 2) {
1055 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id), 1194 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data),
1056 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum); 1195 ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq),
1196 packet.icp6->icmp6_cksum);
1057 } 1197 }
1058 host = table[ntohs(packet.icp6->icmp6_seq) / packets]; 1198 target = table[ntohs(packet.icp6->icmp6_seq) / packets];
1059 } 1199 }
1060 1200
1061 tdiff = get_timevaldiff(&data.stime, &now); 1201 time_t tdiff = get_timevaldiff(data.stime, packet_received_timestamp);
1062 1202
1063 if (host->last_tdiff > 0) { 1203 if (target->last_tdiff > 0) {
1064 /* Calculate jitter */ 1204 /* Calculate jitter */
1065 if (host->last_tdiff > tdiff) { 1205 double jitter_tmp;
1066 jitter_tmp = host->last_tdiff - tdiff; 1206 if (target->last_tdiff > tdiff) {
1207 jitter_tmp = (double)(target->last_tdiff - tdiff);
1067 } else { 1208 } else {
1068 jitter_tmp = tdiff - host->last_tdiff; 1209 jitter_tmp = (double)(tdiff - target->last_tdiff);
1069 } 1210 }
1070 1211
1071 if (host->jitter == 0) { 1212 if (target->jitter == 0) {
1072 host->jitter = jitter_tmp; 1213 target->jitter = jitter_tmp;
1073 host->jitter_max = jitter_tmp; 1214 target->jitter_max = jitter_tmp;
1074 host->jitter_min = jitter_tmp; 1215 target->jitter_min = jitter_tmp;
1075 } else { 1216 } else {
1076 host->jitter += jitter_tmp; 1217 target->jitter += jitter_tmp;
1077 1218
1078 if (jitter_tmp < host->jitter_min) { 1219 if (jitter_tmp < target->jitter_min) {
1079 host->jitter_min = jitter_tmp; 1220 target->jitter_min = jitter_tmp;
1080 } 1221 }
1081 1222
1082 if (jitter_tmp > host->jitter_max) { 1223 if (jitter_tmp > target->jitter_max) {
1083 host->jitter_max = jitter_tmp; 1224 target->jitter_max = jitter_tmp;
1084 } 1225 }
1085 } 1226 }
1086 1227
1087 /* Check if packets in order */ 1228 /* Check if packets in order */
1088 if (host->last_icmp_seq >= packet.icp->icmp_seq) { 1229 if (target->last_icmp_seq >= packet.icp->icmp_seq) {
1089 host->order_status = STATE_CRITICAL; 1230 target->found_out_of_order_packets = true;
1090 } 1231 }
1091 } 1232 }
1092 host->last_tdiff = tdiff; 1233 target->last_tdiff = tdiff;
1093 1234
1094 host->last_icmp_seq = packet.icp->icmp_seq; 1235 target->last_icmp_seq = packet.icp->icmp_seq;
1095 1236
1096 host->time_waited += tdiff; 1237 target->time_waited += tdiff;
1097 host->icmp_recv++; 1238 target->icmp_recv++;
1098 icmp_recv++; 1239 program_state->icmp_recv++;
1099 1240
1100 if (tdiff > (unsigned int)host->rtmax) { 1241 if (tdiff > (unsigned int)target->rtmax) {
1101 host->rtmax = tdiff; 1242 target->rtmax = (double)tdiff;
1102 } 1243 }
1103 1244
1104 if ((host->rtmin == INFINITY) || (tdiff < (unsigned int)host->rtmin)) { 1245 if ((target->rtmin == INFINITY) || (tdiff < (unsigned int)target->rtmin)) {
1105 host->rtmin = tdiff; 1246 target->rtmin = (double)tdiff;
1106 } 1247 }
1107 1248
1108 if (debug) { 1249 if (debug) {
1109 char address[INET6_ADDRSTRLEN]; 1250 char address[INET6_ADDRSTRLEN];
1110 parse_address(&resp_addr, address, sizeof(address)); 1251 parse_address(&resp_addr, address, sizeof(address));
1111 1252
1112 switch (address_family) { 1253 switch (recv_foo.recv_proto) {
1113 case AF_INET: { 1254 case AF_INET: {
1114 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, 1255 printf("%0.3f ms rtt from %s, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
1115 ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); 1256 (float)tdiff / 1000, address, ip_header->ip.ip_ttl,
1257 (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1116 break; 1258 break;
1117 }; 1259 };
1118 case AF_INET6: { 1260 case AF_INET6: {
1119 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ttl, 1261 printf("%0.3f ms rtt from %s, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000,
1120 (float)host->rtmax / 1000, (float)host->rtmin / 1000); 1262 address, (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1121 }; 1263 };
1122 } 1264 }
1123 } 1265 }
1124
1125 /* if we're in hostcheck mode, exit with limited printouts */
1126 if (mode == MODE_HOSTCHECK) {
1127 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
1128 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
1129 host->name, icmp_recv, (float)tdiff / 1000, icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000,
1130 (float)crit.rta / 1000);
1131 exit(STATE_OK);
1132 }
1133 } 1266 }
1134 1267
1135 free(packet.buf); 1268 free(packet.buf);
@@ -1137,38 +1270,28 @@ static int wait_for_reply(int sock, u_int t) {
1137} 1270}
1138 1271
1139/* the ping functions */ 1272/* the ping functions */
1140static int send_icmp_ping(int sock, struct rta_host *host) { 1273static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host,
1141 long int len; 1274 const unsigned short icmp_pkt_size, const uint16_t sender_id,
1142 size_t addrlen; 1275 check_icmp_state *program_state) {
1143 struct icmp_ping_data data; 1276 void *buf = calloc(1, icmp_pkt_size);
1144 struct msghdr hdr;
1145 struct iovec iov;
1146 struct timeval tv;
1147 void *buf = NULL;
1148
1149 if (sock == -1) {
1150 errno = 0;
1151 crash("Attempt to send on bogus socket");
1152 return -1;
1153 }
1154
1155 if (!buf) { 1277 if (!buf) {
1156 if (!(buf = malloc(icmp_pkt_size))) { 1278 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
1157 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1279 return -1; /* might be reached if we're in debug mode */
1158 return -1; /* might be reached if we're in debug mode */
1159 }
1160 } 1280 }
1161 memset(buf, 0, icmp_pkt_size);
1162 1281
1163 if ((gettimeofday(&tv, &tz)) == -1) { 1282 struct timeval current_time;
1283 if ((gettimeofday(&current_time, NULL)) == -1) {
1164 free(buf); 1284 free(buf);
1165 return -1; 1285 return -1;
1166 } 1286 }
1167 1287
1288 struct icmp_ping_data data;
1168 data.ping_id = 10; /* host->icmp.icmp_sent; */ 1289 data.ping_id = 10; /* host->icmp.icmp_sent; */
1169 memcpy(&data.stime, &tv, sizeof(tv)); 1290 memcpy(&data.stime, &current_time, sizeof(current_time));
1170 1291
1171 if (address_family == AF_INET) { 1292 socklen_t addrlen = 0;
1293
1294 if (host->address.ss_family == AF_INET) {
1172 struct icmp *icp = (struct icmp *)buf; 1295 struct icmp *icp = (struct icmp *)buf;
1173 addrlen = sizeof(struct sockaddr_in); 1296 addrlen = sizeof(struct sockaddr_in);
1174 1297
@@ -1177,15 +1300,19 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1177 icp->icmp_type = ICMP_ECHO; 1300 icp->icmp_type = ICMP_ECHO;
1178 icp->icmp_code = 0; 1301 icp->icmp_code = 0;
1179 icp->icmp_cksum = 0; 1302 icp->icmp_cksum = 0;
1180 icp->icmp_id = htons(pid); 1303 icp->icmp_id = htons((uint16_t)sender_id);
1181 icp->icmp_seq = htons(host->id++); 1304 icp->icmp_seq = htons(host->id++);
1182 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); 1305 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size);
1183 1306
1184 if (debug > 2) { 1307 if (debug > 2) {
1185 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), 1308 char address[INET6_ADDRSTRLEN];
1186 ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); 1309 parse_address((&host->address), address, sizeof(address));
1310
1311 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1312 sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum,
1313 address);
1187 } 1314 }
1188 } else { 1315 } else if (host->address.ss_family == AF_INET6) {
1189 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf; 1316 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf;
1190 addrlen = sizeof(struct sockaddr_in6); 1317 addrlen = sizeof(struct sockaddr_in6);
1191 1318
@@ -1194,659 +1321,431 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1194 icp6->icmp6_type = ICMP6_ECHO_REQUEST; 1321 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1195 icp6->icmp6_code = 0; 1322 icp6->icmp6_code = 0;
1196 icp6->icmp6_cksum = 0; 1323 icp6->icmp6_cksum = 0;
1197 icp6->icmp6_id = htons(pid); 1324 icp6->icmp6_id = htons((uint16_t)sender_id);
1198 icp6->icmp6_seq = htons(host->id++); 1325 icp6->icmp6_seq = htons(host->id++);
1199 // let checksum be calculated automatically 1326 // let checksum be calculated automatically
1200 1327
1201 if (debug > 2) { 1328 if (debug > 2) {
1202 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), 1329 char address[INET6_ADDRSTRLEN];
1203 ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name); 1330 parse_address((&host->address), address, sizeof(address));
1331
1332 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to target %s\n",
1333 sizeof(data), ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum,
1334 address);
1204 } 1335 }
1336 } else {
1337 // unknown address family
1338 crash("unknown address family in %s", __func__);
1205 } 1339 }
1206 1340
1341 struct iovec iov;
1207 memset(&iov, 0, sizeof(iov)); 1342 memset(&iov, 0, sizeof(iov));
1208 iov.iov_base = buf; 1343 iov.iov_base = buf;
1209 iov.iov_len = icmp_pkt_size; 1344 iov.iov_len = icmp_pkt_size;
1210 1345
1346 struct msghdr hdr;
1211 memset(&hdr, 0, sizeof(hdr)); 1347 memset(&hdr, 0, sizeof(hdr));
1212 hdr.msg_name = (struct sockaddr *)&host->saddr_in; 1348 hdr.msg_name = (struct sockaddr *)&host->address;
1213 hdr.msg_namelen = addrlen; 1349 hdr.msg_namelen = addrlen;
1214 hdr.msg_iov = &iov; 1350 hdr.msg_iov = &iov;
1215 hdr.msg_iovlen = 1; 1351 hdr.msg_iovlen = 1;
1216 1352
1217 errno = 0; 1353 errno = 0;
1218 1354
1219/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ 1355 long int len;
1356 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1357 if (host->address.ss_family == AF_INET) {
1358#ifdef MSG_CONFIRM
1359 len = sendmsg(sockset.socket4, &hdr, MSG_CONFIRM);
1360#else
1361 len = sendmsg(sockset.socket4, &hdr, 0);
1362#endif
1363 } else if (host->address.ss_family == AF_INET6) {
1220#ifdef MSG_CONFIRM 1364#ifdef MSG_CONFIRM
1221 len = sendmsg(sock, &hdr, MSG_CONFIRM); 1365 len = sendmsg(sockset.socket6, &hdr, MSG_CONFIRM);
1222#else 1366#else
1223 len = sendmsg(sock, &hdr, 0); 1367 len = sendmsg(sockset.socket6, &hdr, 0);
1224#endif 1368#endif
1369 } else {
1370 assert(false);
1371 }
1225 1372
1226 free(buf); 1373 free(buf);
1227 1374
1228 if (len < 0 || (unsigned int)len != icmp_pkt_size) { 1375 if (len < 0 || (unsigned int)len != icmp_pkt_size) {
1229 if (debug) { 1376 if (debug) {
1230 char address[INET6_ADDRSTRLEN]; 1377 char address[INET6_ADDRSTRLEN];
1231 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address)); 1378 parse_address((&host->address), address, sizeof(address));
1232 printf("Failed to send ping to %s: %s\n", address, strerror(errno)); 1379 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1233 } 1380 }
1234 errno = 0; 1381 errno = 0;
1235 return -1; 1382 return -1;
1236 } 1383 }
1237 1384
1238 icmp_sent++; 1385 program_state->icmp_sent++;
1239 host->icmp_sent++; 1386 host->icmp_sent++;
1240 1387
1241 return 0; 1388 return 0;
1242} 1389}
1243 1390
1244static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { 1391static recvfrom_wto_wrapper recvfrom_wto(const check_icmp_socket_set sockset, void *buf,
1245 u_int slen; 1392 const unsigned int len, struct sockaddr *saddr,
1246 int n, ret; 1393 time_t *timeout, struct timeval *received_timestamp) {
1247 struct timeval to, then, now;
1248 fd_set rd, wr;
1249#ifdef HAVE_MSGHDR_MSG_CONTROL 1394#ifdef HAVE_MSGHDR_MSG_CONTROL
1250 char ans_data[4096]; 1395 char ans_data[4096];
1251#endif // HAVE_MSGHDR_MSG_CONTROL 1396#endif // HAVE_MSGHDR_MSG_CONTROL
1252 struct msghdr hdr;
1253 struct iovec iov;
1254#ifdef SO_TIMESTAMP 1397#ifdef SO_TIMESTAMP
1255 struct cmsghdr *chdr; 1398 struct cmsghdr *chdr;
1256#endif 1399#endif
1257 1400
1258 if (!*timo) { 1401 recvfrom_wto_wrapper result = {
1402 .received = 0,
1403 .recv_proto = AF_UNSPEC,
1404 };
1405
1406 if (!*timeout) {
1259 if (debug) { 1407 if (debug) {
1260 printf("*timo is not\n"); 1408 printf("*timeout is not\n");
1261 } 1409 }
1262 return 0; 1410 return result;
1411 }
1412
1413 struct timeval real_timeout;
1414 real_timeout.tv_sec = *timeout / 1000000;
1415 real_timeout.tv_usec = (*timeout - (real_timeout.tv_sec * 1000000));
1416
1417 // Dummy fds for select
1418 fd_set dummy_write_fds;
1419 FD_ZERO(&dummy_write_fds);
1420
1421 // Read fds for select with the socket
1422 fd_set read_fds;
1423 FD_ZERO(&read_fds);
1424
1425 if (sockset.socket4 != -1) {
1426 FD_SET(sockset.socket4, &read_fds);
1427 }
1428 if (sockset.socket6 != -1) {
1429 FD_SET(sockset.socket6, &read_fds);
1263 } 1430 }
1264 1431
1265 to.tv_sec = *timo / 1000000; 1432 int nfds = (sockset.socket4 > sockset.socket6 ? sockset.socket4 : sockset.socket6) + 1;
1266 to.tv_usec = (*timo - (to.tv_sec * 1000000)); 1433
1434 struct timeval then;
1435 gettimeofday(&then, NULL);
1267 1436
1268 FD_ZERO(&rd);
1269 FD_ZERO(&wr);
1270 FD_SET(sock, &rd);
1271 errno = 0; 1437 errno = 0;
1272 gettimeofday(&then, &tz); 1438 int select_return = select(nfds, &read_fds, &dummy_write_fds, NULL, &real_timeout);
1273 n = select(sock + 1, &rd, &wr, NULL, &to); 1439 if (select_return < 0) {
1274 if (n < 0) {
1275 crash("select() in recvfrom_wto"); 1440 crash("select() in recvfrom_wto");
1276 } 1441 }
1277 gettimeofday(&now, &tz);
1278 *timo = get_timevaldiff(&then, &now);
1279 1442
1280 if (!n) { 1443 struct timeval now;
1281 return 0; /* timeout */ 1444 gettimeofday(&now, NULL);
1445 *timeout = get_timevaldiff(then, now);
1446
1447 if (!select_return) {
1448 return result; /* timeout */
1282 } 1449 }
1283 1450
1284 slen = sizeof(struct sockaddr_storage); 1451 unsigned int slen = sizeof(struct sockaddr_storage);
1285 1452
1286 memset(&iov, 0, sizeof(iov)); 1453 struct iovec iov = {
1287 iov.iov_base = buf; 1454 .iov_base = buf,
1288 iov.iov_len = len; 1455 .iov_len = len,
1456 };
1289 1457
1290 memset(&hdr, 0, sizeof(hdr)); 1458 struct msghdr hdr = {
1291 hdr.msg_name = saddr; 1459 .msg_name = saddr,
1292 hdr.msg_namelen = slen; 1460 .msg_namelen = slen,
1293 hdr.msg_iov = &iov; 1461 .msg_iov = &iov,
1294 hdr.msg_iovlen = 1; 1462 .msg_iovlen = 1,
1295#ifdef HAVE_MSGHDR_MSG_CONTROL 1463#ifdef HAVE_MSGHDR_MSG_CONTROL
1296 hdr.msg_control = ans_data; 1464 .msg_control = ans_data,
1297 hdr.msg_controllen = sizeof(ans_data); 1465 .msg_controllen = sizeof(ans_data),
1298#endif 1466#endif
1467 };
1468
1469 ssize_t ret;
1470 if (FD_ISSET(sockset.socket4, &read_fds)) {
1471 ret = recvmsg(sockset.socket4, &hdr, 0);
1472 result.recv_proto = AF_INET;
1473 } else if (FD_ISSET(sockset.socket6, &read_fds)) {
1474 ret = recvmsg(sockset.socket6, &hdr, 0);
1475 result.recv_proto = AF_INET6;
1476 } else {
1477 assert(false);
1478 }
1479
1480 result.received = ret;
1299 1481
1300 ret = recvmsg(sock, &hdr, 0);
1301#ifdef SO_TIMESTAMP 1482#ifdef SO_TIMESTAMP
1302 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { 1483 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) {
1303 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) { 1484 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP &&
1304 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv)); 1485 chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1486 memcpy(received_timestamp, CMSG_DATA(chdr), sizeof(*received_timestamp));
1305 break; 1487 break;
1306 } 1488 }
1307 } 1489 }
1308 1490
1309 if (!chdr) 1491 if (!chdr) {
1492 gettimeofday(received_timestamp, NULL);
1493 }
1494#else
1495 gettimeofday(tv, NULL);
1310#endif // SO_TIMESTAMP 1496#endif // SO_TIMESTAMP
1311 gettimeofday(tv, &tz);
1312 return (ret);
1313}
1314 1497
1315static void finish(int sig) { 1498 return (result);
1316 u_int i = 0; 1499}
1317 unsigned char pl;
1318 double rta;
1319 struct rta_host *host;
1320 const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1321 int hosts_ok = 0;
1322 int hosts_warn = 0;
1323 int this_status;
1324 double R;
1325 1500
1501static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive,
1502 check_icmp_threshold warn, check_icmp_threshold crit,
1503 const unsigned short number_of_targets, check_icmp_state *program_state,
1504 check_icmp_target_container host_list[], unsigned short number_of_hosts,
1505 mp_check overall[static 1]) {
1506 // Deactivate alarm
1326 alarm(0); 1507 alarm(0);
1508
1327 if (debug > 1) { 1509 if (debug > 1) {
1328 printf("finish(%d) called\n", sig); 1510 printf("finish(%d) called\n", sig);
1329 } 1511 }
1330 1512
1331 if (icmp_sock != -1) {
1332 close(icmp_sock);
1333 }
1334 if (udp_sock != -1) {
1335 close(udp_sock);
1336 }
1337 if (tcp_sock != -1) {
1338 close(tcp_sock);
1339 }
1340
1341 if (debug) { 1513 if (debug) {
1342 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", icmp_sent, icmp_recv, icmp_lost); 1514 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", program_state->icmp_sent,
1343 printf("targets: %u targets_alive: %u\n", targets, targets_alive); 1515 program_state->icmp_recv, program_state->icmp_lost);
1516 printf("targets: %u targets_alive: %u\n", number_of_targets,
1517 targets_alive(number_of_targets, program_state->targets_down));
1344 } 1518 }
1345 1519
1346 /* iterate thrice to calculate values, give output, and print perfparse */ 1520 // loop over targets to evaluate each one
1347 status = STATE_OK; 1521 int targets_ok = 0;
1348 host = list; 1522 int targets_warn = 0;
1349 1523 for (unsigned short i = 0; i < number_of_hosts; i++) {
1350 while (host) { 1524 evaluate_host_wrapper host_check = evaluate_host(host_list[i], modes, warn, crit);
1351 this_status = STATE_OK;
1352
1353 if (!host->icmp_recv) {
1354 /* rta 0 is ofcourse not entirely correct, but will still show up
1355 * conspicuously as missing entries in perfparse and cacti */
1356 pl = 100;
1357 rta = 0;
1358 status = STATE_CRITICAL;
1359 /* up the down counter if not already counted */
1360 if (!(host->flags & FLAG_LOST_CAUSE) && targets_alive) {
1361 targets_down++;
1362 }
1363 } else {
1364 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
1365 rta = (double)host->time_waited / host->icmp_recv;
1366 }
1367
1368 if (host->icmp_recv > 1) {
1369 /*
1370 * This algorithm is probably pretty much blindly copied from
1371 * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos
1372 * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good).
1373 * According to some quick research MOS originates from the Audio/Video transport network area.
1374 * Whether it can and should be computed from ICMP data, I can not say.
1375 *
1376 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
1377 *
1378 * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score )
1379 *
1380 * More links:
1381 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
1382 */
1383 host->jitter = (host->jitter / (host->icmp_recv - 1) / 1000);
1384
1385 /*
1386 * Take the average round trip latency (in milliseconds), add
1387 * round trip jitter, but double the impact to latency
1388 * then add 10 for protocol latencies (in milliseconds).
1389 */
1390 host->EffectiveLatency = (rta / 1000) + host->jitter * 2 + 10;
1391
1392 if (host->EffectiveLatency < 160) {
1393 R = 93.2 - (host->EffectiveLatency / 40);
1394 } else {
1395 R = 93.2 - ((host->EffectiveLatency - 120) / 10);
1396 }
1397
1398 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
1399 // loss of 5% will be entered as 5).
1400 R = R - (pl * 2.5);
1401
1402 if (R < 0) {
1403 R = 0;
1404 }
1405
1406 host->score = R;
1407 host->mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
1408 } else {
1409 host->jitter = 0;
1410 host->jitter_min = 0;
1411 host->jitter_max = 0;
1412 host->mos = 0;
1413 }
1414
1415 host->pl = pl;
1416 host->rta = rta;
1417
1418 /* if no new mode selected, use old schema */
1419 if (!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && !order_mode) {
1420 rta_mode = true;
1421 pl_mode = true;
1422 }
1423
1424 /* Check which mode is on and do the warn / Crit stuff */
1425 if (rta_mode) {
1426 if (rta >= crit.rta) {
1427 this_status = STATE_CRITICAL;
1428 status = STATE_CRITICAL;
1429 host->rta_status = STATE_CRITICAL;
1430 } else if (status != STATE_CRITICAL && (rta >= warn.rta)) {
1431 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1432 status = STATE_WARNING;
1433 host->rta_status = STATE_WARNING;
1434 }
1435 }
1436
1437 if (pl_mode) {
1438 if (pl >= crit.pl) {
1439 this_status = STATE_CRITICAL;
1440 status = STATE_CRITICAL;
1441 host->pl_status = STATE_CRITICAL;
1442 } else if (status != STATE_CRITICAL && (pl >= warn.pl)) {
1443 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1444 status = STATE_WARNING;
1445 host->pl_status = STATE_WARNING;
1446 }
1447 }
1448
1449 if (jitter_mode) {
1450 if (host->jitter >= crit.jitter) {
1451 this_status = STATE_CRITICAL;
1452 status = STATE_CRITICAL;
1453 host->jitter_status = STATE_CRITICAL;
1454 } else if (status != STATE_CRITICAL && (host->jitter >= warn.jitter)) {
1455 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1456 status = STATE_WARNING;
1457 host->jitter_status = STATE_WARNING;
1458 }
1459 }
1460
1461 if (mos_mode) {
1462 if (host->mos <= crit.mos) {
1463 this_status = STATE_CRITICAL;
1464 status = STATE_CRITICAL;
1465 host->mos_status = STATE_CRITICAL;
1466 } else if (status != STATE_CRITICAL && (host->mos <= warn.mos)) {
1467 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1468 status = STATE_WARNING;
1469 host->mos_status = STATE_WARNING;
1470 }
1471 }
1472
1473 if (score_mode) {
1474 if (host->score <= crit.score) {
1475 this_status = STATE_CRITICAL;
1476 status = STATE_CRITICAL;
1477 host->score_status = STATE_CRITICAL;
1478 } else if (status != STATE_CRITICAL && (host->score <= warn.score)) {
1479 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1480 status = STATE_WARNING;
1481 host->score_status = STATE_WARNING;
1482 }
1483 }
1484 1525
1485 if (this_status == STATE_WARNING) { 1526 targets_ok += host_check.targets_ok;
1486 hosts_warn++; 1527 targets_warn += host_check.targets_warn;
1487 } else if (this_status == STATE_OK) {
1488 hosts_ok++;
1489 }
1490 1528
1491 host = host->next; 1529 mp_add_subcheck_to_check(overall, host_check.sc_host);
1492 } 1530 }
1493 1531
1494 /* this is inevitable */
1495 if (!targets_alive) {
1496 status = STATE_CRITICAL;
1497 }
1498 if (min_hosts_alive > -1) { 1532 if (min_hosts_alive > -1) {
1499 if (hosts_ok >= min_hosts_alive) { 1533 mp_subcheck sc_min_targets_alive = mp_subcheck_init();
1500 status = STATE_OK; 1534 sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK);
1501 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) { 1535
1502 status = STATE_WARNING; 1536 if (targets_ok >= min_hosts_alive) {
1503 } 1537 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK);
1504 } 1538 xasprintf(&sc_min_targets_alive.output, "%u targets OK of a minimum of %u", targets_ok,
1505 printf("%s - ", status_string[status]); 1539 min_hosts_alive);
1506 1540
1507 host = list; 1541 // Overwrite main state here
1508 while (host) { 1542 overall->evaluation_function = &mp_eval_ok;
1509 if (debug) { 1543 } else if ((targets_ok + targets_warn) >= min_hosts_alive) {
1510 puts(""); 1544 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING);
1511 } 1545 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1512 1546 targets_ok + targets_warn, min_hosts_alive);
1513 if (i) { 1547 overall->evaluation_function = &mp_eval_warning;
1514 if (i < targets) { 1548 } else {
1515 printf(" :: "); 1549 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_CRITICAL);
1516 } else { 1550 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1517 printf("\n"); 1551 targets_ok + targets_warn, min_hosts_alive);
1518 } 1552 overall->evaluation_function = &mp_eval_critical;
1519 }
1520
1521 i++;
1522
1523 if (!host->icmp_recv) {
1524 status = STATE_CRITICAL;
1525 host->rtmin = 0;
1526 host->jitter_min = 0;
1527
1528 if (host->flags & FLAG_LOST_CAUSE) {
1529 char address[INET6_ADDRSTRLEN];
1530 parse_address(&host->error_addr, address, sizeof(address));
1531 printf("%s: %s @ %s. rta nan, lost %d%%", host->name, get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100);
1532 } else { /* not marked as lost cause, so we have no flags for it */
1533 printf("%s: rta nan, lost 100%%", host->name);
1534 }
1535 } else { /* !icmp_recv */
1536 printf("%s", host->name);
1537 /* rta text output */
1538 if (rta_mode) {
1539 if (status == STATE_OK) {
1540 printf(" rta %0.3fms", host->rta / 1000);
1541 } else if (status == STATE_WARNING && host->rta_status == status) {
1542 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)warn.rta / 1000);
1543 } else if (status == STATE_CRITICAL && host->rta_status == status) {
1544 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)crit.rta / 1000);
1545 }
1546 }
1547
1548 /* pl text output */
1549 if (pl_mode) {
1550 if (status == STATE_OK) {
1551 printf(" lost %u%%", host->pl);
1552 } else if (status == STATE_WARNING && host->pl_status == status) {
1553 printf(" lost %u%% > %u%%", host->pl, warn.pl);
1554 } else if (status == STATE_CRITICAL && host->pl_status == status) {
1555 printf(" lost %u%% > %u%%", host->pl, crit.pl);
1556 }
1557 }
1558
1559 /* jitter text output */
1560 if (jitter_mode) {
1561 if (status == STATE_OK) {
1562 printf(" jitter %0.3fms", (float)host->jitter);
1563 } else if (status == STATE_WARNING && host->jitter_status == status) {
1564 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, warn.jitter);
1565 } else if (status == STATE_CRITICAL && host->jitter_status == status) {
1566 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, crit.jitter);
1567 }
1568 }
1569
1570 /* mos text output */
1571 if (mos_mode) {
1572 if (status == STATE_OK) {
1573 printf(" MOS %0.1f", (float)host->mos);
1574 } else if (status == STATE_WARNING && host->mos_status == status) {
1575 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)warn.mos);
1576 } else if (status == STATE_CRITICAL && host->mos_status == status) {
1577 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)crit.mos);
1578 }
1579 }
1580
1581 /* score text output */
1582 if (score_mode) {
1583 if (status == STATE_OK) {
1584 printf(" Score %u", (int)host->score);
1585 } else if (status == STATE_WARNING && host->score_status == status) {
1586 printf(" Score %u < %u", (int)host->score, (int)warn.score);
1587 } else if (status == STATE_CRITICAL && host->score_status == status) {
1588 printf(" Score %u < %u", (int)host->score, (int)crit.score);
1589 }
1590 }
1591
1592 /* order statis text output */
1593 if (order_mode) {
1594 if (status == STATE_OK) {
1595 printf(" Packets in order");
1596 } else if (status == STATE_CRITICAL && host->order_status == status) {
1597 printf(" Packets out of order");
1598 }
1599 }
1600 }
1601 host = host->next;
1602 }
1603
1604 /* iterate once more for pretty perfparse output */
1605 if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) {
1606 printf("|");
1607 }
1608 i = 0;
1609 host = list;
1610 while (host) {
1611 if (debug) {
1612 puts("");
1613 }
1614
1615 if (rta_mode) {
1616 if (host->pl < 100) {
1617 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", (targets > 1) ? host->name : "",
1618 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000, (targets > 1) ? host->name : "",
1619 (float)host->rtmax / 1000, (targets > 1) ? host->name : "",
1620 (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0);
1621 } else {
1622 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "",
1623 (targets > 1) ? host->name : "");
1624 }
1625 }
1626
1627 if (pl_mode) {
1628 printf("%spl=%u%%;%u;%u;0;100 ", (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl);
1629 }
1630
1631 if (jitter_mode) {
1632 if (host->pl < 100) {
1633 printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ",
1634 (targets > 1) ? host->name : "", (float)host->jitter, (float)warn.jitter, (float)crit.jitter,
1635 (targets > 1) ? host->name : "", (float)host->jitter_max / 1000, (targets > 1) ? host->name : "",
1636 (float)host->jitter_min / 1000);
1637 } else {
1638 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets > 1) ? host->name : "",
1639 (targets > 1) ? host->name : "", (targets > 1) ? host->name : "");
1640 }
1641 }
1642
1643 if (mos_mode) {
1644 if (host->pl < 100) {
1645 printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (targets > 1) ? host->name : "", (float)host->mos, (float)warn.mos, (float)crit.mos);
1646 } else {
1647 printf("%smos=U;;;; ", (targets > 1) ? host->name : "");
1648 }
1649 }
1650
1651 if (score_mode) {
1652 if (host->pl < 100) {
1653 printf("%sscore=%u;%u;%u;0;100 ", (targets > 1) ? host->name : "", (int)host->score, (int)warn.score, (int)crit.score);
1654 } else {
1655 printf("%sscore=U;;;; ", (targets > 1) ? host->name : "");
1656 }
1657 } 1553 }
1658 1554
1659 host = host->next; 1555 mp_add_subcheck_to_check(overall, sc_min_targets_alive);
1660 }
1661
1662 if (min_hosts_alive > -1) {
1663 if (hosts_ok >= min_hosts_alive) {
1664 status = STATE_OK;
1665 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) {
1666 status = STATE_WARNING;
1667 }
1668 } 1556 }
1669 1557
1670 /* finish with an empty line */ 1558 /* finish with an empty line */
1671 puts("");
1672 if (debug) { 1559 if (debug) {
1673 printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", targets, targets_alive, hosts_ok, 1560 printf(
1674 hosts_warn, min_hosts_alive); 1561 "targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
1562 number_of_targets, targets_alive(number_of_targets, program_state->targets_down),
1563 targets_ok, targets_warn, min_hosts_alive);
1675 } 1564 }
1676
1677 exit(status);
1678} 1565}
1679 1566
1680static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { 1567static time_t get_timevaldiff(const struct timeval earlier, const struct timeval later) {
1681 u_int ret;
1682 struct timeval now;
1683
1684 if (!later) {
1685 gettimeofday(&now, &tz);
1686 later = &now;
1687 }
1688 if (!early) {
1689 early = &prog_start;
1690 }
1691
1692 /* if early > later we return 0 so as to indicate a timeout */ 1568 /* if early > later we return 0 so as to indicate a timeout */
1693 if (early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) { 1569 if (earlier.tv_sec > later.tv_sec ||
1570 (earlier.tv_sec == later.tv_sec && earlier.tv_usec > later.tv_usec)) {
1694 return 0; 1571 return 0;
1695 } 1572 }
1696 ret = (later->tv_sec - early->tv_sec) * 1000000; 1573
1697 ret += later->tv_usec - early->tv_usec; 1574 time_t ret = (later.tv_sec - earlier.tv_sec) * 1000000;
1575 ret += later.tv_usec - earlier.tv_usec;
1698 1576
1699 return ret; 1577 return ret;
1700} 1578}
1701 1579
1702static int add_target_ip(char *arg, struct sockaddr_storage *in) { 1580static time_t get_timevaldiff_to_now(struct timeval earlier) {
1703 struct rta_host *host; 1581 struct timeval now;
1704 struct sockaddr_in *sin, *host_sin; 1582 gettimeofday(&now, NULL);
1705 struct sockaddr_in6 *sin6, *host_sin6; 1583
1584 return get_timevaldiff(earlier, now);
1585}
1586
1587static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) {
1588 assert((address.ss_family == AF_INET) || (address.ss_family == AF_INET6));
1706 1589
1707 if (address_family == AF_INET) { 1590 if (debug) {
1708 sin = (struct sockaddr_in *)in; 1591 char straddr[INET6_ADDRSTRLEN];
1592 parse_address((&address), straddr, sizeof(straddr));
1593 printf("add_target_ip called with: %s\n", straddr);
1594 }
1595 struct sockaddr_in *sin;
1596 struct sockaddr_in6 *sin6;
1597 if (address.ss_family == AF_INET) {
1598 sin = (struct sockaddr_in *)&address;
1599 } else if (address.ss_family == AF_INET6) {
1600 sin6 = (struct sockaddr_in6 *)&address;
1709 } else { 1601 } else {
1710 sin6 = (struct sockaddr_in6 *)in; 1602 assert(false);
1711 } 1603 }
1712 1604
1605 add_target_ip_wrapper result = {
1606 .error_code = OK,
1607 .target = NULL,
1608 };
1609
1713 /* disregard obviously stupid addresses 1610 /* disregard obviously stupid addresses
1714 * (I didn't find an ipv6 equivalent to INADDR_NONE) */ 1611 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1715 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || 1612 if (((address.ss_family == AF_INET &&
1716 (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { 1613 (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) ||
1717 return -1; 1614 (address.ss_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1615 result.error_code = ERROR;
1616 return result;
1718 } 1617 }
1719 1618
1720 /* no point in adding two identical IP's, so don't. ;) */ 1619 // get string representation of address
1721 host = list; 1620 char straddr[INET6_ADDRSTRLEN];
1722 while (host) { 1621 parse_address((&address), straddr, sizeof(straddr));
1723 host_sin = (struct sockaddr_in *)&host->saddr_in;
1724 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1725
1726 if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) ||
1727 (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) {
1728 if (debug) {
1729 printf("Identical IP already exists. Not adding %s\n", arg);
1730 }
1731 return -1;
1732 }
1733 host = host->next;
1734 }
1735 1622
1736 /* add the fresh ip */ 1623 /* add the fresh ip */
1737 host = (struct rta_host *)malloc(sizeof(struct rta_host)); 1624 ping_target *target = (ping_target *)calloc(1, sizeof(ping_target));
1738 if (!host) { 1625 if (!target) {
1739 char straddr[INET6_ADDRSTRLEN]; 1626 crash("add_target_ip(%s): malloc(%lu) failed", straddr, sizeof(ping_target));
1740 parse_address((struct sockaddr_storage *)&in, straddr, sizeof(straddr));
1741 crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(struct rta_host));
1742 } 1627 }
1743 memset(host, 0, sizeof(struct rta_host));
1744 1628
1745 /* set the values. use calling name for output */ 1629 ping_target_create_wrapper target_wrapper = ping_target_create(address);
1746 host->name = strdup(arg);
1747 1630
1748 /* fill out the sockaddr_storage struct */ 1631 if (target_wrapper.errorcode == OK) {
1749 if (address_family == AF_INET) { 1632 *target = target_wrapper.host;
1750 host_sin = (struct sockaddr_in *)&host->saddr_in; 1633 result.target = target;
1751 host_sin->sin_family = AF_INET;
1752 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1753 } else { 1634 } else {
1754 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; 1635 result.error_code = target_wrapper.errorcode;
1755 host_sin6->sin6_family = AF_INET6;
1756 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1757 }
1758
1759 /* fill out the sockaddr_in struct */
1760 host->rtmin = INFINITY;
1761 host->rtmax = 0;
1762 host->jitter = 0;
1763 host->jitter_max = 0;
1764 host->jitter_min = INFINITY;
1765 host->last_tdiff = 0;
1766 host->order_status = STATE_OK;
1767 host->last_icmp_seq = 0;
1768 host->rta_status = 0;
1769 host->pl_status = 0;
1770 host->jitter_status = 0;
1771 host->mos_status = 0;
1772 host->score_status = 0;
1773 host->pl_status = 0;
1774
1775 if (!list) {
1776 list = cursor = host;
1777 } else {
1778 cursor->next = host;
1779 } 1636 }
1780 1637
1781 cursor = host; 1638 return result;
1782 targets++;
1783
1784 return 0;
1785} 1639}
1786 1640
1787/* wrapper for add_target_ip */ 1641/* wrapper for add_target_ip */
1788static int add_target(char *arg) { 1642static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode,
1789 int error, result = -1; 1643 sa_family_t enforced_proto) {
1790 struct sockaddr_storage ip; 1644 if (debug > 0) {
1791 struct addrinfo hints, *res, *p; 1645 printf("add_target called with argument %s\n", arg);
1792 struct sockaddr_in *sin; 1646 }
1793 struct sockaddr_in6 *sin6; 1647
1794 1648 struct sockaddr_storage address_storage = {};
1795 switch (address_family) { 1649 struct sockaddr_in *sin = NULL;
1796 case -1: 1650 struct sockaddr_in6 *sin6 = NULL;
1797 /* -4 and -6 are not specified on cmdline */ 1651 int error_code = -1;
1798 address_family = AF_INET; 1652
1799 sin = (struct sockaddr_in *)&ip; 1653 switch (enforced_proto) {
1800 result = inet_pton(address_family, arg, &sin->sin_addr); 1654 case AF_UNSPEC:
1801#ifdef USE_IPV6 1655 /*
1802 if (result != 1) { 1656 * no enforced protocol family
1803 address_family = AF_INET6; 1657 * try to parse the address with each one
1804 sin6 = (struct sockaddr_in6 *)&ip; 1658 */
1805 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1659 sin = (struct sockaddr_in *)&address_storage;
1806 } 1660 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1807#endif 1661 address_storage.ss_family = AF_INET;
1808 /* If we don't find any valid addresses, we still don't know the address_family */ 1662
1809 if (result != 1) { 1663 if (error_code != 1) {
1810 address_family = -1; 1664 sin6 = (struct sockaddr_in6 *)&address_storage;
1665 error_code = inet_pton(AF_INET6, arg, &sin6->sin6_addr);
1666 address_storage.ss_family = AF_INET6;
1811 } 1667 }
1812 break; 1668 break;
1813 case AF_INET: 1669 case AF_INET:
1814 sin = (struct sockaddr_in *)&ip; 1670 sin = (struct sockaddr_in *)&address_storage;
1815 result = inet_pton(address_family, arg, &sin->sin_addr); 1671 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1672 address_storage.ss_family = AF_INET;
1816 break; 1673 break;
1817 case AF_INET6: 1674 case AF_INET6:
1818 sin6 = (struct sockaddr_in6 *)&ip; 1675 sin6 = (struct sockaddr_in6 *)&address_storage;
1819 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1676 error_code = inet_pton(AF_INET, arg, &sin6->sin6_addr);
1677 address_storage.ss_family = AF_INET6;
1820 break; 1678 break;
1821 default: 1679 default:
1822 crash("Address family not supported"); 1680 crash("Address family not supported");
1823 } 1681 }
1824 1682
1825 /* don't resolve if we don't have to */ 1683 add_target_wrapper result = {
1826 if (result == 1) { 1684 .error_code = OK,
1685 .targets = NULL,
1686 .has_v4 = false,
1687 .has_v6 = false,
1688 };
1689
1690 // if error_code == 1 the address was a valid address parsed above
1691 if (error_code == 1) {
1827 /* don't add all ip's if we were given a specific one */ 1692 /* don't add all ip's if we were given a specific one */
1828 return add_target_ip(arg, &ip); 1693 add_target_ip_wrapper targeted = add_target_ip(address_storage);
1829 } else { 1694
1830 errno = 0; 1695 if (targeted.error_code != OK) {
1831 memset(&hints, 0, sizeof(hints)); 1696 result.error_code = ERROR;
1832 if (address_family == -1) { 1697 return result;
1833 hints.ai_family = AF_UNSPEC;
1834 } else {
1835 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1836 } 1698 }
1837 hints.ai_socktype = SOCK_RAW; 1699
1838 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { 1700 if (targeted.target->address.ss_family == AF_INET) {
1839 errno = 0; 1701 result.has_v4 = true;
1840 crash("Failed to resolve %s: %s", arg, gai_strerror(error)); 1702 } else if (targeted.target->address.ss_family == AF_INET6) {
1841 return -1; 1703 result.has_v6 = true;
1704 } else {
1705 assert(false);
1842 } 1706 }
1843 address_family = res->ai_family; 1707 result.targets = targeted.target;
1708 result.number_of_targets = 1;
1709 return result;
1710 }
1711
1712 struct addrinfo hints = {};
1713 errno = 0;
1714 hints.ai_family = enforced_proto;
1715 hints.ai_socktype = SOCK_RAW;
1716
1717 int error;
1718 struct addrinfo *res;
1719 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1720 errno = 0;
1721 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1722 result.error_code = ERROR;
1723 return result;
1844 } 1724 }
1845 1725
1846 /* possibly add all the IP's as targets */ 1726 /* possibly add all the IP's as targets */
1847 for (p = res; p != NULL; p = p->ai_next) { 1727 for (struct addrinfo *address = res; address != NULL; address = address->ai_next) {
1848 memcpy(&ip, p->ai_addr, p->ai_addrlen); 1728 struct sockaddr_storage temporary_ip_address;
1849 add_target_ip(arg, &ip); 1729 memcpy(&temporary_ip_address, address->ai_addr, address->ai_addrlen);
1730
1731 add_target_ip_wrapper tmp = add_target_ip(temporary_ip_address);
1732
1733 if (tmp.error_code != OK) {
1734 // No proper error handling
1735 // What to do?
1736 } else {
1737 if (result.targets == NULL) {
1738 result.targets = tmp.target;
1739 result.number_of_targets = 1;
1740 } else {
1741 result.number_of_targets += ping_target_list_append(result.targets, tmp.target);
1742 }
1743 if (address->ai_family == AF_INET) {
1744 result.has_v4 = true;
1745 } else if (address->ai_family == AF_INET6) {
1746 result.has_v6 = true;
1747 }
1748 }
1850 1749
1851 /* this is silly, but it works */ 1750 /* this is silly, but it works */
1852 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1751 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1855,18 +1754,20 @@ static int add_target(char *arg) {
1855 } 1754 }
1856 continue; 1755 continue;
1857 } 1756 }
1757
1758 // Abort after first hit if not in of the modes above
1858 break; 1759 break;
1859 } 1760 }
1860 freeaddrinfo(res); 1761 freeaddrinfo(res);
1861 1762
1862 return 0; 1763 return result;
1863} 1764}
1864 1765
1865static void set_source_ip(char *arg) { 1766static void set_source_ip(char *arg, const int icmp_sock, sa_family_t addr_family) {
1866 struct sockaddr_in src; 1767 struct sockaddr_in src;
1867 1768
1868 memset(&src, 0, sizeof(src)); 1769 memset(&src, 0, sizeof(src));
1869 src.sin_family = address_family; 1770 src.sin_family = addr_family;
1870 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) { 1771 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) {
1871 src.sin_addr.s_addr = get_ip_address(arg); 1772 src.sin_addr.s_addr = get_ip_address(arg);
1872 } 1773 }
@@ -1878,8 +1779,8 @@ static void set_source_ip(char *arg) {
1878/* TODO: Move this to netutils.c and also change check_dhcp to use that. */ 1779/* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1879static in_addr_t get_ip_address(const char *ifname) { 1780static in_addr_t get_ip_address(const char *ifname) {
1880 // TODO: Rewrite this so the function return an error and we exit somewhere else 1781 // TODO: Rewrite this so the function return an error and we exit somewhere else
1881 struct sockaddr_in ip; 1782 struct sockaddr_in ip_address;
1882 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy 1783 ip_address.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1883#if defined(SIOCGIFADDR) 1784#if defined(SIOCGIFADDR)
1884 struct ifreq ifr; 1785 struct ifreq ifr;
1885 1786
@@ -1897,7 +1798,7 @@ static in_addr_t get_ip_address(const char *ifname) {
1897 errno = 0; 1798 errno = 0;
1898 crash("Cannot get interface IP address on this platform."); 1799 crash("Cannot get interface IP address on this platform.");
1899#endif 1800#endif
1900 return ip.sin_addr.s_addr; 1801 return ip_address.sin_addr.s_addr;
1901} 1802}
1902 1803
1903/* 1804/*
@@ -1906,103 +1807,127 @@ static in_addr_t get_ip_address(const char *ifname) {
1906 * s = seconds 1807 * s = seconds
1907 * return value is in microseconds 1808 * return value is in microseconds
1908 */ 1809 */
1909static u_int get_timevar(const char *str) { 1810static get_timevar_wrapper get_timevar(const char *str) {
1910 char p, u, *ptr; 1811 get_timevar_wrapper result = {
1911 size_t len; 1812 .error_code = OK,
1912 u_int i, d; /* integer and decimal, respectively */ 1813 .time_range = 0,
1913 u_int factor = 1000; /* default to milliseconds */ 1814 };
1914 1815
1915 if (!str) { 1816 if (!str) {
1916 return 0; 1817 result.error_code = ERROR;
1818 return result;
1917 } 1819 }
1918 len = strlen(str); 1820
1821 size_t len = strlen(str);
1919 if (!len) { 1822 if (!len) {
1920 return 0; 1823 result.error_code = ERROR;
1824 return result;
1921 } 1825 }
1922 1826
1923 /* unit might be given as ms|m (millisec), 1827 /* unit might be given as ms|m (millisec),
1924 * us|u (microsec) or just plain s, for seconds */ 1828 * us|u (microsec) or just plain s, for seconds */
1925 p = '\0'; 1829 char tmp = '\0';
1926 u = str[len - 1]; 1830 char unit = str[len - 1];
1927 if (len >= 2 && !isdigit((int)str[len - 2])) { 1831 if (len >= 2 && !isdigit((int)str[len - 2])) {
1928 p = str[len - 2]; 1832 tmp = str[len - 2];
1929 } 1833 }
1930 if (p && u == 's') { 1834
1931 u = p; 1835 if (tmp && unit == 's') {
1932 } else if (!p) { 1836 unit = tmp;
1933 p = u; 1837 } else if (!tmp) {
1838 tmp = unit;
1934 } 1839 }
1840
1935 if (debug > 2) { 1841 if (debug > 2) {
1936 printf("evaluating %s, u: %c, p: %c\n", str, u, p); 1842 printf("evaluating %s, u: %c, p: %c\n", str, unit, tmp);
1937 } 1843 }
1938 1844
1939 if (u == 'u') { 1845 unsigned int factor = 1000; /* default to milliseconds */
1846 if (unit == 'u') {
1940 factor = 1; /* microseconds */ 1847 factor = 1; /* microseconds */
1941 } else if (u == 'm') { 1848 } else if (unit == 'm') {
1942 factor = 1000; /* milliseconds */ 1849 factor = 1000; /* milliseconds */
1943 } else if (u == 's') { 1850 } else if (unit == 's') {
1944 factor = 1000000; /* seconds */ 1851 factor = 1000000; /* seconds */
1945 } 1852 }
1853
1946 if (debug > 2) { 1854 if (debug > 2) {
1947 printf("factor is %u\n", factor); 1855 printf("factor is %u\n", factor);
1948 } 1856 }
1949 1857
1950 i = strtoul(str, &ptr, 0); 1858 char *ptr;
1859 unsigned long pre_radix;
1860 pre_radix = strtoul(str, &ptr, 0);
1951 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { 1861 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) {
1952 return i * factor; 1862 result.time_range = (unsigned int)(pre_radix * factor);
1863 return result;
1953 } 1864 }
1954 1865
1955 /* time specified in usecs can't have decimal points, so ignore them */ 1866 /* time specified in usecs can't have decimal points, so ignore them */
1956 if (factor == 1) { 1867 if (factor == 1) {
1957 return i; 1868 result.time_range = (unsigned int)pre_radix;
1869 return result;
1958 } 1870 }
1959 1871
1960 d = strtoul(ptr + 1, NULL, 0); 1872 /* integer and decimal, respectively */
1873 unsigned int post_radix = (unsigned int)strtoul(ptr + 1, NULL, 0);
1961 1874
1962 /* d is decimal, so get rid of excess digits */ 1875 /* d is decimal, so get rid of excess digits */
1963 while (d >= factor) { 1876 while (post_radix >= factor) {
1964 d /= 10; 1877 post_radix /= 10;
1965 } 1878 }
1966 1879
1967 /* the last parenthesis avoids floating point exceptions. */ 1880 /* the last parenthesis avoids floating point exceptions. */
1968 return ((i * factor) + (d * (factor / 10))); 1881 result.time_range = (unsigned int)((pre_radix * factor) + (post_radix * (factor / 10)));
1882 return result;
1969} 1883}
1970 1884
1971/* not too good at checking errors, but it'll do (main() should barfe on -1) */ 1885static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold) {
1972static int get_threshold(char *str, threshold *th) { 1886 get_threshold_wrapper result = {
1973 char *p = NULL, i = 0; 1887 .errorcode = OK,
1888 .threshold = threshold,
1889 };
1974 1890
1975 if (!str || !strlen(str) || !th) { 1891 if (!str || !strlen(str)) {
1976 return -1; 1892 result.errorcode = ERROR;
1893 return result;
1977 } 1894 }
1978 1895
1979 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ 1896 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1980 p = &str[strlen(str) - 1]; 1897 bool is_at_last_char = false;
1981 while (p != &str[1]) { 1898 char *tmp = &str[strlen(str) - 1];
1982 if (*p == '%') { 1899 while (tmp != &str[1]) {
1983 *p = '\0'; 1900 if (*tmp == '%') {
1984 } else if (*p == ',' && i) { 1901 *tmp = '\0';
1985 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1902 } else if (*tmp == ',' && is_at_last_char) {
1986 th->pl = (unsigned char)strtoul(p + 1, NULL, 0); 1903 *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */
1904 result.threshold.pl = (unsigned char)strtoul(tmp + 1, NULL, 0);
1987 break; 1905 break;
1988 } 1906 }
1989 i = 1; 1907 is_at_last_char = true;
1990 p--; 1908 tmp--;
1991 } 1909 }
1992 th->rta = get_timevar(str);
1993 1910
1994 if (!th->rta) { 1911 get_timevar_wrapper parsed_time = get_timevar(str);
1995 return -1; 1912
1913 if (parsed_time.error_code == OK) {
1914 result.threshold.rta = parsed_time.time_range;
1915 } else {
1916 if (debug > 1) {
1917 printf("%s: failed to parse rta threshold\n", __FUNCTION__);
1918 }
1919 result.errorcode = ERROR;
1920 return result;
1996 } 1921 }
1997 1922
1998 if (th->rta > MAXTTL * 1000000) { 1923 if (result.threshold.rta > MAXTTL * 1000000) {
1999 th->rta = MAXTTL * 1000000; 1924 result.threshold.rta = MAXTTL * 1000000;
2000 } 1925 }
2001 if (th->pl > 100) { 1926 if (result.threshold.pl > 100) {
2002 th->pl = 100; 1927 result.threshold.pl = 100;
2003 } 1928 }
2004 1929
2005 return 0; 1930 return result;
2006} 1931}
2007 1932
2008/* 1933/*
@@ -2013,184 +1938,537 @@ static int get_threshold(char *str, threshold *th) {
2013 * @param[in] length strlen(str) 1938 * @param[in] length strlen(str)
2014 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned 1939 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned
2015 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned 1940 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned
2016 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively) 1941 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score
1942 * (exclusively)
2017 */ 1943 */
2018static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) { 1944static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
2019 if (!str || !length || !warn || !crit) { 1945 check_icmp_threshold crit, threshold_mode mode) {
2020 return false; 1946 get_threshold2_wrapper result = {
1947 .errorcode = OK,
1948 .warn = warn,
1949 .crit = crit,
1950 };
1951
1952 if (!str || !length) {
1953 result.errorcode = ERROR;
1954 return result;
2021 } 1955 }
2022 1956
2023 // p points to the last char in str 1957 // p points to the last char in str
2024 char *p = &str[length - 1]; 1958 char *work_pointer = &str[length - 1];
2025 1959
2026 // first_iteration is bof-stop on stupid libc's 1960 // first_iteration is bof-stop on stupid libc's
2027 bool first_iteration = true; 1961 bool first_iteration = true;
2028 1962
2029 while (p != &str[0]) { 1963 while (work_pointer != &str[0]) {
2030 if ((*p == 'm') || (*p == '%')) { 1964 if ((*work_pointer == 'm') || (*work_pointer == '%')) {
2031 *p = '\0'; 1965 *work_pointer = '\0';
2032 } else if (*p == ',' && !first_iteration) { 1966 } else if (*work_pointer == ',' && !first_iteration) {
2033 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1967 *work_pointer = '\0'; /* reset it so get_timevar(str) works nicely later */
2034 1968
2035 char *start_of_value = p + 1; 1969 char *start_of_value = work_pointer + 1;
2036 1970
2037 if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) { 1971 parse_threshold2_helper_wrapper tmp =
2038 return false; 1972 parse_threshold2_helper(start_of_value, strlen(start_of_value), result.crit, mode);
1973 if (tmp.errorcode != OK) {
1974 result.errorcode = ERROR;
1975 return result;
2039 } 1976 }
1977 result.crit = tmp.result;
2040 } 1978 }
2041 first_iteration = false; 1979 first_iteration = false;
2042 p--; 1980 work_pointer--;
2043 } 1981 }
2044 1982
2045 return parse_threshold2_helper(p, strlen(p), warn, mode); 1983 parse_threshold2_helper_wrapper tmp =
1984 parse_threshold2_helper(work_pointer, strlen(work_pointer), result.warn, mode);
1985 if (tmp.errorcode != OK) {
1986 result.errorcode = ERROR;
1987 } else {
1988 result.warn = tmp.result;
1989 }
1990 return result;
2046} 1991}
2047 1992
2048static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) { 1993static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
1994 size_t length,
1995 check_icmp_threshold thr,
1996 threshold_mode mode) {
2049 char *resultChecker = {0}; 1997 char *resultChecker = {0};
1998 parse_threshold2_helper_wrapper result = {
1999 .result = thr,
2000 .errorcode = OK,
2001 };
2050 2002
2051 switch (mode) { 2003 switch (mode) {
2052 case const_rta_mode: 2004 case const_rta_mode:
2053 thr->rta = strtod(s, &resultChecker) * 1000; 2005 result.result.rta = (unsigned int)(strtod(threshold_string, &resultChecker) * 1000);
2054 break; 2006 break;
2055 case const_packet_loss_mode: 2007 case const_packet_loss_mode:
2056 thr->pl = (unsigned char)strtoul(s, &resultChecker, 0); 2008 result.result.pl = (unsigned char)strtoul(threshold_string, &resultChecker, 0);
2057 break; 2009 break;
2058 case const_jitter_mode: 2010 case const_jitter_mode:
2059 thr->jitter = strtod(s, &resultChecker); 2011 result.result.jitter = strtod(threshold_string, &resultChecker);
2060
2061 break; 2012 break;
2062 case const_mos_mode: 2013 case const_mos_mode:
2063 thr->mos = strtod(s, &resultChecker); 2014 result.result.mos = strtod(threshold_string, &resultChecker);
2064 break; 2015 break;
2065 case const_score_mode: 2016 case const_score_mode:
2066 thr->score = strtod(s, &resultChecker); 2017 result.result.score = strtod(threshold_string, &resultChecker);
2067 break; 2018 break;
2068 } 2019 }
2069 2020
2070 if (resultChecker == s) { 2021 if (resultChecker == threshold_string) {
2071 // Failed to parse 2022 // Failed to parse
2072 return false; 2023 result.errorcode = ERROR;
2024 return result;
2073 } 2025 }
2074 2026
2075 if (resultChecker != (s + length)) { 2027 if (resultChecker != (threshold_string + length)) {
2076 // Trailing symbols 2028 // Trailing symbols
2077 return false; 2029 result.errorcode = ERROR;
2078 } 2030 }
2079 2031
2080 return true; 2032 return result;
2081} 2033}
2082 2034
2083unsigned short icmp_checksum(uint16_t *p, size_t n) { 2035unsigned short icmp_checksum(uint16_t *packet, size_t packet_size) {
2084 unsigned short cksum;
2085 long sum = 0; 2036 long sum = 0;
2086 2037
2087 /* sizeof(uint16_t) == 2 */ 2038 /* sizeof(uint16_t) == 2 */
2088 while (n >= 2) { 2039 while (packet_size >= 2) {
2089 sum += *(p++); 2040 sum += *(packet++);
2090 n -= 2; 2041 packet_size -= 2;
2091 } 2042 }
2092 2043
2093 /* mop up the occasional odd byte */ 2044 /* mop up the occasional odd byte */
2094 if (n == 1) { 2045 if (packet_size == 1) {
2095 sum += *((uint8_t *)p - 1); 2046 sum += *((uint8_t *)packet - 1);
2096 } 2047 }
2097 2048
2098 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 2049 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
2099 sum += (sum >> 16); /* add carry */ 2050 sum += (sum >> 16); /* add carry */
2100 cksum = ~sum; /* ones-complement, trunc to 16 bits */ 2051 unsigned short cksum;
2052 cksum = (unsigned short)~sum; /* ones-complement, trunc to 16 bits */
2101 2053
2102 return cksum; 2054 return cksum;
2103} 2055}
2104 2056
2105void print_help(void) { 2057void print_help(void) {
2106 /*print_revision (progname);*/ /* FIXME: Why? */ 2058 // print_revision (progname); /* FIXME: Why? */
2107 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n"); 2059 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
2108 2060
2109 printf(COPYRIGHT, copyright, email); 2061 printf(COPYRIGHT, copyright, email);
2110 2062
2111 printf("\n\n");
2112
2113 print_usage(); 2063 print_usage();
2114 2064
2115 printf(UT_HELP_VRSN); 2065 printf(UT_HELP_VRSN);
2116 printf(UT_EXTRA_OPTS); 2066 printf(UT_EXTRA_OPTS);
2117 2067
2118 printf(" %s\n", "-H"); 2068 printf(" -H, --Host=HOST\n");
2119 printf(" %s\n", _("specify a target")); 2069 printf(" %s\n",
2120 printf(" %s\n", "[-4|-6]"); 2070 _("specify a target, might be one of: resolveable name | IPv6 address | IPv4 address\n"
2121 printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets")); 2071 " (required, can be given multiple times)"));
2122 printf(" %s\n", "-w"); 2072 printf(" %s\n", "[-4|-6], [--ipv4-only|--ipv6-only]");
2123 printf(" %s", _("warning threshold (currently ")); 2073 printf(" %s\n", _("Use IPv4 or IPv6 only to communicate with the targets"));
2124 printf("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 2074 printf(" %s\n", "-w, --warning=WARN_VALUE");
2125 printf(" %s\n", "-c"); 2075 printf(" %s", _("warning threshold (default "));
2126 printf(" %s", _("critical threshold (currently ")); 2076 printf("%0.3fms,%u%%)\n", (float)DEFAULT_WARN_RTA / 1000, DEFAULT_WARN_PL);
2127 printf("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl); 2077 printf(" %s\n", "-c, --critical=CRIT_VALUE");
2128 2078 printf(" %s", _("critical threshold (default "));
2129 printf(" %s\n", "-R"); 2079 printf("%0.3fms,%u%%)\n", (float)DEFAULT_CRIT_RTA / 1000, DEFAULT_CRIT_PL);
2130 printf(" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms")); 2080
2131 printf(" %s\n", "-P"); 2081 printf(" %s\n", "-R, --rta-mode-thresholds=RTA_THRESHOLDS");
2082 printf(" %s\n",
2083 _("RTA (round trip average) mode warning,critical, ex. 100ms,200ms unit in ms"));
2084 printf(" %s\n", "-P, --packet-loss-mode-thresholds=PACKET_LOSS_THRESHOLD");
2132 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %")); 2085 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2133 printf(" %s\n", "-J"); 2086 printf(" %s\n", "-J, --jitter-mode-thresholds=JITTER_MODE_THRESHOLD");
2134 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms ")); 2087 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms "));
2135 printf(" %s\n", "-M"); 2088 printf(" %s\n", "-M, --mos-mode-thresholds=MOS_MODE_THRESHOLD");
2136 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0")); 2089 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0"));
2137 printf(" %s\n", "-S"); 2090 printf(" %s\n", "-S, --score-mode-thresholds=SCORE_MODE_THRESHOLD");
2138 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 ")); 2091 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 "));
2139 printf(" %s\n", "-O"); 2092 printf(" %s\n", "-O, --out-of-order-packets");
2140 printf(" %s\n", _("detect out of order ICMP packts ")); 2093 printf(
2141 printf(" %s\n", "-H"); 2094 " %s\n",
2142 printf(" %s\n", _("specify a target")); 2095 _("detect out of order ICMP packets, if such packets are found, the result is CRITICAL"));
2143 printf(" %s\n", "-s"); 2096 printf(" %s\n", "[-n|-p], --number-of-packets=NUMBER_OF_PACKETS");
2144 printf(" %s\n", _("specify a source IP address or device name")); 2097 printf(" %s", _("number of packets to send (default "));
2145 printf(" %s\n", "-n"); 2098 printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS);
2146 printf(" %s", _("number of packets to send (currently ")); 2099
2147 printf("%u)\n", packets);
2148 printf(" %s\n", "-p");
2149 printf(" %s", _("number of packets to send (currently "));
2150 printf("%u)\n", packets);
2151 printf(" %s\n", "-i"); 2100 printf(" %s\n", "-i");
2152 printf(" %s", _("max packet interval (currently ")); 2101 printf(" %s", _("[DEPRECATED] packet interval (default "));
2153 printf("%0.3fms)\n", (float)pkt_interval / 1000); 2102 printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000);
2154 printf(" %s\n", "-I"); 2103 printf(" %s", _("This option was never actually used and is just mentioned here for "
2155 printf(" %s", _("max target interval (currently ")); 2104 "historical purposes\n"));
2156 printf("%0.3fms)\n", (float)target_interval / 1000); 2105
2157 printf(" %s\n", "-m"); 2106 printf(" %s\n", "-I, --target-interval=TARGET_INTERVAL");
2158 printf(" %s", _("number of alive hosts required for success")); 2107 printf(" %s%0.3fms)\n The time interval to wait in between one target and the next\n",
2108 _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000);
2109 printf(" %s\n", "-m, --minimal-host-alive=MIN_ALIVE");
2110 printf(" %s", _("number of alive hosts required for success. If less than MIN_ALIVE hosts "
2111 "are OK, but MIN_ALIVE hosts are WARNING or OK, WARNING, else CRITICAL"));
2159 printf("\n"); 2112 printf("\n");
2160 printf(" %s\n", "-l"); 2113 printf(" %s\n", "-l, --outgoing-ttl=OUTGOING_TTL");
2161 printf(" %s", _("TTL on outgoing packets (currently ")); 2114 printf(" %s", _("TTL on outgoing packets (default "));
2162 printf("%u)\n", ttl); 2115 printf("%u)\n", DEFAULT_TTL);
2163 printf(" %s\n", "-t"); 2116 printf(" %s\n", "-b, --size=SIZE");
2164 printf(" %s", _("timeout value (seconds, currently ")); 2117 printf(" %s\n", _("Number of icmp ping data bytes to send"));
2165 printf("%u)\n", timeout); 2118 printf(" %s %lu + %d)\n", _("Packet size will be SIZE + icmp header (default"),
2166 printf(" %s\n", "-b"); 2119 DEFAULT_PING_DATA_SIZE, ICMP_MINLEN);
2167 printf(" %s\n", _("Number of icmp data bytes to send")); 2120 printf(" %s\n", "-v, --verbose");
2168 printf(" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"), icmp_data_size, ICMP_MINLEN); 2121 printf(" %s\n", _("Verbosity, can be given multiple times (for debugging)"));
2169 printf(" %s\n", "-v"); 2122
2170 printf(" %s\n", _("verbose")); 2123 printf(UT_OUTPUT_FORMAT);
2124
2171 printf("\n"); 2125 printf("\n");
2172 printf("%s\n", _("Notes:")); 2126 printf("%s\n", _("Notes:"));
2173 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P")); 2127 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P"));
2174 printf(" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not.")); 2128 printf(" %s\n", _("Naming a host (or several) to check is not."));
2175 printf("\n"); 2129 printf("\n");
2176 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); 2130 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
2177 printf(" %s\n", _("packet loss. The default values should work well for most users.")); 2131 printf(" %s\n", _("packet loss. The default values should work well for most users."));
2178 printf(" %s\n", _("You can specify different RTA factors using the standardized abbreviations")); 2132 printf(" %s\n",
2179 printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); 2133 _("You can specify different RTA factors using the standardized abbreviations"));
2180 /* -d not yet implemented */ 2134 printf(" %s\n",
2181 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops")); 2135 _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2182 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
2183 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
2184 printf("\n");
2185 printf(" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2186 /* printf ("%s\n", _("Long options are currently unsupported."));
2187 printf ("%s\n", _("Options marked with * require an argument"));
2188 */
2189 2136
2190 printf(UT_SUPPORT); 2137 printf(UT_SUPPORT);
2191} 2138}
2192 2139
2193void print_usage(void) { 2140void print_usage(void) {
2194 printf("%s\n", _("Usage:")); 2141 printf("%s\n", _("Usage:"));
2195 printf(" %s [options] [-H] host1 host2 hostN\n", progname); 2142 printf(" %s [options] [-H host1 [-H host2 [-H hostN]]]\n", progname);
2143}
2144
2145static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
2146 sa_family_t enforced_proto) {
2147 if (debug) {
2148 printf("add_host called with argument %s\n", arg);
2149 }
2150
2151 add_host_wrapper result = {
2152 .error_code = OK,
2153 .host = check_icmp_target_container_init(),
2154 .has_v4 = false,
2155 .has_v6 = false,
2156 };
2157
2158 add_target_wrapper targets = add_target(arg, mode, enforced_proto);
2159
2160 if (targets.error_code != OK) {
2161 result.error_code = targets.error_code;
2162 return result;
2163 }
2164
2165 result.has_v4 = targets.has_v4;
2166 result.has_v6 = targets.has_v6;
2167
2168 result.host = check_icmp_target_container_init();
2169
2170 result.host.name = strdup(arg);
2171 result.host.target_list = targets.targets;
2172 result.host.number_of_targets = targets.number_of_targets;
2173
2174 return result;
2175}
2176
2177mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
2178 check_icmp_threshold warn, check_icmp_threshold crit) {
2179 /* if no new mode selected, use old schema */
2180 if (!modes.rta_mode && !modes.pl_mode && !modes.jitter_mode && !modes.score_mode &&
2181 !modes.mos_mode && !modes.order_mode) {
2182 modes.rta_mode = true;
2183 modes.pl_mode = true;
2184 }
2185
2186 mp_subcheck result = mp_subcheck_init();
2187 result = mp_set_subcheck_default_state(result, STATE_OK);
2188
2189 char address[INET6_ADDRSTRLEN];
2190 memset(address, 0, INET6_ADDRSTRLEN);
2191 parse_address(&target.address, address, sizeof(address));
2192
2193 xasprintf(&result.output, "%s", address);
2194
2195 double packet_loss;
2196 time_t rta;
2197 if (!target.icmp_recv) {
2198 /* rta 0 is of course not entirely correct, but will still show up
2199 * conspicuously as missing entries in perfparse and cacti */
2200 packet_loss = 100;
2201 rta = 0;
2202 result = mp_set_subcheck_state(result, STATE_CRITICAL);
2203 /* up the down counter if not already counted */
2204
2205 if (target.flags & FLAG_LOST_CAUSE) {
2206 xasprintf(&result.output, "%s: %s @ %s", result.output,
2207 get_icmp_error_msg(target.icmp_type, target.icmp_code), address);
2208 } else { /* not marked as lost cause, so we have no flags for it */
2209 xasprintf(&result.output, "%s", result.output);
2210 }
2211 } else {
2212 packet_loss =
2213 (unsigned char)((target.icmp_sent - target.icmp_recv) * 100) / target.icmp_sent;
2214 rta = target.time_waited / target.icmp_recv;
2215 }
2216
2217 double EffectiveLatency;
2218 double mos; /* Mean opinion score */
2219 double score; /* score */
2220
2221 if (target.icmp_recv > 1) {
2222 /*
2223 * This algorithm is probably pretty much blindly copied from
2224 * locations like this one:
2225 * https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos It calculates a MOS
2226 * value (range of 1 to 5, where 1 is bad and 5 really good). According to some quick
2227 * research MOS originates from the Audio/Video transport network area. Whether it can
2228 * and should be computed from ICMP data, I can not say.
2229 *
2230 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
2231 *
2232 * MOS stands likely for Mean Opinion Score (
2233 * https://en.wikipedia.org/wiki/Mean_Opinion_Score )
2234 *
2235 * More links:
2236 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
2237 */
2238 target.jitter = (target.jitter / (target.icmp_recv - 1) / 1000);
2239
2240 /*
2241 * Take the average round trip latency (in milliseconds), add
2242 * round trip jitter, but double the impact to latency
2243 * then add 10 for protocol latencies (in milliseconds).
2244 */
2245 EffectiveLatency = ((double)rta / 1000) + target.jitter * 2 + 10;
2246
2247 double R;
2248 if (EffectiveLatency < 160) {
2249 R = 93.2 - (EffectiveLatency / 40);
2250 } else {
2251 R = 93.2 - ((EffectiveLatency - 120) / 10);
2252 }
2253
2254 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
2255 // loss of 5% will be entered as 5).
2256 R = R - (packet_loss * 2.5);
2257
2258 if (R < 0) {
2259 R = 0;
2260 }
2261
2262 score = R;
2263 mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
2264 } else {
2265 target.jitter = 0;
2266 target.jitter_min = 0;
2267 target.jitter_max = 0;
2268 mos = 0;
2269 }
2270
2271 /* Check which mode is on and do the warn / Crit stuff */
2272 if (modes.rta_mode) {
2273 mp_subcheck sc_rta = mp_subcheck_init();
2274 sc_rta = mp_set_subcheck_default_state(sc_rta, STATE_OK);
2275 xasprintf(&sc_rta.output, "rta %0.3fms", (double)rta / 1000);
2276
2277 if (rta >= crit.rta) {
2278 sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL);
2279 xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)crit.rta / 1000);
2280 } else if (rta >= warn.rta) {
2281 sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING);
2282 xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)warn.rta / 1000);
2283 }
2284
2285 if (packet_loss < 100) {
2286 mp_perfdata pd_rta = perfdata_init();
2287 xasprintf(&pd_rta.label, "%srta", address);
2288 pd_rta.uom = strdup("ms");
2289 pd_rta.value = mp_create_pd_value(rta / 1000);
2290 pd_rta.min = mp_create_pd_value(0);
2291
2292 pd_rta.warn = mp_range_set_end(pd_rta.warn, mp_create_pd_value(warn.rta));
2293 pd_rta.crit = mp_range_set_end(pd_rta.crit, mp_create_pd_value(crit.rta));
2294 mp_add_perfdata_to_subcheck(&sc_rta, pd_rta);
2295
2296 mp_perfdata pd_rt_min = perfdata_init();
2297 xasprintf(&pd_rt_min.label, "%srtmin", address);
2298 pd_rt_min.value = mp_create_pd_value(target.rtmin / 1000);
2299 pd_rt_min.uom = strdup("ms");
2300 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_min);
2301
2302 mp_perfdata pd_rt_max = perfdata_init();
2303 xasprintf(&pd_rt_max.label, "%srtmax", address);
2304 pd_rt_max.value = mp_create_pd_value(target.rtmax / 1000);
2305 pd_rt_max.uom = strdup("ms");
2306 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_max);
2307 }
2308
2309 mp_add_subcheck_to_subcheck(&result, sc_rta);
2310 }
2311
2312 if (modes.pl_mode) {
2313 mp_subcheck sc_pl = mp_subcheck_init();
2314 sc_pl = mp_set_subcheck_default_state(sc_pl, STATE_OK);
2315 xasprintf(&sc_pl.output, "packet loss %.1f%%", packet_loss);
2316
2317 if (packet_loss >= crit.pl) {
2318 sc_pl = mp_set_subcheck_state(sc_pl, STATE_CRITICAL);
2319 xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, crit.pl);
2320 } else if (packet_loss >= warn.pl) {
2321 sc_pl = mp_set_subcheck_state(sc_pl, STATE_WARNING);
2322 xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, warn.pl);
2323 }
2324
2325 mp_perfdata pd_pl = perfdata_init();
2326 xasprintf(&pd_pl.label, "%spl", address);
2327 pd_pl.uom = strdup("%");
2328
2329 pd_pl.warn = mp_range_set_end(pd_pl.warn, mp_create_pd_value(warn.pl));
2330 pd_pl.crit = mp_range_set_end(pd_pl.crit, mp_create_pd_value(crit.pl));
2331 pd_pl.value = mp_create_pd_value(packet_loss);
2332
2333 mp_add_perfdata_to_subcheck(&sc_pl, pd_pl);
2334
2335 mp_add_subcheck_to_subcheck(&result, sc_pl);
2336 }
2337
2338 if (modes.jitter_mode) {
2339 mp_subcheck sc_jitter = mp_subcheck_init();
2340 sc_jitter = mp_set_subcheck_default_state(sc_jitter, STATE_OK);
2341 xasprintf(&sc_jitter.output, "jitter %0.3fms", target.jitter);
2342
2343 if (target.jitter >= crit.jitter) {
2344 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_CRITICAL);
2345 xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, crit.jitter);
2346 } else if (target.jitter >= warn.jitter) {
2347 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_WARNING);
2348 xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, warn.jitter);
2349 }
2350
2351 if (packet_loss < 100) {
2352 mp_perfdata pd_jitter = perfdata_init();
2353 pd_jitter.uom = strdup("ms");
2354 xasprintf(&pd_jitter.label, "%sjitter_avg", address);
2355 pd_jitter.value = mp_create_pd_value(target.jitter);
2356 pd_jitter.warn = mp_range_set_end(pd_jitter.warn, mp_create_pd_value(warn.jitter));
2357 pd_jitter.crit = mp_range_set_end(pd_jitter.crit, mp_create_pd_value(crit.jitter));
2358 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
2359
2360 mp_perfdata pd_jitter_min = perfdata_init();
2361 pd_jitter_min.uom = strdup("ms");
2362 xasprintf(&pd_jitter_min.label, "%sjitter_min", address);
2363 pd_jitter_min.value = mp_create_pd_value(target.jitter_min);
2364 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_min);
2365
2366 mp_perfdata pd_jitter_max = perfdata_init();
2367 pd_jitter_max.uom = strdup("ms");
2368 xasprintf(&pd_jitter_max.label, "%sjitter_max", address);
2369 pd_jitter_max.value = mp_create_pd_value(target.jitter_max);
2370 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_max);
2371 }
2372 mp_add_subcheck_to_subcheck(&result, sc_jitter);
2373 }
2374
2375 if (modes.mos_mode) {
2376 mp_subcheck sc_mos = mp_subcheck_init();
2377 sc_mos = mp_set_subcheck_default_state(sc_mos, STATE_OK);
2378 xasprintf(&sc_mos.output, "MOS %0.1f", mos);
2379
2380 if (mos <= crit.mos) {
2381 sc_mos = mp_set_subcheck_state(sc_mos, STATE_CRITICAL);
2382 xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, crit.mos);
2383 } else if (mos <= warn.mos) {
2384 sc_mos = mp_set_subcheck_state(sc_mos, STATE_WARNING);
2385 xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, warn.mos);
2386 }
2387
2388 if (packet_loss < 100) {
2389 mp_perfdata pd_mos = perfdata_init();
2390 xasprintf(&pd_mos.label, "%smos", address);
2391 pd_mos.value = mp_create_pd_value(mos);
2392 pd_mos.warn = mp_range_set_end(pd_mos.warn, mp_create_pd_value(warn.mos));
2393 pd_mos.crit = mp_range_set_end(pd_mos.crit, mp_create_pd_value(crit.mos));
2394 pd_mos.min = mp_create_pd_value(0); // MOS starts at 0
2395 pd_mos.max = mp_create_pd_value(5); // MOS max is 5, by definition
2396 mp_add_perfdata_to_subcheck(&sc_mos, pd_mos);
2397 }
2398 mp_add_subcheck_to_subcheck(&result, sc_mos);
2399 }
2400
2401 if (modes.score_mode) {
2402 mp_subcheck sc_score = mp_subcheck_init();
2403 sc_score = mp_set_subcheck_default_state(sc_score, STATE_OK);
2404 xasprintf(&sc_score.output, "Score %f", score);
2405
2406 if (score <= crit.score) {
2407 sc_score = mp_set_subcheck_state(sc_score, STATE_CRITICAL);
2408 xasprintf(&sc_score.output, "%s <= %f", sc_score.output, crit.score);
2409 } else if (score <= warn.score) {
2410 sc_score = mp_set_subcheck_state(sc_score, STATE_WARNING);
2411 xasprintf(&sc_score.output, "%s <= %f", sc_score.output, warn.score);
2412 }
2413
2414 if (packet_loss < 100) {
2415 mp_perfdata pd_score = perfdata_init();
2416 xasprintf(&pd_score.label, "%sscore", address);
2417 pd_score.value = mp_create_pd_value(score);
2418 pd_score.warn = mp_range_set_end(pd_score.warn, mp_create_pd_value(warn.score));
2419 pd_score.crit = mp_range_set_end(pd_score.crit, mp_create_pd_value(crit.score));
2420 pd_score.min = mp_create_pd_value(0);
2421 pd_score.max = mp_create_pd_value(100);
2422 mp_add_perfdata_to_subcheck(&sc_score, pd_score);
2423 }
2424
2425 mp_add_subcheck_to_subcheck(&result, sc_score);
2426 }
2427
2428 if (modes.order_mode) {
2429 mp_subcheck sc_order = mp_subcheck_init();
2430 sc_order = mp_set_subcheck_default_state(sc_order, STATE_OK);
2431
2432 if (target.found_out_of_order_packets) {
2433 mp_set_subcheck_state(sc_order, STATE_CRITICAL);
2434 xasprintf(&sc_order.output, "Packets out of order");
2435 } else {
2436 xasprintf(&sc_order.output, "Packets in order");
2437 }
2438
2439 mp_add_subcheck_to_subcheck(&result, sc_order);
2440 }
2441
2442 return result;
2443}
2444
2445evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
2446 check_icmp_mode_switches modes, check_icmp_threshold warn,
2447 check_icmp_threshold crit) {
2448 evaluate_host_wrapper result = {
2449 .targets_warn = 0,
2450 .targets_ok = 0,
2451 .sc_host = mp_subcheck_init(),
2452 };
2453 result.sc_host = mp_set_subcheck_default_state(result.sc_host, STATE_OK);
2454
2455 result.sc_host.output = strdup(host.name);
2456
2457 ping_target *target = host.target_list;
2458 for (unsigned int i = 0; i < host.number_of_targets; i++) {
2459 mp_subcheck sc_target = evaluate_target(*target, modes, warn, crit);
2460
2461 mp_state_enum target_state = mp_compute_subcheck_state(sc_target);
2462
2463 if (target_state == STATE_WARNING) {
2464 result.targets_warn++;
2465 } else if (target_state == STATE_OK) {
2466 result.targets_ok++;
2467 }
2468 mp_add_subcheck_to_subcheck(&result.sc_host, sc_target);
2469
2470 target = target->next;
2471 }
2472
2473 return result;
2196} 2474}
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c
new file mode 100644
index 00000000..d56fbd8b
--- /dev/null
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.c
@@ -0,0 +1,134 @@
1#include "./config.h"
2#include <math.h>
3#include <netinet/in.h>
4#include <sys/socket.h>
5#include "./check_icmp_helpers.h"
6#include "../../plugins/netutils.h"
7
8// timeout as a global variable to make it available to the timeout handler
9unsigned int timeout = DEFAULT_TIMEOUT;
10
11check_icmp_config check_icmp_config_init() {
12 check_icmp_config tmp = {
13 .modes =
14 {
15 .order_mode = false,
16 .mos_mode = false,
17 .rta_mode = false,
18 .pl_mode = false,
19 .jitter_mode = false,
20 .score_mode = false,
21 },
22
23 .min_hosts_alive = -1,
24 .crit = {.pl = DEFAULT_CRIT_PL,
25 .rta = DEFAULT_CRIT_RTA,
26 .jitter = 50.0,
27 .mos = 3.0,
28 .score = 70.0},
29 .warn = {.pl = DEFAULT_WARN_PL,
30 .rta = DEFAULT_WARN_RTA,
31 .jitter = 40.0,
32 .mos = 3.5,
33 .score = 80.0},
34
35 .ttl = DEFAULT_TTL,
36 .icmp_data_size = DEFAULT_PING_DATA_SIZE,
37 .target_interval = 0,
38 .number_of_packets = DEFAULT_NUMBER_OF_PACKETS,
39
40 .source_ip = NULL,
41 .need_v4 = false,
42 .need_v6 = false,
43
44 .sender_id = 0,
45
46 .mode = MODE_RTA,
47
48 .number_of_targets = 0,
49 .targets = NULL,
50
51 .number_of_hosts = 0,
52 .hosts = NULL,
53
54 .output_format_is_set = false,
55 };
56 return tmp;
57}
58
59ping_target ping_target_init() {
60 ping_target tmp = {
61 .rtmin = INFINITY,
62
63 .jitter_min = INFINITY,
64
65 .found_out_of_order_packets = false,
66 };
67
68 return tmp;
69}
70
71check_icmp_state check_icmp_state_init() {
72 check_icmp_state tmp = {.icmp_sent = 0, .icmp_lost = 0, .icmp_recv = 0, .targets_down = 0};
73
74 return tmp;
75}
76
77ping_target_create_wrapper ping_target_create(struct sockaddr_storage address) {
78 ping_target_create_wrapper result = {
79 .errorcode = OK,
80 };
81
82 struct sockaddr_storage *tmp_addr = &address;
83
84 /* disregard obviously stupid addresses
85 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
86 if (((tmp_addr->ss_family == AF_INET &&
87 (((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_NONE ||
88 ((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_ANY))) ||
89 (tmp_addr->ss_family == AF_INET6 &&
90 (((struct sockaddr_in6 *)tmp_addr)->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
91 result.errorcode = ERROR;
92 return result;
93 }
94
95 /* add the fresh ip */
96 ping_target target = ping_target_init();
97
98 /* fill out the sockaddr_storage struct */
99 target.address = address;
100
101 result.host = target;
102
103 return result;
104}
105
106check_icmp_target_container check_icmp_target_container_init() {
107 check_icmp_target_container tmp = {
108 .name = NULL,
109 .number_of_targets = 0,
110 .target_list = NULL,
111 };
112 return tmp;
113}
114
115unsigned int ping_target_list_append(ping_target *list, ping_target *elem) {
116 if (elem == NULL || list == NULL) {
117 return 0;
118 }
119
120 while (list->next != NULL) {
121 list = list->next;
122 }
123
124 list->next = elem;
125
126 unsigned int result = 1;
127
128 while (elem->next != NULL) {
129 result++;
130 elem = elem->next;
131 }
132
133 return result;
134}
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h
new file mode 100644
index 00000000..dc6ea40b
--- /dev/null
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.h
@@ -0,0 +1,68 @@
1#pragma once
2
3#include "../../lib/states.h"
4#include <netinet/in_systm.h>
5#include <netinet/in.h>
6#include <netinet/ip.h>
7#include <netinet/ip6.h>
8#include <netinet/ip_icmp.h>
9#include <netinet/icmp6.h>
10#include <arpa/inet.h>
11
12typedef struct ping_target {
13 unsigned short id; /* id in **table, and icmp pkts */
14 char *msg; /* icmp error message, if any */
15
16 struct sockaddr_storage address; /* the address of this host */
17 struct sockaddr_storage error_addr; /* stores address of error replies */
18 time_t time_waited; /* total time waited, in usecs */
19 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
20 unsigned char icmp_type, icmp_code; /* type and code from errors */
21 unsigned short flags; /* control/status flags */
22
23 double rtmax; /* max rtt */
24 double rtmin; /* min rtt */
25
26 double jitter; /* measured jitter */
27 double jitter_max; /* jitter rtt maximum */
28 double jitter_min; /* jitter rtt minimum */
29
30 time_t last_tdiff;
31 unsigned int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
32
33 bool found_out_of_order_packets;
34
35 struct ping_target *next;
36} ping_target;
37
38ping_target ping_target_init();
39
40typedef struct {
41 char *name;
42 ping_target *target_list;
43 unsigned int number_of_targets;
44} check_icmp_target_container;
45
46check_icmp_target_container check_icmp_target_container_init();
47
48typedef struct {
49 unsigned int icmp_sent;
50 unsigned int icmp_recv;
51 unsigned int icmp_lost;
52 unsigned short targets_down;
53} check_icmp_state;
54
55check_icmp_state check_icmp_state_init();
56
57typedef struct {
58 int errorcode;
59 ping_target host;
60} ping_target_create_wrapper;
61
62typedef struct {
63 int socket4;
64 int socket6;
65} check_icmp_socket_set;
66
67ping_target_create_wrapper ping_target_create(struct sockaddr_storage address);
68unsigned int ping_target_list_append(ping_target *list, ping_target *elem);
diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h
new file mode 100644
index 00000000..c348bef5
--- /dev/null
+++ b/plugins-root/check_icmp.d/config.h
@@ -0,0 +1,115 @@
1#pragma once
2
3#include "../../config.h"
4#include "../../lib/states.h"
5#include <stddef.h>
6#include <netinet/in_systm.h>
7#include <netinet/in.h>
8#include <netinet/ip.h>
9#include <netinet/ip6.h>
10#include <netinet/ip_icmp.h>
11#include <netinet/icmp6.h>
12#include <arpa/inet.h>
13#include <stdint.h>
14#include "./check_icmp_helpers.h"
15#include "output.h"
16
17/* threshold structure. all values are maximum allowed, exclusive */
18typedef struct {
19 unsigned char pl; /* max allowed packet loss in percent */
20 time_t rta; /* roundtrip time average, microseconds */
21 double jitter; /* jitter time average, microseconds */
22 double mos; /* MOS */
23 double score; /* Score */
24} check_icmp_threshold;
25
26/* the different modes of this program are as follows:
27 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
28 * MODE_HOSTCHECK: Return immediately upon any sign of life
29 * In addition, sends packets to ALL addresses assigned
30 * to this host (as returned by gethostbyname() or
31 * gethostbyaddr() and expects one host only to be checked at
32 * a time. Therefore, any packet response what so ever will
33 * count as a sign of life, even when received outside
34 * crit.rta limit. Do not misspell any additional IP's.
35 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
36 * MODE_ICMP: Default Mode
37 */
38typedef enum {
39 MODE_RTA,
40 MODE_HOSTCHECK,
41 MODE_ALL,
42 MODE_ICMP,
43} check_icmp_execution_mode;
44
45typedef struct {
46 bool order_mode;
47 bool mos_mode;
48 bool rta_mode;
49 bool pl_mode;
50 bool jitter_mode;
51 bool score_mode;
52} check_icmp_mode_switches;
53
54typedef struct {
55 check_icmp_mode_switches modes;
56
57 int min_hosts_alive;
58 check_icmp_threshold crit;
59 check_icmp_threshold warn;
60
61 unsigned long ttl;
62 unsigned short icmp_data_size;
63 time_t target_interval;
64 unsigned short number_of_packets;
65
66 char *source_ip;
67 bool need_v4;
68 bool need_v6;
69
70 uint16_t sender_id; // PID of the main process, which is used as an ID in packets
71
72 check_icmp_execution_mode mode;
73
74 unsigned short number_of_targets;
75 ping_target *targets;
76
77 unsigned short number_of_hosts;
78 check_icmp_target_container *hosts;
79
80 mp_output_format output_format;
81 bool output_format_is_set;
82} check_icmp_config;
83
84check_icmp_config check_icmp_config_init();
85
86/* the data structure */
87typedef struct icmp_ping_data {
88 struct timeval stime; /* timestamp (saved in protocol struct as well) */
89 unsigned short ping_id;
90} icmp_ping_data;
91
92#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
93#define IP_HDR_SIZE 20
94#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
95#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
96#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
97
98/* 80 msec packet interval by default */
99// DEPRECATED, remove when removing the option
100#define DEFAULT_PKT_INTERVAL 80000
101
102#define DEFAULT_TARGET_INTERVAL 0
103
104#define DEFAULT_WARN_RTA 200000
105#define DEFAULT_CRIT_RTA 500000
106#define DEFAULT_WARN_PL 40
107#define DEFAULT_CRIT_PL 80
108
109#define DEFAULT_TIMEOUT 10
110#define DEFAULT_TTL 64
111
112#define DEFAULT_NUMBER_OF_PACKETS 5
113
114#define PACKET_BACKOFF_FACTOR 1.5
115#define TARGET_BACKOFF_FACTOR 1.5
diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t
index ce627736..70392154 100644
--- a/plugins-root/t/check_dhcp.t
+++ b/plugins-root/t/check_dhcp.t
@@ -12,14 +12,14 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO",
12 "no" ); 12 "no" );
13 13
14if ($allow_sudo eq "yes" or $> == 0) { 14if ($allow_sudo eq "yes" or $> == 0) {
15 plan tests => 6; 15 plan tests => 7;
16} else { 16} else {
17 plan skip_all => "Need sudo to test check_dhcp"; 17 plan skip_all => "Need sudo to test check_dhcp";
18} 18}
19my $sudo = $> == 0 ? '' : 'sudo'; 19my $sudo = $> == 0 ? '' : 'sudo';
20 20
21my $successOutput = '/OK: Received \d+ DHCPOFFER\(s\), \d+ of 1 requested servers responded, max lease time = \d+ sec\./'; 21my $successOutput = '/Received \d+ DHCPOFFER(s)*, max lease time = \d+ seconds/';
22my $failureOutput = '/CRITICAL: (No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/'; 22my $failureOutput = '/(No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/';
23my $invalidOutput = '/Invalid hostname/'; 23my $invalidOutput = '/Invalid hostname/';
24 24
25my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE", 25my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE",
@@ -34,6 +34,8 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
34 "An invalid (not known to DNS) hostname", 34 "An invalid (not known to DNS) hostname",
35 "nosuchhost" ); 35 "nosuchhost" );
36 36
37my $output_format = "--output-format mp-test-json";
38
37# try to determince interface 39# try to determince interface
38my $interface = ''; 40my $interface = '';
39 41
@@ -49,19 +51,21 @@ my $res;
49SKIP: { 51SKIP: {
50 skip('need responsive test host', 2) unless $host_responsive; 52 skip('need responsive test host', 2) unless $host_responsive;
51 $res = NPTest->testCmd( 53 $res = NPTest->testCmd(
52 "$sudo ./check_dhcp $interface -u -s $host_responsive" 54 "$sudo ./check_dhcp $interface -u -s $host_responsive $output_format"
53 ); 55 );
54 is( $res->return_code, 0, "Syntax ok" ); 56 is( $res->return_code, 0, "with JSON test format result should always be OK" );
55 like( $res->output, $successOutput, "Output OK" ); 57 like( $res->{'mp_test_result'}->{'state'}, "/OK/", "Output OK" );
58 like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $successOutput, "Output OK" );
56}; 59};
57 60
58SKIP: { 61SKIP: {
59 skip('need nonresponsive test host', 2) unless $host_nonresponsive; 62 skip('need nonresponsive test host', 2) unless $host_nonresponsive;
60 $res = NPTest->testCmd( 63 $res = NPTest->testCmd(
61 "$sudo ./check_dhcp $interface -u -s $host_nonresponsive" 64 "$sudo ./check_dhcp $interface -u -s $host_nonresponsive $output_format"
62 ); 65 );
63 is( $res->return_code, 2, "Exit code - host nonresponsive" ); 66 is( $res->return_code, 0, "with JSON test format result should always be OK" );
64 like( $res->output, $failureOutput, "Output OK" ); 67 like( $res->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Exit code - host nonresponsive" );
68 like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $failureOutput, "Output OK" );
65}; 69};
66 70
67SKIP: { 71SKIP: {
@@ -69,6 +73,6 @@ SKIP: {
69 $res = NPTest->testCmd( 73 $res = NPTest->testCmd(
70 "$sudo ./check_dhcp $interface -u -s $hostname_invalid" 74 "$sudo ./check_dhcp $interface -u -s $hostname_invalid"
71 ); 75 );
72 is( $res->return_code, 3, "Exit code - host invalid" ); 76 is( $res->return_code, 3, "invalid hostname/address should return UNKNOWN" );
73 like( $res->output, $invalidOutput, "Output OK" ); 77 like( $res->output, $invalidOutput, "Output OK" );
74}; 78};
diff --git a/plugins-root/t/check_icmp.t b/plugins-root/t/check_icmp.t
index de1d88d2..d414c3c7 100644
--- a/plugins-root/t/check_icmp.t
+++ b/plugins-root/t/check_icmp.t
@@ -12,15 +12,12 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO",
12 "no" ); 12 "no" );
13 13
14if ($allow_sudo eq "yes" or $> == 0) { 14if ($allow_sudo eq "yes" or $> == 0) {
15 plan tests => 40; 15 plan tests => 17;
16} else { 16} else {
17 plan skip_all => "Need sudo to test check_icmp"; 17 plan skip_all => "Need sudo to test check_icmp";
18} 18}
19my $sudo = $> == 0 ? '' : 'sudo'; 19my $sudo = $> == 0 ? '' : 'sudo';
20 20
21my $successOutput = '/OK - .*? rta (?:[\d\.]+ms)|(?:nan), lost \d+%/';
22my $failureOutput = '/(WARNING|CRITICAL) - .*? rta (?:[\d\.]+ms > [\d\.]+ms|nan)/';
23
24my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE", 21my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE",
25 "The hostname of system responsive to network requests", 22 "The hostname of system responsive to network requests",
26 "localhost" ); 23 "localhost" );
@@ -36,108 +33,85 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
36my $res; 33my $res;
37 34
38$res = NPTest->testCmd( 35$res = NPTest->testCmd(
39 "$sudo ./check_icmp -H $host_responsive -w 10000ms,100% -c 10000ms,100%" 36 "$sudo ./check_icmp -H $host_responsive -w 100ms,100% -c 100ms,100%"
40 ); 37 );
41is( $res->return_code, 0, "Syntax ok" ); 38is( $res->return_code, 0, "Syntax ok" );
42like( $res->output, $successOutput, "Output OK" );
43 39
44$res = NPTest->testCmd( 40$res = NPTest->testCmd(
45 "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 10000ms,100%" 41 "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 100ms,100%"
46 ); 42 );
47is( $res->return_code, 1, "Syntax ok, with forced warning" ); 43is( $res->return_code, 1, "Syntax ok, with forced warning" );
48like( $res->output, $failureOutput, "Output OK" );
49 44
50$res = NPTest->testCmd( 45$res = NPTest->testCmd(
51 "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%" 46 "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%"
52 ); 47 );
53is( $res->return_code, 2, "Syntax ok, with forced critical" ); 48is( $res->return_code, 2, "Syntax ok, with forced critical" );
54like( $res->output, $failureOutput, "Output OK" );
55 49
56$res = NPTest->testCmd( 50$res = NPTest->testCmd(
57 "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -t 2" 51 "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100%"
58 ); 52 );
59is( $res->return_code, 2, "Timeout - host nonresponsive" ); 53is( $res->return_code, 2, "Timeout - host nonresponsive" );
60like( $res->output, '/pl=100%/', "Error contains 'pl=100%' string (for 100% packet loss)" );
61like( $res->output, '/rta=U/', "Error contains 'rta=U' string" );
62 54
63$res = NPTest->testCmd( 55$res = NPTest->testCmd(
64 "$sudo ./check_icmp -w 10000ms,100% -c 10000ms,100%" 56 "$sudo ./check_icmp -w 100ms,100% -c 100ms,100%"
65 ); 57 );
66is( $res->return_code, 3, "No hostname" ); 58is( $res->return_code, 3, "No hostname" );
67like( $res->output, '/No hosts to check/', "Output with appropriate error message");
68 59
69$res = NPTest->testCmd( 60$res = NPTest->testCmd(
70 "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 0 -t 2" 61 "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 0"
71 ); 62 );
72is( $res->return_code, 0, "One host nonresponsive - zero required" ); 63is( $res->return_code, 0, "One host nonresponsive - zero required" );
73like( $res->output, $successOutput, "Output OK" );
74 64
75$res = NPTest->testCmd( 65$res = NPTest->testCmd(
76 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 1 -t 2" 66 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 1"
77 ); 67 );
78is( $res->return_code, 0, "One of two host nonresponsive - one required" ); 68is( $res->return_code, 0, "One of two host nonresponsive - one required" );
79like( $res->output, $successOutput, "Output OK" );
80 69
81$res = NPTest->testCmd( 70$res = NPTest->testCmd(
82 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" 71 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 2"
83 ); 72 );
84is( $res->return_code, 2, "One of two host nonresponsive - two required" ); 73is( $res->return_code, 2, "One of two host nonresponsive - two required" );
85like( $res->output, $failureOutput, "Output OK" );
86 74
87$res = NPTest->testCmd( 75$res = NPTest->testCmd(
88 "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" 76 "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 100ms,100% -c 100ms,100% -n 1"
89 ); 77 );
90is( $res->return_code, 0, "IPv4 source_ip accepted" ); 78is( $res->return_code, 0, "IPv4 source_ip accepted" );
91like( $res->output, $successOutput, "Output OK" );
92 79
93$res = NPTest->testCmd( 80$res = NPTest->testCmd(
94 "$sudo ./check_icmp -H $host_responsive -b 65507" 81 "$sudo ./check_icmp -H $host_responsive -b 65507"
95 ); 82 );
96is( $res->return_code, 0, "Try max packet size" ); 83is( $res->return_code, 0, "Try max packet size" );
97like( $res->output, $successOutput, "Output OK - Didn't overflow" );
98 84
99$res = NPTest->testCmd( 85$res = NPTest->testCmd(
100 "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1 -t 2" 86 "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1"
101 ); 87 );
102is( $res->return_code, 0, "rta works" ); 88is( $res->return_code, 0, "rta works" );
103like( $res->output, $successOutput, "Output OK" );
104$res = NPTest->testCmd( 89$res = NPTest->testCmd(
105 "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1 -t 2" 90 "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1"
106 ); 91 );
107is( $res->return_code, 0, "pl works" ); 92is( $res->return_code, 0, "pl works" );
108like( $res->output, '/lost 0%/', "Output OK" );
109 93
110$res = NPTest->testCmd( 94$res = NPTest->testCmd(
111 "$sudo ./check_icmp -H $host_responsive -J 80,90 -t 2" 95 "$sudo ./check_icmp -H $host_responsive -J 80,90"
112 ); 96 );
113is( $res->return_code, 0, "jitter works" ); 97is( $res->return_code, 0, "jitter works" );
114like( $res->output, '/jitter \d/', "Output OK" );
115 98
116$res = NPTest->testCmd( 99$res = NPTest->testCmd(
117 "$sudo ./check_icmp -H $host_responsive -M 4,3 -t 2" 100 "$sudo ./check_icmp -H $host_responsive -M 4,3"
118 ); 101 );
119is( $res->return_code, 0, "mos works" ); 102is( $res->return_code, 0, "mos works" );
120like( $res->output, '/MOS \d/', "Output OK" );
121 103
122$res = NPTest->testCmd( 104$res = NPTest->testCmd(
123 "$sudo ./check_icmp -H $host_responsive -S 80,70 -t 2" 105 "$sudo ./check_icmp -H $host_responsive -S 80,70"
124 ); 106 );
125is( $res->return_code, 0, "score works" ); 107is( $res->return_code, 0, "score works" );
126like( $res->output, '/Score \d/', "Output OK" );
127 108
128$res = NPTest->testCmd( 109$res = NPTest->testCmd(
129 "$sudo ./check_icmp -H $host_responsive -O -t 2" 110 "$sudo ./check_icmp -H $host_responsive -O"
130 ); 111 );
131is( $res->return_code, 0, "order works" ); 112is( $res->return_code, 0, "order works" );
132like( $res->output, '/Packets in order/', "Output OK" );
133 113
134$res = NPTest->testCmd( 114$res = NPTest->testCmd(
135 "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100 -t 2" 115 "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100"
136 ); 116 );
137is( $res->return_code, 0, "order works" ); 117is( $res->return_code, 0, "order works" );
138like( $res->output, '/Packets in order/', "Output OK" );
139like( $res->output, '/Score \d/', "Output OK" );
140like( $res->output, '/MOS \d/', "Output OK" );
141like( $res->output, '/jitter \d/', "Output OK" );
142like( $res->output, '/lost 0%/', "Output OK" );
143like( $res->output, $successOutput, "Output OK" );
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 3269b9fb..20a62fb2 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -27,7 +27,7 @@ MATHLIBS = @MATHLIBS@
27#AM_CFLAGS = -Wall 27#AM_CFLAGS = -Wall
28 28
29libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ 29libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \
30 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_nwstat check_ping \ 30 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_ping \
31 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ 31 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \
32 check_ups check_users negate \ 32 check_ups check_users negate \
33 urlize @EXTRAS@ 33 urlize @EXTRAS@
@@ -40,21 +40,28 @@ EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \
40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \ 40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \
41 check_procs check_mysql_query check_apt check_dbi check_curl \ 41 check_procs check_mysql_query check_apt check_dbi check_curl \
42 \ 42 \
43 tests/test_check_swap 43 tests/test_check_swap \
44 tests/test_check_disk
44 45
45SUBDIRS = picohttpparser 46SUBDIRS = picohttpparser
46 47
47np_test_scripts = tests/test_check_swap.t 48np_test_scripts = tests/test_check_swap.t \
49 tests/test_check_disk.t
48 50
49EXTRA_DIST = t \ 51EXTRA_DIST = t \
50 tests \ 52 tests \
51 $(np_test_scripts) \ 53 $(np_test_scripts) \
54 negate.d \
52 check_swap.d \ 55 check_swap.d \
53 check_ldap.d \ 56 check_ldap.d \
54 check_hpjd.d \ 57 check_hpjd.d \
55 check_game.d \ 58 check_game.d \
59 check_radius.d \
60 check_disk.d \
61 check_time.d \
56 check_nagios.d \ 62 check_nagios.d \
57 check_dbi.d \ 63 check_dbi.d \
64 check_tcp.d \
58 check_real.d \ 65 check_real.d \
59 check_ssh.d \ 66 check_ssh.d \
60 check_nt.d \ 67 check_nt.d \
@@ -62,14 +69,18 @@ EXTRA_DIST = t \
62 check_mrtgtraf.d \ 69 check_mrtgtraf.d \
63 check_mysql_query.d \ 70 check_mysql_query.d \
64 check_mrtg.d \ 71 check_mrtg.d \
72 check_ntp_peer.d \
65 check_apt.d \ 73 check_apt.d \
66 check_pgsql.d \ 74 check_pgsql.d \
75 check_procs.d \
76 check_ping.d \
67 check_by_ssh.d \ 77 check_by_ssh.d \
68 check_smtp.d \ 78 check_smtp.d \
69 check_mysql.d \ 79 check_mysql.d \
70 check_ntp_time.d \ 80 check_ntp_time.d \
71 check_dig.d \ 81 check_dig.d \
72 check_cluster.d \ 82 check_cluster.d \
83 check_ups.d \
73 check_fping.d 84 check_fping.d
74 85
75PLUGINHDRS = common.h 86PLUGINHDRS = common.h
@@ -112,6 +123,7 @@ check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohtt
112check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 123check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
113check_dig_LDADD = $(NETLIBS) 124check_dig_LDADD = $(NETLIBS)
114check_disk_LDADD = $(BASEOBJS) 125check_disk_LDADD = $(BASEOBJS)
126check_disk_SOURCES = check_disk.c check_disk.d/utils_disk.c
115check_dns_LDADD = $(NETLIBS) 127check_dns_LDADD = $(NETLIBS)
116check_dummy_LDADD = $(BASEOBJS) 128check_dummy_LDADD = $(BASEOBJS)
117check_fping_LDADD = $(NETLIBS) 129check_fping_LDADD = $(NETLIBS)
@@ -132,7 +144,6 @@ check_nagios_LDADD = $(BASEOBJS)
132check_nt_LDADD = $(NETLIBS) 144check_nt_LDADD = $(NETLIBS)
133check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) 145check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
134check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 146check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
135check_nwstat_LDADD = $(NETLIBS)
136check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) 147check_pgsql_LDADD = $(NETLIBS) $(PGLIBS)
137check_ping_LDADD = $(NETLIBS) 148check_ping_LDADD = $(NETLIBS)
138check_procs_LDADD = $(BASEOBJS) 149check_procs_LDADD = $(BASEOBJS)
@@ -159,6 +170,8 @@ endif
159 170
160tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap 171tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
161tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c 172tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c
173tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap
174tests_test_check_disk_SOURCES = tests/test_check_disk.c
162 175
163############################################################################## 176##############################################################################
164# secondary dependencies 177# secondary dependencies
diff --git a/plugins/check_disk.c b/plugins/check_disk.c
index 037a6f7a..515ddff0 100644
--- a/plugins/check_disk.c
+++ b/plugins/check_disk.c
@@ -31,24 +31,35 @@ const char *program_name = "check_disk"; /* Required for coreutils libs */
31const char *copyright = "1999-2024"; 31const char *copyright = "1999-2024";
32const char *email = "devel@monitoring-plugins.org"; 32const char *email = "devel@monitoring-plugins.org";
33 33
34#include "states.h"
34#include "common.h" 35#include "common.h"
36#include "output.h"
37#include "perfdata.h"
38#include "utils_base.h"
39#include "lib/thresholds.h"
40
35#ifdef HAVE_SYS_STAT_H 41#ifdef HAVE_SYS_STAT_H
36# include <sys/stat.h> 42# include <sys/stat.h>
37#endif 43#endif
44
38#if HAVE_INTTYPES_H 45#if HAVE_INTTYPES_H
39# include <inttypes.h> 46# include <inttypes.h>
40#endif 47#endif
48
41#include <assert.h> 49#include <assert.h>
42#include "popen.h"
43#include "utils.h"
44#include "utils_disk.h"
45#include <stdarg.h> 50#include <stdarg.h>
46#include "fsusage.h" 51#include <stdint.h>
47#include "mountlist.h"
48#include <float.h> 52#include <float.h>
53#include "./popen.h"
54#include "./utils.h"
55#include "../gl/fsusage.h"
56#include "../gl/mountlist.h"
57#include "./check_disk.d/utils_disk.h"
58
49#if HAVE_LIMITS_H 59#if HAVE_LIMITS_H
50# include <limits.h> 60# include <limits.h>
51#endif 61#endif
62
52#include "regex.h" 63#include "regex.h"
53 64
54#ifdef __CYGWIN__ 65#ifdef __CYGWIN__
@@ -57,424 +68,310 @@ const char *email = "devel@monitoring-plugins.org";
57# define ERROR -1 68# define ERROR -1
58#endif 69#endif
59 70
60/* If nonzero, show even filesystems with zero size or
61 uninteresting types. */
62static int show_all_fs = 1;
63
64/* If nonzero, show only local filesystems. */
65static int show_local_fs = 0;
66
67/* If nonzero, show only local filesystems but call stat() on remote ones. */
68static int stat_remote_fs = 0;
69
70/* If positive, the units to use when printing sizes;
71 if negative, the human-readable base. */
72/* static int output_block_size; */
73
74/* If nonzero, invoke the `sync' system call before getting any usage data.
75 Using this option can make df very slow, especially with many or very
76 busy disks. Note that this may make a difference on some systems --
77 SunOs4.1.3, for one. It is *not* necessary on Linux. */
78/* static int require_sync = 0; */
79
80/* Linked list of filesystem types to display.
81 If `fs_select_list' is NULL, list all types.
82 This table is generated dynamically from command-line options,
83 rather than hardcoding into the program what it thinks are the
84 valid filesystem types; let the user specify any filesystem type
85 they want to, and if there are any filesystems of that type, they
86 will be shown.
87
88 Some filesystem types:
89 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
90
91/* static struct parameter_list *fs_select_list; */
92
93/* Linked list of filesystem types to omit.
94 If the list is empty, don't exclude any types. */
95static struct regex_list *fs_exclude_list = NULL;
96
97/* Linked list of filesystem types to check.
98 If the list is empty, include all types. */
99static struct regex_list *fs_include_list;
100
101static struct name_list *dp_exclude_list;
102
103static struct parameter_list *path_select_list = NULL;
104
105/* Linked list of mounted filesystems. */
106static struct mount_entry *mount_list;
107
108/* For long options that have no equivalent short option, use a
109 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
110enum {
111 SYNC_OPTION = CHAR_MAX + 1,
112 NO_SYNC_OPTION,
113 BLOCK_SIZE_OPTION
114};
115
116#ifdef _AIX 71#ifdef _AIX
117# pragma alloca 72# pragma alloca
118#endif 73#endif
119 74
120static int process_arguments(int /*argc*/, char ** /*argv*/); 75typedef struct {
121static void set_all_thresholds(struct parameter_list *path); 76 int errorcode;
122static void print_help(void); 77 check_disk_config config;
78} check_disk_config_wrapper;
79static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
80
81static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, char *crit_freespace_units,
82 char *warn_freespace_percent, char *crit_freespace_percent, char *warn_freeinodes_percent,
83 char *crit_freeinodes_percent);
84static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/);
85static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/);
86
87/*
88 * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control how reserved
89 * and inodes should be judged (ignored or not)
90 */
91static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp, bool freespace_ignore_reserved);
92static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, byte_unit unit);
93
123void print_usage(void); 94void print_usage(void);
124static double calculate_percent(uintmax_t, uintmax_t); 95static void print_help(void);
125static bool stat_path(struct parameter_list *p);
126static void get_stats(struct parameter_list *p, struct fs_usage *fsp);
127static void get_path_stats(struct parameter_list *p, struct fs_usage *fsp);
128 96
129static char *units;
130static uintmax_t mult = 1024 * 1024;
131static int verbose = 0; 97static int verbose = 0;
132static bool erronly = false;
133static bool display_mntp = false;
134static bool exact_match = false;
135static bool ignore_missing = false;
136static bool freespace_ignore_reserved = false;
137static bool display_inodes_perfdata = false;
138static char *warn_freespace_units = NULL;
139static char *crit_freespace_units = NULL;
140static char *warn_freespace_percent = NULL;
141static char *crit_freespace_percent = NULL;
142static char *warn_usedspace_units = NULL;
143static char *crit_usedspace_units = NULL;
144static char *warn_usedspace_percent = NULL;
145static char *crit_usedspace_percent = NULL;
146static char *warn_usedinodes_percent = NULL;
147static char *crit_usedinodes_percent = NULL;
148static char *warn_freeinodes_percent = NULL;
149static char *crit_freeinodes_percent = NULL;
150static bool path_selected = false;
151static bool path_ignored = false;
152static char *group = NULL;
153static struct stat *stat_buf;
154static struct name_list *seen = NULL;
155 98
156int main(int argc, char **argv) { 99// This would not be necessary in C23!!
157 int result = STATE_UNKNOWN; 100const byte_unit Bytes_Factor = 1;
158 int disk_result = STATE_UNKNOWN; 101const byte_unit KibiBytes_factor = 1024;
159 char *output = NULL; 102const byte_unit MebiBytes_factor = 1048576;
160 char *ignored = NULL; 103const byte_unit GibiBytes_factor = 1073741824;
161 char *details = NULL; 104const byte_unit TebiBytes_factor = 1099511627776;
162 char *perf = NULL; 105const byte_unit PebiBytes_factor = 1125899906842624;
163 char *perf_ilabel = NULL; 106const byte_unit ExbiBytes_factor = 1152921504606846976;
164 char *preamble = " - free space:"; 107const byte_unit KiloBytes_factor = 1000;
165 char *ignored_preamble = " - ignored paths:"; 108const byte_unit MegaBytes_factor = 1000000;
166 char *flag_header = NULL; 109const byte_unit GigaBytes_factor = 1000000000;
167 int temp_result = STATE_UNKNOWN; 110const byte_unit TeraBytes_factor = 1000000000000;
168 111const byte_unit PetaBytes_factor = 1000000000000000;
169 struct mount_entry *me = NULL; 112const byte_unit ExaBytes_factor = 1000000000000000000;
170 struct fs_usage fsp = {0};
171 struct parameter_list *temp_list = NULL;
172 struct parameter_list *path = NULL;
173
174#ifdef __CYGWIN__
175 char mountdir[32];
176#endif
177 113
178 output = strdup("");
179 ignored = strdup("");
180 details = strdup("");
181 perf = strdup("");
182 perf_ilabel = strdup("");
183 stat_buf = malloc(sizeof *stat_buf);
184 114
115int main(int argc, char **argv) {
185 setlocale(LC_ALL, ""); 116 setlocale(LC_ALL, "");
186 bindtextdomain(PACKAGE, LOCALEDIR); 117 bindtextdomain(PACKAGE, LOCALEDIR);
187 textdomain(PACKAGE); 118 textdomain(PACKAGE);
188 119
189 mount_list = read_file_system_list(0); 120#ifdef __CYGWIN__
121 char mountdir[32];
122#endif
190 123
191 /* Parse extra opts if any */ 124 // Parse extra opts if any
192 argv = np_extra_opts(&argc, argv, progname); 125 argv = np_extra_opts(&argc, argv, progname);
193 126
194 if (process_arguments(argc, argv) == ERROR) 127 check_disk_config_wrapper tmp_config = process_arguments(argc, argv);
128 if (tmp_config.errorcode == ERROR) {
195 usage4(_("Could not parse arguments")); 129 usage4(_("Could not parse arguments"));
130 }
196 131
197 /* If a list of paths has not been selected, find entire 132 check_disk_config config = tmp_config.config;
198 mount list and create list of paths 133
199 */ 134 if (config.output_format_is_set) {
200 if (path_selected == false && path_ignored == false) { 135 mp_set_format(config.output_format);
201 for (me = mount_list; me; me = me->me_next) {
202 if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) {
203 path = np_add_parameter(&path_select_list, me->me_mountdir);
204 }
205 path->best_match = me;
206 path->group = group;
207 set_all_thresholds(path);
208 }
209 } 136 }
210 137
211 if (path_ignored == false) { 138 if (config.erronly) {
212 np_set_best_match(path_select_list, mount_list, exact_match); 139 mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY);
213 } 140 }
214 141
215 /* Error if no match found for specified paths */ 142 if (!config.path_ignored) {
216 temp_list = path_select_list; 143 mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list, config.exact_match);
144 }
217 145
218 while (path_select_list) { 146 // Error if no match found for specified paths
219 if (!path_select_list->best_match && ignore_missing == true) { 147 for (parameter_list_elem *elem = config.path_select_list.first; elem;) {
220 /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ 148 if (!elem->best_match && config.ignore_missing) {
221 if (path_select_list == temp_list) {
222 temp_list = path_select_list->name_next;
223 }
224 /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */
225 xasprintf(&ignored, "%s %s;", ignored, path_select_list->name);
226 /* Delete the path from the list so that it is not stat-checked later in the code. */ 149 /* Delete the path from the list so that it is not stat-checked later in the code. */
227 path_select_list = np_del_parameter(path_select_list, path_select_list->name_prev); 150 elem = mp_int_fs_list_del(&config.path_select_list, elem);
228 } else if (!path_select_list->best_match) { 151 continue;
152 }
153 if (!elem->best_match) {
229 /* Without --ignore-missing option, exit with Critical state. */ 154 /* Without --ignore-missing option, exit with Critical state. */
230 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list->name); 155 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name);
231 } else {
232 /* Continue jumping through the list */
233 path_select_list = path_select_list->name_next;
234 } 156 }
235 }
236 157
237 path_select_list = temp_list; 158 elem = mp_int_fs_list_get_next(elem);
159 }
238 160
239 if (!path_select_list && ignore_missing == true) { 161 mp_check overall = mp_check_init();
240 result = STATE_OK; 162 if (config.path_select_list.length == 0) {
241 if (verbose >= 2) { 163 mp_subcheck none_sc = mp_subcheck_init();
242 printf("None of the provided paths were found\n"); 164 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
165 if (config.ignore_missing) {
166 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
167 } else {
168 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
169 if (verbose >= 2) {
170 printf("None of the provided paths were found\n");
171 }
243 } 172 }
173 mp_add_subcheck_to_check(&overall, none_sc);
174 mp_exit(overall);
244 } 175 }
245 176
246 /* Process for every path in list */ 177 // Filter list first
247 for (path = path_select_list; path; path = path->name_next) { 178 for (parameter_list_elem *path = config.path_select_list.first; path;) {
248 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) 179 if (!path->best_match) {
249 printf("Thresholds(pct) for %s warn: %f crit %f\n", path->name, path->freespace_percent->warning->end, 180 path = mp_int_fs_list_del(&config.path_select_list, path);
250 path->freespace_percent->critical->end);
251
252 if (verbose >= 3 && path->group != NULL)
253 printf("Group of %s: %s\n", path->name, path->group);
254
255 /* reset disk result */
256 disk_result = STATE_UNKNOWN;
257
258 me = path->best_match;
259
260 if (!me) {
261 continue; 181 continue;
262 } 182 }
263 183
184 struct mount_entry *mount_entry = path->best_match;
185
264#ifdef __CYGWIN__ 186#ifdef __CYGWIN__
265 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) 187 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) {
188 path = mp_int_fs_list_del(&config.path_select_list, path);
266 continue; 189 continue;
190 }
191
192 char *mountdir = NULL;
267 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); 193 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10);
268 if (GetDriveType(mountdir) != DRIVE_FIXED) 194 if (GetDriveType(mountdir) != DRIVE_FIXED) {
269 me->me_remote = 1; 195 mount_entry->me_remote = 1;
196 }
270#endif 197#endif
271 /* Filters */
272 198
273 /* Remove filesystems already seen */ 199 /* Remove filesystems already seen */
274 if (np_seen_name(seen, me->me_mountdir)) { 200 if (np_seen_name(config.seen, mount_entry->me_mountdir)) {
201 path = mp_int_fs_list_del(&config.path_select_list, path);
275 continue; 202 continue;
276 } 203 }
277 np_add_name(&seen, me->me_mountdir);
278 204
279 if (path->group == NULL) { 205 if (path->group == NULL) {
280 /* Skip remote filesystems if we're not interested in them */ 206 if (config.fs_exclude_list && np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) {
281 if (me->me_remote && show_local_fs) { 207 // Skip excluded fs's
282 if (stat_remote_fs) { 208 path = mp_int_fs_list_del(&config.path_select_list, path);
283 if (!stat_path(path) && ignore_missing == true) {
284 result = STATE_OK;
285 xasprintf(&ignored, "%s %s;", ignored, path->name);
286 }
287 }
288 continue;
289 /* Skip pseudo fs's if we haven't asked for all fs's */
290 }
291 if (me->me_dummy && !show_all_fs) {
292 continue; 209 continue;
293 /* Skip excluded fstypes */
294 } 210 }
295 if (fs_exclude_list && np_find_regmatch(fs_exclude_list, me->me_type)) { 211
212 if (config.device_path_exclude_list && (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) ||
213 np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) {
214 // Skip excluded device or mount paths
215 path = mp_int_fs_list_del(&config.path_select_list, path);
296 continue; 216 continue;
297 /* Skip excluded fs's */
298 } 217 }
299 if (dp_exclude_list && (np_find_name(dp_exclude_list, me->me_devname) || np_find_name(dp_exclude_list, me->me_mountdir))) { 218
219 if (config.fs_include_list && !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) {
220 // Skip not included fstypes
221 path = mp_int_fs_list_del(&config.path_select_list, path);
300 continue; 222 continue;
301 /* Skip not included fstypes */
302 } 223 }
303 if (fs_include_list && !np_find_regmatch(fs_include_list, me->me_type)) { 224
225 /* Skip remote filesystems if we're not interested in them */
226 if (mount_entry->me_remote && config.show_local_fs) {
227 if (config.stat_remote_fs) {
228 // TODO Stat here
229 if (!stat_path(path, config.ignore_missing) && config.ignore_missing) {
230 }
231 }
304 continue; 232 continue;
305 } 233 }
306 }
307 234
308 if (!stat_path(path)) { 235 // TODO why stat here? remove unstatable fs?
309 if (ignore_missing == true) { 236 if (!stat_path(path, config.ignore_missing)) {
310 result = STATE_OK; 237 // if (config.ignore_missing) {
311 xasprintf(&ignored, "%s %s;", ignored, path->name); 238 // xasprintf(&ignored, "%s %s;", ignored, path->name);
239 // }
240 // not accessible, remove from list
241 path = mp_int_fs_list_del(&config.path_select_list, path);
242 continue;
312 } 243 }
313 continue;
314 } 244 }
315 get_fs_usage(me->me_mountdir, me->me_devname, &fsp);
316 245
317 if (fsp.fsu_blocks && strcmp("none", me->me_mountdir)) { 246 path = mp_int_fs_list_get_next(path);
318 get_stats(path, &fsp); 247 }
319
320 if (verbose >= 3) {
321 printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f "
322 "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n",
323 me->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units,
324 path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult);
325 }
326
327 /* Threshold comparisons */
328
329 temp_result = get_status(path->dfree_units, path->freespace_units);
330 if (verbose >= 3)
331 printf("Freespace_units result=%d\n", temp_result);
332 disk_result = max_state(disk_result, temp_result);
333
334 temp_result = get_status(path->dfree_pct, path->freespace_percent);
335 if (verbose >= 3)
336 printf("Freespace%% result=%d\n", temp_result);
337 disk_result = max_state(disk_result, temp_result);
338
339 temp_result = get_status(path->dused_units, path->usedspace_units);
340 if (verbose >= 3)
341 printf("Usedspace_units result=%d\n", temp_result);
342 disk_result = max_state(disk_result, temp_result);
343
344 temp_result = get_status(path->dused_pct, path->usedspace_percent);
345 if (verbose >= 3)
346 printf("Usedspace_percent result=%d\n", temp_result);
347 disk_result = max_state(disk_result, temp_result);
348
349 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
350 if (verbose >= 3)
351 printf("Usedinodes_percent result=%d\n", temp_result);
352 disk_result = max_state(disk_result, temp_result);
353
354 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
355 if (verbose >= 3)
356 printf("Freeinodes_percent result=%d\n", temp_result);
357 disk_result = max_state(disk_result, temp_result);
358
359 result = max_state(result, disk_result);
360 248
361 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes! 249 // now get the actual measurements
362 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf 250 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) {
363 data. Assumption that start=0. Roll on new syntax... 251 // Get actual metrics here
364 */ 252 struct mount_entry *mount_entry = filesystem->best_match;
253 struct fs_usage fsp = {0};
254 get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp);
365 255
366 /* *_high_tide must be reinitialized at each run */ 256 if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) {
367 uint64_t warning_high_tide = UINT64_MAX; 257 *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved);
368 258
369 if (path->freespace_units->warning != NULL) { 259 if (verbose >= 3) {
370 warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * mult; 260 printf("For %s, used_units=%lu free_units=%lu total_units=%lu "
371 } 261 "fsp.fsu_blocksize=%lu\n",
372 if (path->freespace_percent->warning != NULL) { 262 mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes, filesystem->total_bytes,
373 warning_high_tide = 263 fsp.fsu_blocksize);
374 min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * (path->dtotal_units * mult)));
375 }
376
377 uint64_t critical_high_tide = UINT64_MAX;
378
379 if (path->freespace_units->critical != NULL) {
380 critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * mult;
381 }
382 if (path->freespace_percent->critical != NULL) {
383 critical_high_tide =
384 min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * (path->dtotal_units * mult)));
385 } 264 }
265 } else {
266 // failed to retrieve file system data or not mounted?
267 filesystem = mp_int_fs_list_del(&config.path_select_list, filesystem);
268 continue;
269 }
270 filesystem = mp_int_fs_list_get_next(filesystem);
271 }
386 272
387 /* Nb: *_high_tide are unset when == UINT64_MAX */ 273 if (verbose > 2) {
388 xasprintf(&perf, "%s %s", perf, 274 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
389 perfdata_uint64((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, 275 filesystem = mp_int_fs_list_get_next(filesystem)) {
390 path->dused_units * mult, "B", (warning_high_tide == UINT64_MAX ? false : true), warning_high_tide, 276 assert(filesystem->best_match != NULL);
391 (critical_high_tide == UINT64_MAX ? false : true), critical_high_tide, true, 0, true, 277 if (filesystem->best_match == NULL) {
392 path->dtotal_units * mult)); 278 printf("Filesystem path %s has no mount_entry!\n", filesystem->name);
393 279 } else {
394 if (display_inodes_perfdata) { 280 // printf("Filesystem path %s has a mount_entry!\n", filesystem->name);
395 /* *_high_tide must be reinitialized at each run */
396 warning_high_tide = UINT64_MAX;
397 critical_high_tide = UINT64_MAX;
398
399 if (path->freeinodes_percent->warning != NULL) {
400 warning_high_tide = (uint64_t)fabs(
401 min((double)warning_high_tide, (double)(1.0 - path->freeinodes_percent->warning->end / 100) * path->inodes_total));
402 }
403 if (path->freeinodes_percent->critical != NULL) {
404 critical_high_tide = (uint64_t)fabs(min(
405 (double)critical_high_tide, (double)(1.0 - path->freeinodes_percent->critical->end / 100) * path->inodes_total));
406 }
407
408 xasprintf(&perf_ilabel, "%s (inodes)",
409 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir);
410 /* Nb: *_high_tide are unset when == UINT64_MAX */
411 xasprintf(&perf, "%s %s", perf,
412 perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX ? true : false),
413 warning_high_tide, (critical_high_tide != UINT64_MAX ? true : false), critical_high_tide, true, 0,
414 true, path->inodes_total));
415 } 281 }
282 }
283 }
416 284
417 if (disk_result == STATE_OK && erronly && !verbose) 285 measurement_unit_list *measurements = NULL;
418 continue; 286 measurement_unit_list *current = NULL;
419 287 // create measuring units, because of groups
420 if (disk_result && verbose >= 1) { 288 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; filesystem = mp_int_fs_list_get_next(filesystem)) {
421 xasprintf(&flag_header, " %s [", state_text(disk_result)); 289 assert(filesystem->best_match != NULL);
290
291 if (filesystem->group == NULL) {
292 // create a measurement unit for the fs
293 measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
294 if (measurements == NULL) {
295 measurements = current = add_measurement_list(NULL, unit);
422 } else { 296 } else {
423 xasprintf(&flag_header, ""); 297 current = add_measurement_list(measurements, unit);
424 } 298 }
425 xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, 299 } else {
426 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, path->dfree_units, units, 300 // Grouped elements are consecutive
427 path->dfree_pct); 301 if (measurements == NULL) {
428 if (path->dused_inodes_percent < 0) { 302 // first entry
429 xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : "")); 303 measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
304 unit.name = strdup(filesystem->group);
305 measurements = current = add_measurement_list(NULL, unit);
430 } else { 306 } else {
431 xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : "")); 307 // if this is the first element of a group, the name of the previous entry is different
308 if (strcmp(filesystem->group, current->unit.name) != 0) {
309 // so, this must be the first element of a group
310 measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
311 unit.name = filesystem->group;
312 current = add_measurement_list(measurements, unit);
313
314 } else {
315 // NOT the first entry of a group, add info to the other one
316 current->unit = add_filesystem_to_measurement_unit(current->unit, *filesystem);
317 }
432 } 318 }
433 free(flag_header);
434 } 319 }
435 } 320 }
436 321
437 if (verbose >= 2) 322 /* Process for every path in list */
438 xasprintf(&output, "%s%s", output, details); 323 if (measurements != NULL) {
324 for (measurement_unit_list *unit = measurements; unit; unit = unit->next) {
325 mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata, config.display_unit);
326 mp_add_subcheck_to_check(&overall, unit_sc);
327 }
328 } else {
329 // Apparently no machting fs found
330 mp_subcheck none_sc = mp_subcheck_init();
331 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
439 332
440 if (strcmp(output, "") == 0 && !erronly) { 333 if (config.ignore_missing) {
441 preamble = ""; 334 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
442 xasprintf(&output, " - No disks were found for provided parameters"); 335 } else {
336 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
337 }
338 mp_add_subcheck_to_check(&overall, none_sc);
443 } 339 }
444 340
445 printf("DISK %s%s%s%s%s|%s\n", state_text(result), ((erronly && result == STATE_OK)) ? "" : preamble, output, 341 mp_exit(overall);
446 (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf);
447 return result;
448} 342}
449 343
450double calculate_percent(uintmax_t value, uintmax_t total) { 344double calculate_percent(uintmax_t value, uintmax_t total) {
451 double pct = -1; 345 double pct = -1;
452 if (value <= DBL_MAX && total != 0) { 346 if (value <= DBL_MAX && total != 0) {
453 pct = (double)value / total * 100.0; 347 pct = (double)value / (double)total * 100.0;
454 } 348 }
349
455 return pct; 350 return pct;
456} 351}
457 352
458/* process command-line arguments */ 353/* process command-line arguments */
459int process_arguments(int argc, char **argv) { 354check_disk_config_wrapper process_arguments(int argc, char **argv) {
460 int c; 355
461 int err; 356 check_disk_config_wrapper result = {
462 struct parameter_list *se; 357 .errorcode = OK,
463 struct parameter_list *temp_list = NULL; 358 .config = check_disk_config_init(),
464 struct parameter_list *previous = NULL; 359 };
465 struct mount_entry *me; 360
466 regex_t re; 361 if (argc < 2) {
467 int cflags = REG_NOSUB | REG_EXTENDED; 362 result.errorcode = ERROR;
468 int default_cflags = cflags; 363 return result;
469 char errbuf[MAX_INPUT_BUFFER]; 364 }
470 int fnd = 0; 365
366 enum {
367 output_format_index = CHAR_MAX + 1,
368 display_unit_index,
369 };
471 370
472 int option = 0;
473 static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, 371 static struct option longopts[] = {{"timeout", required_argument, 0, 't'},
474 {"warning", required_argument, 0, 'w'}, 372 {"warning", required_argument, 0, 'w'},
475 {"critical", required_argument, 0, 'c'}, 373 {"critical", required_argument, 0, 'c'},
476 {"iwarning", required_argument, 0, 'W'}, 374 {"iwarning", required_argument, 0, 'W'},
477 /* Dang, -C is taken. We might want to reshuffle this. */
478 {"icritical", required_argument, 0, 'K'}, 375 {"icritical", required_argument, 0, 'K'},
479 {"kilobytes", no_argument, 0, 'k'}, 376 {"kilobytes", no_argument, 0, 'k'},
480 {"megabytes", no_argument, 0, 'm'}, 377 {"megabytes", no_argument, 0, 'm'},
@@ -507,24 +404,42 @@ int process_arguments(int argc, char **argv) {
507 {"clear", no_argument, 0, 'C'}, 404 {"clear", no_argument, 0, 'C'},
508 {"version", no_argument, 0, 'V'}, 405 {"version", no_argument, 0, 'V'},
509 {"help", no_argument, 0, 'h'}, 406 {"help", no_argument, 0, 'h'},
407 {"output-format", required_argument, 0, output_format_index},
408 {"display-unit", required_argument, 0, display_unit_index},
510 {0, 0, 0, 0}}; 409 {0, 0, 0, 0}};
511 410
512 if (argc < 2) 411 for (int index = 1; index < argc; index++) {
513 return ERROR; 412 if (strcmp("-to", argv[index]) == 0) {
413 strcpy(argv[index], "-t");
414 }
415 }
416
417 int cflags = REG_NOSUB | REG_EXTENDED;
418 int default_cflags = cflags;
419 char *warn_freespace_units = NULL;
420 char *crit_freespace_units = NULL;
421 char *warn_freespace_percent = NULL;
422 char *crit_freespace_percent = NULL;
423 char *warn_freeinodes_percent = NULL;
424 char *crit_freeinodes_percent = NULL;
425
426 bool path_selected = false;
427 char *group = NULL;
428 byte_unit unit = MebiBytes_factor;
514 429
515 np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); 430 result.config.mount_list = read_file_system_list(false);
516 431
517 for (c = 1; c < argc; c++) 432 np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED);
518 if (strcmp("-to", argv[c]) == 0)
519 strcpy(argv[c], "-t");
520 433
521 while (1) { 434 while (true) {
522 c = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); 435 int option = 0;
436 int option_index = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
523 437
524 if (c == -1 || c == EOF) 438 if (option_index == -1 || option_index == EOF) {
525 break; 439 break;
440 }
526 441
527 switch (c) { 442 switch (option_index) {
528 case 't': /* timeout period */ 443 case 't': /* timeout period */
529 if (is_integer(optarg)) { 444 if (is_integer(optarg)) {
530 timeout_interval = atoi(optarg); 445 timeout_interval = atoi(optarg);
@@ -555,10 +470,10 @@ int process_arguments(int argc, char **argv) {
555 break; 470 break;
556 471
557 /* Awful mistake where the range values do not make sense. Normally, 472 /* Awful mistake where the range values do not make sense. Normally,
558 you alert if the value is within the range, but since we are using 473 * you alert if the value is within the range, but since we are using
559 freespace, we have to alert if outside the range. Thus we artificially 474 * freespace, we have to alert if outside the range. Thus we artificially
560 force @ at the beginning of the range, so that it is backwards compatible 475 * force @ at the beginning of the range, so that it is backwards compatible
561 */ 476 */
562 case 'c': /* critical threshold */ 477 case 'c': /* critical threshold */
563 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { 478 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
564 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); 479 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg);
@@ -594,181 +509,184 @@ int process_arguments(int argc, char **argv) {
594 } 509 }
595 break; 510 break;
596 case 'u': 511 case 'u':
597 if (units)
598 free(units);
599 if (!strcasecmp(optarg, "bytes")) { 512 if (!strcasecmp(optarg, "bytes")) {
600 mult = (uintmax_t)1; 513 unit = Bytes_Factor;
601 units = strdup("B");
602 } else if (!strcmp(optarg, "KiB")) { 514 } else if (!strcmp(optarg, "KiB")) {
603 mult = (uintmax_t)1024; 515 unit = KibiBytes_factor;
604 units = strdup("KiB");
605 } else if (!strcmp(optarg, "kB")) { 516 } else if (!strcmp(optarg, "kB")) {
606 mult = (uintmax_t)1000; 517 unit = KiloBytes_factor;
607 units = strdup("kB");
608 } else if (!strcmp(optarg, "MiB")) { 518 } else if (!strcmp(optarg, "MiB")) {
609 mult = (uintmax_t)1024 * 1024; 519 unit = MebiBytes_factor;
610 units = strdup("MiB");
611 } else if (!strcmp(optarg, "MB")) { 520 } else if (!strcmp(optarg, "MB")) {
612 mult = (uintmax_t)1000 * 1000; 521 unit = MegaBytes_factor;
613 units = strdup("MB");
614 } else if (!strcmp(optarg, "GiB")) { 522 } else if (!strcmp(optarg, "GiB")) {
615 mult = (uintmax_t)1024 * 1024 * 1024; 523 unit = GibiBytes_factor;
616 units = strdup("GiB");
617 } else if (!strcmp(optarg, "GB")) { 524 } else if (!strcmp(optarg, "GB")) {
618 mult = (uintmax_t)1000 * 1000 * 1000; 525 unit = GigaBytes_factor;
619 units = strdup("GB");
620 } else if (!strcmp(optarg, "TiB")) { 526 } else if (!strcmp(optarg, "TiB")) {
621 mult = (uintmax_t)1024 * 1024 * 1024 * 1024; 527 unit = TebiBytes_factor;
622 units = strdup("TiB");
623 } else if (!strcmp(optarg, "TB")) { 528 } else if (!strcmp(optarg, "TB")) {
624 mult = (uintmax_t)1000 * 1000 * 1000 * 1000; 529 unit = TeraBytes_factor;
625 units = strdup("TB");
626 } else if (!strcmp(optarg, "PiB")) { 530 } else if (!strcmp(optarg, "PiB")) {
627 mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; 531 unit = PebiBytes_factor;
628 units = strdup("PiB");
629 } else if (!strcmp(optarg, "PB")) { 532 } else if (!strcmp(optarg, "PB")) {
630 mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; 533 unit = PetaBytes_factor;
631 units = strdup("PB");
632 } else { 534 } else {
633 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); 535 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
634 } 536 }
635 if (units == NULL)
636 die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
637 break; 537 break;
638 case 'k': /* display mountpoint */ 538 case 'k':
639 mult = 1024; 539 unit = KibiBytes_factor;
640 if (units) 540 break;
641 free(units); 541 case 'm':
642 units = strdup("kiB"); 542 unit = MebiBytes_factor;
643 break; 543 break;
644 case 'm': /* display mountpoint */ 544 case display_unit_index:
645 mult = 1024 * 1024; 545 if (!strcasecmp(optarg, "bytes")) {
646 if (units) 546 result.config.display_unit = Bytes;
647 free(units); 547 } else if (!strcmp(optarg, "KiB")) {
648 units = strdup("MiB"); 548 result.config.display_unit = KibiBytes;
549 } else if (!strcmp(optarg, "kB")) {
550 result.config.display_unit = KiloBytes;
551 } else if (!strcmp(optarg, "MiB")) {
552 result.config.display_unit = MebiBytes;
553 } else if (!strcmp(optarg, "MB")) {
554 result.config.display_unit = MegaBytes;
555 } else if (!strcmp(optarg, "GiB")) {
556 result.config.display_unit = GibiBytes;
557 } else if (!strcmp(optarg, "GB")) {
558 result.config.display_unit = GigaBytes;
559 } else if (!strcmp(optarg, "TiB")) {
560 result.config.display_unit = TebiBytes;
561 } else if (!strcmp(optarg, "TB")) {
562 result.config.display_unit = TeraBytes;
563 } else if (!strcmp(optarg, "PiB")) {
564 result.config.display_unit = PebiBytes;
565 } else if (!strcmp(optarg, "PB")) {
566 result.config.display_unit = PetaBytes;
567 } else {
568 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
569 }
649 break; 570 break;
650 case 'L': 571 case 'L':
651 stat_remote_fs = 1; 572 result.config.stat_remote_fs = true;
652 /* fallthrough */ 573 /* fallthrough */
653 case 'l': 574 case 'l':
654 show_local_fs = 1; 575 result.config.show_local_fs = true;
655 break; 576 break;
656 case 'P': 577 case 'P':
657 display_inodes_perfdata = 1; 578 result.config.display_inodes_perfdata = true;
658 break; 579 break;
659 case 'p': /* select path */ 580 case 'p': /* select path */ {
660 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || 581 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent ||
661 warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || 582 warn_freeinodes_percent || crit_freeinodes_percent)) {
662 warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
663 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); 583 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
664 } 584 }
665 585
666 /* add parameter if not found. overwrite thresholds if path has already been added */ 586 /* add parameter if not found. overwrite thresholds if path has already been added */
667 if (!(se = np_find_parameter(path_select_list, optarg))) { 587 parameter_list_elem *search_entry;
668 se = np_add_parameter(&path_select_list, optarg); 588 if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) {
669 589 search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg);
670 if (stat(optarg, &stat_buf[0]) && ignore_missing == true) { 590
671 path_ignored = true; 591 // struct stat stat_buf = {};
672 break; 592 // if (stat(optarg, &stat_buf) && result.config.ignore_missing) {
673 } 593 // result.config.path_ignored = true;
594 // break;
595 // }
674 } 596 }
675 se->group = group; 597 search_entry->group = group;
676 set_all_thresholds(se); 598 set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent,
599
600 warn_freeinodes_percent, crit_freeinodes_percent);
677 601
678 /* With autofs, it is required to stat() the path before re-populating the mount_list */ 602 /* With autofs, it is required to stat() the path before re-populating the mount_list */
679 if (!stat_path(se)) { 603 // if (!stat_path(se, result.config.ignore_missing)) {
680 break; 604 // break;
681 } 605 // }
682 /* NB: We can't free the old mount_list "just like that": both list pointers and struct 606 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match);
683 * pointers are copied around. One of the reason it wasn't done yet is that other parts
684 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
685 mount_list = read_file_system_list(0);
686 np_set_best_match(se, mount_list, exact_match);
687 607
688 path_selected = true; 608 path_selected = true;
689 break; 609 } break;
690 case 'x': /* exclude path or partition */ 610 case 'x': /* exclude path or partition */
691 np_add_name(&dp_exclude_list, optarg); 611 np_add_name(&result.config.device_path_exclude_list, optarg);
692 break; 612 break;
693 case 'X': /* exclude file system type */ 613 case 'X': /* exclude file system type */ {
694 err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED); 614 int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED);
695 if (err != 0) { 615 if (err != 0) {
696 regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); 616 char errbuf[MAX_INPUT_BUFFER];
617 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
697 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 618 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf);
698 } 619 }
699 break; 620 break;
700 case 'N': /* include file system type */ 621 case 'N': /* include file system type */
701 err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED); 622 err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED);
702 if (err != 0) { 623 if (err != 0) {
703 regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); 624 char errbuf[MAX_INPUT_BUFFER];
625 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
704 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 626 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf);
705 } 627 }
706 break; 628 } break;
707 case 'v': /* verbose */ 629 case 'v': /* verbose */
708 verbose++; 630 verbose++;
709 break; 631 break;
710 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */ 632 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
711 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */ 633 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
712 erronly = true; 634 result.config.erronly = true;
713 break; 635 break;
714 case 'e': 636 case 'e':
715 erronly = true; 637 result.config.erronly = true;
716 break; 638 break;
717 case 'E': 639 case 'E':
718 if (path_selected) 640 if (path_selected) {
719 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); 641 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
720 exact_match = true; 642 }
643 result.config.exact_match = true;
721 break; 644 break;
722 case 'f': 645 case 'f':
723 freespace_ignore_reserved = true; 646 result.config.freespace_ignore_reserved = true;
724 break; 647 break;
725 case 'g': 648 case 'g':
726 if (path_selected) 649 if (path_selected) {
727 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); 650 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
651 }
728 group = optarg; 652 group = optarg;
729 break; 653 break;
730 case 'I': 654 case 'I':
731 cflags |= REG_ICASE; 655 cflags |= REG_ICASE;
732 // Intentional fallthrough 656 // Intentional fallthrough
733 case 'i': 657 case 'i': {
734 if (!path_selected) 658 if (!path_selected) {
735 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), 659 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"),
736 _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); 660 _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
737 err = regcomp(&re, optarg, cflags); 661 }
662 regex_t regex;
663 int err = regcomp(&regex, optarg, cflags);
738 if (err != 0) { 664 if (err != 0) {
739 regerror(err, &re, errbuf, MAX_INPUT_BUFFER); 665 char errbuf[MAX_INPUT_BUFFER];
666 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
740 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 667 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf);
741 } 668 }
742 669
743 temp_list = path_select_list; 670 for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) {
744 671 if (elem->best_match) {
745 previous = NULL; 672 if (np_regex_match_mount_entry(elem->best_match, &regex)) {
746 while (temp_list) {
747 if (temp_list->best_match) {
748 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
749 673
750 if (verbose >= 3) 674 if (verbose >= 3) {
751 printf("ignoring %s matching regex\n", temp_list->name); 675 printf("ignoring %s matching regex\n", elem->name);
676 }
752 677
753 temp_list = np_del_parameter(temp_list, previous); 678 elem = mp_int_fs_list_del(&result.config.path_select_list, elem);
754 /* pointer to first element needs to be updated if first item gets deleted */ 679 continue;
755 if (previous == NULL)
756 path_select_list = temp_list;
757 } else {
758 previous = temp_list;
759 temp_list = temp_list->name_next;
760 } 680 }
761 } else {
762 previous = temp_list;
763 temp_list = temp_list->name_next;
764 } 681 }
682
683 elem = mp_int_fs_list_get_next(elem);
765 } 684 }
766 685
767 cflags = default_cflags; 686 cflags = default_cflags;
768 break; 687 } break;
769
770 case 'n': 688 case 'n':
771 ignore_missing = true; 689 result.config.ignore_missing = true;
772 break; 690 break;
773 case 'A': 691 case 'A':
774 optarg = strdup(".*"); 692 optarg = strdup(".*");
@@ -776,80 +694,83 @@ int process_arguments(int argc, char **argv) {
776 case 'R': 694 case 'R':
777 cflags |= REG_ICASE; 695 cflags |= REG_ICASE;
778 // Intentional fallthrough 696 // Intentional fallthrough
779 case 'r': 697 case 'r': {
780 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || 698 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent ||
781 warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || 699 warn_freeinodes_percent || crit_freeinodes_percent)) {
782 warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
783 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), 700 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
784 _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); 701 _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n"));
785 } 702 }
786 703
787 err = regcomp(&re, optarg, cflags); 704 regex_t regex;
705 int err = regcomp(&regex, optarg, cflags);
788 if (err != 0) { 706 if (err != 0) {
789 regerror(err, &re, errbuf, MAX_INPUT_BUFFER); 707 char errbuf[MAX_INPUT_BUFFER];
708 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
790 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 709 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf);
791 } 710 }
792 711
793 for (me = mount_list; me; me = me->me_next) { 712 bool found = false;
794 if (np_regex_match_mount_entry(me, &re)) { 713 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
795 fnd = true; 714 if (np_regex_match_mount_entry(me, &regex)) {
796 if (verbose >= 3) 715 found = true;
716 if (verbose >= 3) {
797 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); 717 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
718 }
798 719
799 /* add parameter if not found. overwrite thresholds if path has already been added */ 720 /* add parameter if not found. overwrite thresholds if path has already been added */
800 if (!(se = np_find_parameter(path_select_list, me->me_mountdir))) { 721 parameter_list_elem *se = NULL;
801 se = np_add_parameter(&path_select_list, me->me_mountdir); 722 if (!(se = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) {
723 se = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
802 } 724 }
803 se->group = group; 725 se->group = group;
804 set_all_thresholds(se); 726 set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent,
727 warn_freeinodes_percent, crit_freeinodes_percent);
805 } 728 }
806 } 729 }
807 730
808 if (!fnd && ignore_missing == true) { 731 if (!found) {
809 path_ignored = true; 732 if (result.config.ignore_missing) {
810 path_selected = true; 733 result.config.path_ignored = true;
811 break; 734 path_selected = true;
812 } 735 break;
813 if (!fnd) 736 }
737
814 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg); 738 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg);
739 }
815 740
816 fnd = false;
817 path_selected = true; 741 path_selected = true;
818 np_set_best_match(path_select_list, mount_list, exact_match); 742 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match);
819 cflags = default_cflags; 743 cflags = default_cflags;
820 744
821 break; 745 } break;
822 case 'M': /* display mountpoint */ 746 case 'M': /* display mountpoint */
823 display_mntp = true; 747 result.config.display_mntp = true;
824 break; 748 break;
825 case 'C': 749 case 'C': {
826 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ 750 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
827 if (path_selected == false) { 751 if (!path_selected) {
828 struct parameter_list *path; 752 parameter_list_elem *path;
829 for (me = mount_list; me; me = me->me_next) { 753 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
830 if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) 754 if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) {
831 path = np_add_parameter(&path_select_list, me->me_mountdir); 755 path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
756 }
832 path->best_match = me; 757 path->best_match = me;
833 path->group = group; 758 path->group = group;
834 set_all_thresholds(path); 759 set_all_thresholds(path, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent,
760 warn_freeinodes_percent, crit_freeinodes_percent);
835 } 761 }
836 } 762 }
763
837 warn_freespace_units = NULL; 764 warn_freespace_units = NULL;
838 crit_freespace_units = NULL; 765 crit_freespace_units = NULL;
839 warn_usedspace_units = NULL;
840 crit_usedspace_units = NULL;
841 warn_freespace_percent = NULL; 766 warn_freespace_percent = NULL;
842 crit_freespace_percent = NULL; 767 crit_freespace_percent = NULL;
843 warn_usedspace_percent = NULL;
844 crit_usedspace_percent = NULL;
845 warn_usedinodes_percent = NULL;
846 crit_usedinodes_percent = NULL;
847 warn_freeinodes_percent = NULL; 768 warn_freeinodes_percent = NULL;
848 crit_freeinodes_percent = NULL; 769 crit_freeinodes_percent = NULL;
849 770
850 path_selected = false; 771 path_selected = false;
851 group = NULL; 772 group = NULL;
852 break; 773 } break;
853 case 'V': /* version */ 774 case 'V': /* version */
854 print_revision(progname, NP_VERSION); 775 print_revision(progname, NP_VERSION);
855 exit(STATE_UNKNOWN); 776 exit(STATE_UNKNOWN);
@@ -858,50 +779,145 @@ int process_arguments(int argc, char **argv) {
858 exit(STATE_UNKNOWN); 779 exit(STATE_UNKNOWN);
859 case '?': /* help */ 780 case '?': /* help */
860 usage(_("Unknown argument")); 781 usage(_("Unknown argument"));
782 case output_format_index: {
783 parsed_output_format parser = mp_parse_output_format(optarg);
784 if (!parser.parsing_success) {
785 // TODO List all available formats here, maybe add anothoer usage function
786 printf("Invalid output format: %s\n", optarg);
787 exit(STATE_UNKNOWN);
788 }
789
790 result.config.output_format_is_set = true;
791 result.config.output_format = parser.output_format;
792 break;
793 }
861 } 794 }
862 } 795 }
863 796
864 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ 797 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
865 c = optind; 798 int index = optind;
866 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) 799
867 warn_usedspace_percent = argv[c++]; 800 if (argc > index && is_intnonneg(argv[index])) {
801 if (verbose > 0) {
802 printf("Got an positional warn threshold: %s\n", argv[index]);
803 }
804 char *range = argv[index++];
805 mp_range_parsed tmp = mp_parse_range_string(range);
806 if (tmp.error != MP_PARSING_SUCCES) {
807 die(STATE_UNKNOWN, "failed to parse warning threshold");
808 }
809
810 mp_range tmp_range = tmp.range;
811 // Invert range to use it for free instead of used
812 // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range;
868 813
869 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) 814 warn_freespace_percent = mp_range_to_string(tmp_range);
870 crit_usedspace_percent = argv[c++];
871 815
872 if (argc > c) { 816 if (verbose > 0) {
873 se = np_add_parameter(&path_select_list, strdup(argv[c++])); 817 printf("Positional warning threshold transformed to: %s\n", warn_freespace_percent);
818 }
819 }
820
821 if (argc > index && is_intnonneg(argv[index])) {
822 if (verbose > 0) {
823 printf("Got an positional crit threshold: %s\n", argv[index]);
824 }
825 char *range = argv[index++];
826 mp_range_parsed tmp = mp_parse_range_string(range);
827 if (tmp.error != MP_PARSING_SUCCES) {
828 die(STATE_UNKNOWN, "failed to parse warning threshold");
829 }
830
831 mp_range tmp_range = tmp.range;
832 // Invert range to use it for free instead of used
833 // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range;
834
835 crit_freespace_percent = mp_range_to_string(tmp_range);
836
837 if (verbose > 0) {
838 printf("Positional critical threshold transformed to: %s\n", crit_freespace_percent);
839 }
840 }
841
842 if (argc > index) {
843 if (verbose > 0) {
844 printf("Got an positional filesystem: %s\n", argv[index]);
845 }
846 struct parameter_list *se = mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++]));
874 path_selected = true; 847 path_selected = true;
875 set_all_thresholds(se); 848 set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent,
849 warn_freeinodes_percent, crit_freeinodes_percent);
850 }
851
852 // If a list of paths has not been explicitly selected, find entire
853 // mount list and create list of paths
854 if (!path_selected && !result.config.path_ignored) {
855 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
856 if (me->me_dummy != 0) {
857 // just do not add dummy filesystems
858 continue;
859 }
860
861 parameter_list_elem *path = NULL;
862 if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) {
863 path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
864 }
865 path->best_match = me;
866 path->group = group;
867 set_all_thresholds(path, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent,
868 warn_freeinodes_percent, crit_freeinodes_percent);
869 }
876 } 870 }
877 871
878 if (units == NULL) { 872 // Set thresholds to the appropriate unit
879 units = strdup("MiB"); 873 for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp; tmp = mp_int_fs_list_get_next(tmp)) {
880 mult = (uintmax_t)1024 * 1024; 874
875 mp_perfdata_value factor = mp_create_pd_value(unit);
876
877 if (tmp->freespace_units.critical_is_set) {
878 tmp->freespace_units.critical = mp_range_multiply(tmp->freespace_units.critical, factor);
879 }
880 if (tmp->freespace_units.warning_is_set) {
881 tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor);
882 }
881 } 883 }
882 884
883 return true; 885 return result;
884} 886}
885 887
886void set_all_thresholds(struct parameter_list *path) { 888void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, char *crit_freespace_units, char *warn_freespace_percent,
887 if (path->freespace_units != NULL) 889 char *crit_freespace_percent, char *warn_freeinodes_percent, char *crit_freeinodes_percent) {
888 free(path->freespace_units); 890 mp_range_parsed tmp;
889 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); 891
890 if (path->freespace_percent != NULL) 892 if (warn_freespace_units) {
891 free(path->freespace_percent); 893 tmp = mp_parse_range_string(warn_freespace_units);
892 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); 894 path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range);
893 if (path->usedspace_units != NULL) 895 }
894 free(path->usedspace_units); 896
895 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); 897 if (crit_freespace_units) {
896 if (path->usedspace_percent != NULL) 898 tmp = mp_parse_range_string(crit_freespace_units);
897 free(path->usedspace_percent); 899 path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range);
898 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); 900 }
899 if (path->usedinodes_percent != NULL) 901
900 free(path->usedinodes_percent); 902 if (warn_freespace_percent) {
901 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); 903 tmp = mp_parse_range_string(warn_freespace_percent);
902 if (path->freeinodes_percent != NULL) 904 path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range);
903 free(path->freeinodes_percent); 905 }
904 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); 906
907 if (crit_freespace_percent) {
908 tmp = mp_parse_range_string(crit_freespace_percent);
909 path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range);
910 }
911
912 if (warn_freeinodes_percent) {
913 tmp = mp_parse_range_string(warn_freeinodes_percent);
914 path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range);
915 }
916
917 if (crit_freeinodes_percent) {
918 tmp = mp_parse_range_string(crit_freeinodes_percent);
919 path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range);
920 }
905} 921}
906 922
907void print_help(void) { 923void print_help(void) {
@@ -948,8 +964,6 @@ void print_help(void) {
948 printf(" %s\n", _("Display inode usage in perfdata")); 964 printf(" %s\n", _("Display inode usage in perfdata"));
949 printf(" %s\n", "-g, --group=NAME"); 965 printf(" %s\n", "-g, --group=NAME");
950 printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together")); 966 printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
951 printf(" %s\n", "-k, --kilobytes");
952 printf(" %s\n", _("Same as '--units kB'"));
953 printf(" %s\n", "-l, --local"); 967 printf(" %s\n", "-l, --local");
954 printf(" %s\n", _("Only check local filesystems")); 968 printf(" %s\n", _("Only check local filesystems"));
955 printf(" %s\n", "-L, --stat-remote-fs"); 969 printf(" %s\n", "-L, --stat-remote-fs");
@@ -957,8 +971,6 @@ void print_help(void) {
957 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); 971 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
958 printf(" %s\n", "-M, --mountpoint"); 972 printf(" %s\n", "-M, --mountpoint");
959 printf(" %s\n", _("Display the (block) device instead of the mount point")); 973 printf(" %s\n", _("Display the (block) device instead of the mount point"));
960 printf(" %s\n", "-m, --megabytes");
961 printf(" %s\n", _("Same as '--units MB'"));
962 printf(" %s\n", "-A, --all"); 974 printf(" %s\n", "-A, --all");
963 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); 975 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
964 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); 976 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
@@ -974,12 +986,25 @@ void print_help(void) {
974 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); 986 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
975 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 987 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
976 printf(" %s\n", "-u, --units=STRING"); 988 printf(" %s\n", "-u, --units=STRING");
977 printf(" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); 989 printf(" %s\n", _("Select the unit used for the absolute value thresholds"));
990 printf(
991 " %s\n",
992 _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", \"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
993 printf(" %s\n", "-k, --kilobytes");
994 printf(" %s\n", _("Same as '--units kB'"));
995 printf(" %s\n", "--display-unit");
996 printf(" %s\n", _("Select the unit used for in the output"));
997 printf(
998 " %s\n",
999 _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", \"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1000 printf(" %s\n", "-m, --megabytes");
1001 printf(" %s\n", _("Same as '--units MB'"));
978 printf(UT_VERBOSE); 1002 printf(UT_VERBOSE);
979 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); 1003 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX");
980 printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); 1004 printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
981 printf(" %s\n", "-N, --include-type=TYPE_REGEX"); 1005 printf(" %s\n", "-N, --include-type=TYPE_REGEX");
982 printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); 1006 printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)"));
1007 printf(UT_OUTPUT_FORMAT);
983 1008
984 printf("\n"); 1009 printf("\n");
985 printf("%s\n", _("General usage hints:")); 1010 printf("%s\n", _("General usage hints:"));
@@ -1009,105 +1034,187 @@ void print_usage(void) {
1009 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); 1034 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n");
1010} 1035}
1011 1036
1012bool stat_path(struct parameter_list *p) { 1037bool stat_path(parameter_list_elem *parameters, bool ignore_missing) {
1013 /* Stat entry to check that dir exists and is accessible */ 1038 /* Stat entry to check that dir exists and is accessible */
1014 if (verbose >= 3) 1039 if (verbose >= 3) {
1015 printf("calling stat on %s\n", p->name); 1040 printf("calling stat on %s\n", parameters->name);
1016 if (stat(p->name, &stat_buf[0])) { 1041 }
1017 if (verbose >= 3) 1042
1018 printf("stat failed on %s\n", p->name); 1043 struct stat stat_buf = {0};
1019 if (ignore_missing == true) { 1044 if (stat(parameters->name, &stat_buf)) {
1045 if (verbose >= 3) {
1046 printf("stat failed on %s\n", parameters->name);
1047 }
1048 if (ignore_missing) {
1020 return false; 1049 return false;
1021 } 1050 }
1022 printf("DISK %s - ", _("CRITICAL")); 1051 printf("DISK %s - ", _("CRITICAL"));
1023 die(STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); 1052 die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"), strerror(errno));
1024 } 1053 }
1054
1025 return true; 1055 return true;
1026} 1056}
1027 1057
1028void get_stats(struct parameter_list *p, struct fs_usage *fsp) { 1058static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp, bool freespace_ignore_reserved) {
1029 struct parameter_list *p_list; 1059 uintmax_t available = fsp.fsu_bavail;
1030 struct fs_usage tmpfsp; 1060 uintmax_t available_to_root = fsp.fsu_bfree;
1031 int first = 1; 1061 uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree;
1062 uintmax_t total;
1032 1063
1033 if (p->group == NULL) {
1034 get_path_stats(p, fsp);
1035 } else {
1036 /* find all group members */
1037 for (p_list = path_select_list; p_list; p_list = p_list->name_next) {
1038#ifdef __CYGWIN__
1039 if (strncmp(p_list->name, "/cygdrive/", 10) != 0)
1040 continue;
1041#endif
1042 if (p_list->group && !(strcmp(p_list->group, p->group))) {
1043 if (!stat_path(p_list))
1044 continue;
1045 get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
1046 get_path_stats(p_list, &tmpfsp);
1047 if (verbose >= 3)
1048 printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n",
1049 p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units,
1050 p_list->dfree_units, p_list->dtotal_units, mult);
1051
1052 /* prevent counting the first FS of a group twice since its parameter_list entry
1053 * is used to carry the information of all file systems of the entire group */
1054 if (!first) {
1055 p->total += p_list->total;
1056 p->available += p_list->available;
1057 p->available_to_root += p_list->available_to_root;
1058 p->used += p_list->used;
1059
1060 p->dused_units += p_list->dused_units;
1061 p->dfree_units += p_list->dfree_units;
1062 p->dtotal_units += p_list->dtotal_units;
1063 p->inodes_total += p_list->inodes_total;
1064 p->inodes_free += p_list->inodes_free;
1065 p->inodes_free_to_root += p_list->inodes_free_to_root;
1066 p->inodes_used += p_list->inodes_used;
1067 }
1068 first = 0;
1069 }
1070 if (verbose >= 3)
1071 printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", p->group,
1072 p->dused_units, p->dfree_units, p->dtotal_units, tmpfsp.fsu_blocksize, mult);
1073 }
1074 /* modify devname and mountdir for output */
1075 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
1076 }
1077 /* finally calculate percentages for either plain FS or summed up group */
1078 p->dused_pct = calculate_percent(p->used, p->used + p->available); /* used + available can never be > uintmax */
1079 p->dfree_pct = 100.0 - p->dused_pct;
1080 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1081 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1082}
1083
1084void get_path_stats(struct parameter_list *p, struct fs_usage *fsp) {
1085 p->available = fsp->fsu_bavail;
1086 p->available_to_root = fsp->fsu_bfree;
1087 p->used = fsp->fsu_blocks - fsp->fsu_bfree;
1088 if (freespace_ignore_reserved) { 1064 if (freespace_ignore_reserved) {
1089 /* option activated : we subtract the root-reserved space from the total */ 1065 /* option activated : we subtract the root-reserved space from the total */
1090 p->total = fsp->fsu_blocks - p->available_to_root + p->available; 1066 total = fsp.fsu_blocks - available_to_root + available;
1091 } else { 1067 } else {
1092 /* default behaviour : take all the blocks into account */ 1068 /* default behaviour : take all the blocks into account */
1093 p->total = fsp->fsu_blocks; 1069 total = fsp.fsu_blocks;
1094 } 1070 }
1095 1071
1096 p->dused_units = p->used * fsp->fsu_blocksize / mult; 1072 parameters.used_bytes = used * fsp.fsu_blocksize;
1097 p->dfree_units = p->available * fsp->fsu_blocksize / mult; 1073 parameters.free_bytes = available * fsp.fsu_blocksize;
1098 p->dtotal_units = p->total * fsp->fsu_blocksize / mult; 1074 parameters.total_bytes = total * fsp.fsu_blocksize;
1075
1099 /* Free file nodes. Not sure the workaround is required, but in case...*/ 1076 /* Free file nodes. Not sure the workaround is required, but in case...*/
1100 p->inodes_free = fsp->fsu_ffree; 1077 parameters.inodes_free = fsp.fsu_ffree;
1101 p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ 1078 parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */
1102 p->inodes_used = fsp->fsu_files - fsp->fsu_ffree; 1079 parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree;
1080
1103 if (freespace_ignore_reserved) { 1081 if (freespace_ignore_reserved) {
1104 /* option activated : we subtract the root-reserved inodes from the total */ 1082 /* option activated : we subtract the root-reserved inodes from the total */
1105 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ 1083 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */
1106 /* for others, fsp->fsu_ffree == fsp->fsu_favail */ 1084 /* for others, fsp->fsu_ffree == fsp->fsu_favail */
1107 p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free; 1085 parameters.inodes_total = fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free;
1108 } else { 1086 } else {
1109 /* default behaviour : take all the inodes into account */ 1087 /* default behaviour : take all the inodes into account */
1110 p->inodes_total = fsp->fsu_files; 1088 parameters.inodes_total = fsp.fsu_files;
1089 }
1090
1091 return parameters;
1092}
1093
1094mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, byte_unit unit) {
1095 mp_subcheck result = mp_subcheck_init();
1096 result = mp_set_subcheck_default_state(result, STATE_UNKNOWN);
1097 xasprintf(&result.output, "%s", measurement_unit.name);
1098
1099 if (!measurement_unit.is_group && measurement_unit.filesystem_type) {
1100 xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type);
1111 } 1101 }
1112 np_add_name(&seen, p->best_match->me_mountdir); 1102
1103 /* Threshold comparisons */
1104
1105 // ===============================
1106 // Free space absolute values test
1107 mp_subcheck freespace_bytes_sc = mp_subcheck_init();
1108 freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK);
1109
1110 if (unit != Humanized) {
1111 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)", (uintmax_t)(measurement_unit.free_bytes / unit),
1112 get_unit_string(unit), (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit));
1113 } else {
1114 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)", humanize_byte_value(measurement_unit.free_bytes, false),
1115 humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false));
1116 }
1117
1118 mp_perfdata used_space = perfdata_init();
1119 used_space.label = measurement_unit.name;
1120 used_space.value = mp_create_pd_value(measurement_unit.free_bytes);
1121 used_space = mp_set_pd_max_value(used_space, mp_create_pd_value(measurement_unit.total_bytes));
1122 used_space = mp_set_pd_min_value(used_space, mp_create_pd_value(0));
1123 used_space.uom = "B";
1124 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1125 freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(used_space));
1126
1127 // special case for absolute space thresholds here:
1128 // if absolute values are not set, compute the thresholds from percentage thresholds
1129 mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds;
1130 if (!temp_thlds.critical_is_set && measurement_unit.freespace_percent_thresholds.critical_is_set) {
1131 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical;
1132
1133 if (!tmp_range.end_infinity) {
1134 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * measurement_unit.total_bytes);
1135 }
1136
1137 if (!tmp_range.start_infinity) {
1138 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * measurement_unit.total_bytes);
1139 }
1140 measurement_unit.freespace_bytes_thresholds = mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range);
1141 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1142 }
1143
1144 if (!temp_thlds.warning_is_set && measurement_unit.freespace_percent_thresholds.warning_is_set) {
1145 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning;
1146 if (!tmp_range.end_infinity) {
1147 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * measurement_unit.total_bytes);
1148 }
1149 if (!tmp_range.start_infinity) {
1150 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * measurement_unit.total_bytes);
1151 }
1152 measurement_unit.freespace_bytes_thresholds = mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range);
1153 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1154 }
1155
1156 mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space);
1157 mp_add_subcheck_to_subcheck(&result, freespace_bytes_sc);
1158
1159 // ==========================
1160 // Free space percentage test
1161 mp_subcheck freespace_percent_sc = mp_subcheck_init();
1162 freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK);
1163
1164 double free_percentage = calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes);
1165 xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage);
1166
1167 // Using perfdata here just to get to the test result
1168 mp_perfdata free_space_percent_pd = perfdata_init();
1169 free_space_percent_pd.value = mp_create_pd_value(free_percentage);
1170 free_space_percent_pd = mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds);
1171
1172 freespace_percent_sc = mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd));
1173 mp_add_subcheck_to_subcheck(&result, freespace_percent_sc);
1174
1175 // ================
1176 // Free inodes test
1177 // Only ever useful if the number of inodes is static (e.g. ext4),
1178 // not when it is dynamic (e.g btrfs)
1179 // Assumption: if the total number of inodes == 0, we have such a case and just skip the test
1180 if (measurement_unit.inodes_total > 0) {
1181 mp_subcheck freeindodes_percent_sc = mp_subcheck_init();
1182 freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK);
1183
1184 double free_inode_percentage = calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total);
1185
1186 if (verbose > 0) {
1187 printf("free inode percentage computed: %g\n", free_inode_percentage);
1188 }
1189
1190 xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)", free_inode_percentage, measurement_unit.inodes_free,
1191 measurement_unit.inodes_total);
1192
1193 mp_perfdata inodes_pd = perfdata_init();
1194 xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name);
1195 inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used);
1196 inodes_pd = mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total));
1197 inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0));
1198
1199 mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds;
1200
1201 if (absolut_inode_thresholds.critical_is_set) {
1202 absolut_inode_thresholds.critical =
1203 mp_range_multiply(absolut_inode_thresholds.critical, mp_create_pd_value(measurement_unit.inodes_total / 100));
1204 }
1205 if (absolut_inode_thresholds.warning_is_set) {
1206 absolut_inode_thresholds.warning =
1207 mp_range_multiply(absolut_inode_thresholds.warning, mp_create_pd_value(measurement_unit.inodes_total / 100));
1208 }
1209
1210 inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds);
1211
1212 freeindodes_percent_sc = mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd));
1213 if (display_inodes_perfdata) {
1214 mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd);
1215 }
1216 mp_add_subcheck_to_subcheck(&result, freeindodes_percent_sc);
1217 }
1218
1219 return result;
1113} 1220}
diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c
new file mode 100644
index 00000000..eec1282b
--- /dev/null
+++ b/plugins/check_disk.d/utils_disk.c
@@ -0,0 +1,517 @@
1/*****************************************************************************
2 *
3 * Library for check_disk
4 *
5 * License: GPL
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains utilities for check_disk. These are tested by libtap
11 *
12 *
13 * This program is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 *
27 *****************************************************************************/
28
29#include "../common.h"
30#include "utils_disk.h"
31#include "../../gl/fsusage.h"
32#include "../../lib/thresholds.h"
33#include "../../lib/states.h"
34#include <stdint.h>
35#include <stdio.h>
36#include <string.h>
37#include <assert.h>
38
39void np_add_name(struct name_list **list, const char *name) {
40 struct name_list *new_entry;
41 new_entry = (struct name_list *)malloc(sizeof *new_entry);
42 new_entry->name = (char *)name;
43 new_entry->next = *list;
44 *list = new_entry;
45}
46
47/* @brief Initialises a new regex at the begin of list via regcomp(3)
48 *
49 * @details if the regex fails to compile the error code of regcomp(3) is returned
50 * and list is not modified, otherwise list is modified to point to the new
51 * element
52 * @param list Pointer to a linked list of regex_list elements
53 * @param regex the string containing the regex which should be inserted into the list
54 * @param clags the cflags parameter for regcomp(3)
55 */
56int np_add_regex(struct regex_list **list, const char *regex, int cflags) {
57 struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry);
58
59 if (new_entry == NULL) {
60 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
61 }
62
63 int regcomp_result = regcomp(&new_entry->regex, regex, cflags);
64
65 if (!regcomp_result) {
66 // regcomp succeeded
67 new_entry->next = *list;
68 *list = new_entry;
69
70 return 0;
71 }
72 // regcomp failed
73 free(new_entry);
74
75 return regcomp_result;
76}
77
78parameter_list_elem parameter_list_init(const char *name) {
79 parameter_list_elem result = {
80 .name = strdup(name),
81 .best_match = NULL,
82
83 .freespace_units = mp_thresholds_init(),
84 .freespace_percent = mp_thresholds_init(),
85 .freeinodes_percent = mp_thresholds_init(),
86
87 .group = NULL,
88
89 .inodes_total = 0,
90 .inodes_free = 0,
91 .inodes_free_to_root = 0,
92 .inodes_used = 0,
93
94 .used_bytes = 0,
95 .free_bytes = 0,
96 .total_bytes = 0,
97
98 .next = NULL,
99 .prev = NULL,
100 };
101 return result;
102}
103
104/* Returns true if name is in list */
105bool np_find_name(struct name_list *list, const char *name) {
106 if (list == NULL || name == NULL) {
107 return false;
108 }
109 for (struct name_list *iterator = list; iterator; iterator = iterator->next) {
110 if (!strcmp(name, iterator->name)) {
111 return true;
112 }
113 }
114 return false;
115}
116
117/* Returns true if name is in list */
118bool np_find_regmatch(struct regex_list *list, const char *name) {
119 if (name == NULL) {
120 return false;
121 }
122
123 size_t len = strlen(name);
124
125 for (; list; list = list->next) {
126 /* Emulate a full match as if surrounded with ^( )$
127 by checking whether the match spans the whole name */
128 regmatch_t dummy_match;
129 if (!regexec(&list->regex, name, 1, &dummy_match, 0) && dummy_match.rm_so == 0 && dummy_match.rm_eo == len) {
130 return true;
131 }
132 }
133
134 return false;
135}
136
137bool np_seen_name(struct name_list *list, const char *name) {
138 for (struct name_list *iterator = list; iterator; iterator = iterator->next) {
139 if (!strcmp(iterator->name, name)) {
140 return true;
141 }
142 }
143 return false;
144}
145
146bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) {
147 return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) || (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0));
148}
149
150check_disk_config check_disk_config_init() {
151 check_disk_config tmp = {
152 .erronly = false,
153 .display_mntp = false,
154 .show_local_fs = false,
155 .stat_remote_fs = false,
156 .display_inodes_perfdata = false,
157
158 .exact_match = false,
159 .freespace_ignore_reserved = false,
160
161 .ignore_missing = false,
162 .path_ignored = false,
163
164 // FS Filters
165 .fs_exclude_list = NULL,
166 .fs_include_list = NULL,
167 .device_path_exclude_list = NULL,
168
169 // Actual filesystems paths to investigate
170 .path_select_list = filesystem_list_init(),
171
172 .mount_list = NULL,
173 .seen = NULL,
174
175 .display_unit = Humanized,
176 // .unit = MebiBytes,
177
178 .output_format_is_set = false,
179 };
180 return tmp;
181}
182
183char *get_unit_string(byte_unit_enum unit) {
184 switch (unit) {
185 case Bytes:
186 return "Bytes";
187 case KibiBytes:
188 return "KiB";
189 case MebiBytes:
190 return "MiB";
191 case GibiBytes:
192 return "GiB";
193 case TebiBytes:
194 return "TiB";
195 case PebiBytes:
196 return "PiB";
197 case ExbiBytes:
198 return "EiB";
199 case KiloBytes:
200 return "KB";
201 case MegaBytes:
202 return "MB";
203 case GigaBytes:
204 return "GB";
205 case TeraBytes:
206 return "TB";
207 case PetaBytes:
208 return "PB";
209 case ExaBytes:
210 return "EB";
211 default:
212 assert(false);
213 }
214}
215
216measurement_unit measurement_unit_init() {
217 measurement_unit tmp = {
218 .name = NULL,
219 .filesystem_type = NULL,
220 .is_group = false,
221
222 .freeinodes_percent_thresholds = mp_thresholds_init(),
223 .freespace_percent_thresholds = mp_thresholds_init(),
224 .freespace_bytes_thresholds = mp_thresholds_init(),
225
226 .free_bytes = 0,
227 .used_bytes = 0,
228 .total_bytes = 0,
229
230 .inodes_total = 0,
231 .inodes_free = 0,
232 .inodes_free_to_root = 0,
233 .inodes_used = 0,
234 };
235 return tmp;
236}
237
238// Add a given element to the list, memory for the new element is freshly allocated
239// Returns a pointer to new element
240measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem) {
241 // find last element
242 measurement_unit_list *new = NULL;
243 if (list == NULL) {
244 new = calloc(1, sizeof(measurement_unit_list));
245 if (new == NULL) {
246 die(STATE_UNKNOWN, _("allocation failed"));
247 }
248 } else {
249 measurement_unit_list *list_elem = list;
250 while (list_elem->next != NULL) {
251 list_elem = list_elem->next;
252 }
253
254 new = calloc(1, sizeof(measurement_unit_list));
255 if (new == NULL) {
256 die(STATE_UNKNOWN, _("allocation failed"));
257 }
258
259 list_elem->next = new;
260 }
261
262 new->unit = elem;
263 new->next = NULL;
264 return new;
265}
266
267measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, parameter_list_elem filesystem) {
268
269 unit.free_bytes += filesystem.free_bytes;
270 unit.used_bytes += filesystem.used_bytes;
271 unit.total_bytes += filesystem.total_bytes;
272
273 unit.inodes_total += filesystem.inodes_total;
274 unit.inodes_free += filesystem.inodes_free;
275 unit.inodes_free_to_root += filesystem.inodes_free_to_root;
276 unit.inodes_used += filesystem.inodes_used;
277 return unit;
278}
279
280measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, bool display_mntp) {
281 measurement_unit result = measurement_unit_init();
282 if (!display_mntp) {
283 result.name = strdup(filesystem.best_match->me_mountdir);
284 } else {
285 result.name = strdup(filesystem.best_match->me_devname);
286 }
287
288 if (filesystem.group) {
289 result.is_group = true;
290 } else {
291 result.is_group = false;
292 if (filesystem.best_match) {
293 result.filesystem_type = filesystem.best_match->me_type;
294 }
295 }
296
297 result.freeinodes_percent_thresholds = filesystem.freeinodes_percent;
298 result.freespace_percent_thresholds = filesystem.freespace_percent;
299 result.freespace_bytes_thresholds = filesystem.freespace_units;
300 result.free_bytes = filesystem.free_bytes;
301 result.total_bytes = filesystem.total_bytes;
302 result.used_bytes = filesystem.used_bytes;
303 result.inodes_total = filesystem.inodes_total;
304 result.inodes_used = filesystem.inodes_used;
305 result.inodes_free = filesystem.inodes_free;
306 result.inodes_free_to_root = filesystem.inodes_free_to_root;
307 return result;
308}
309
310#define RANDOM_STRING_LENGTH 64
311
312char *humanize_byte_value(unsigned long long value, bool use_si_units) {
313 // Idea: A reasonable output should have at most 3 orders of magnitude
314 // before the decimal separator
315 // 353GiB is ok, 2444 GiB should be 2.386 TiB
316 char *result = calloc(RANDOM_STRING_LENGTH, sizeof(char));
317 if (result == NULL) {
318 die(STATE_UNKNOWN, _("allocation failed"));
319 }
320 const byte_unit KibiBytes_factor = 1024;
321 const byte_unit MebiBytes_factor = 1048576;
322 const byte_unit GibiBytes_factor = 1073741824;
323 const byte_unit TebiBytes_factor = 1099511627776;
324 const byte_unit PebiBytes_factor = 1125899906842624;
325 const byte_unit ExbiBytes_factor = 1152921504606846976;
326 const byte_unit KiloBytes_factor = 1000;
327 const byte_unit MegaBytes_factor = 1000000;
328 const byte_unit GigaBytes_factor = 1000000000;
329 const byte_unit TeraBytes_factor = 1000000000000;
330 const byte_unit PetaBytes_factor = 1000000000000000;
331 const byte_unit ExaBytes_factor = 1000000000000000000;
332
333 if (use_si_units) {
334 // SI units, powers of 10
335 if (value < KiloBytes_factor) {
336 snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value);
337 } else if (value < MegaBytes_factor) {
338 snprintf(result, RANDOM_STRING_LENGTH, "%llu KB", value / KiloBytes_factor);
339 } else if (value < GigaBytes_factor) {
340 snprintf(result, RANDOM_STRING_LENGTH, "%llu MB", value / MegaBytes_factor);
341 } else if (value < TeraBytes_factor) {
342 snprintf(result, RANDOM_STRING_LENGTH, "%llu GB", value / GigaBytes_factor);
343 } else if (value < PetaBytes_factor) {
344 snprintf(result, RANDOM_STRING_LENGTH, "%llu TB", value / TeraBytes_factor);
345 } else if (value < ExaBytes_factor) {
346 snprintf(result, RANDOM_STRING_LENGTH, "%llu PB", value / PetaBytes_factor);
347 } else {
348 snprintf(result, RANDOM_STRING_LENGTH, "%llu EB", value / ExaBytes_factor);
349 }
350 } else {
351 // IEC units, powers of 2 ^ 10
352 if (value < KibiBytes_factor) {
353 snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value);
354 } else if (value < MebiBytes_factor) {
355 snprintf(result, RANDOM_STRING_LENGTH, "%llu KiB", value / KibiBytes_factor);
356 } else if (value < GibiBytes_factor) {
357 snprintf(result, RANDOM_STRING_LENGTH, "%llu MiB", value / MebiBytes_factor);
358 } else if (value < TebiBytes_factor) {
359 snprintf(result, RANDOM_STRING_LENGTH, "%llu GiB", value / GibiBytes_factor);
360 } else if (value < PebiBytes_factor) {
361 snprintf(result, RANDOM_STRING_LENGTH, "%llu TiB", value / TebiBytes_factor);
362 } else if (value < ExbiBytes_factor) {
363 snprintf(result, RANDOM_STRING_LENGTH, "%llu PiB", value / PebiBytes_factor);
364 } else {
365 snprintf(result, RANDOM_STRING_LENGTH, "%llu EiB", value / ExbiBytes_factor);
366 }
367 }
368
369 return result;
370}
371
372filesystem_list filesystem_list_init() {
373 filesystem_list tmp = {
374 .length = 0,
375 .first = NULL,
376 };
377 return tmp;
378}
379
380parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name) {
381 parameter_list_elem *current = list->first;
382 parameter_list_elem *new_path = (struct parameter_list *)malloc(sizeof *new_path);
383 *new_path = parameter_list_init(name);
384
385 if (current == NULL) {
386 list->first = new_path;
387 new_path->prev = NULL;
388 list->length = 1;
389 } else {
390 while (current->next) {
391 current = current->next;
392 }
393 current->next = new_path;
394 new_path->prev = current;
395 list->length++;
396 }
397 return new_path;
398}
399
400parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name) {
401 if (list.length == 0) {
402 return NULL;
403 }
404
405 for (parameter_list_elem *temp_list = list.first; temp_list; temp_list = temp_list->next) {
406 if (!strcmp(temp_list->name, name)) {
407 return temp_list;
408 }
409 }
410
411 return NULL;
412}
413
414parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item) {
415 if (list->length == 0) {
416 return NULL;
417 }
418
419 if (item == NULL) {
420 // Got NULL for item, interpret this as "delete first element"
421 // as a kind of compatibility to the old function
422 item = list->first;
423 }
424
425 if (list->first == item) {
426 list->length--;
427
428 list->first = item->next;
429 if (list->first) {
430 list->first->prev = NULL;
431 }
432 return list->first;
433 }
434
435 // Was not the first element, continue
436 parameter_list_elem *prev = list->first;
437 parameter_list_elem *current = list->first->next;
438
439 while (current != item && current != NULL) {
440 prev = current;
441 current = current->next;
442 }
443
444 if (current == NULL) {
445 // didn't find that element ....
446 return NULL;
447 }
448
449 // remove the element
450 parameter_list_elem *next = current->next;
451 prev->next = next;
452 list->length--;
453 if (next) {
454 next->prev = prev;
455 }
456
457 if (item->name) {
458 free(item->name);
459 }
460 free(item);
461
462 return next;
463}
464
465parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current) {
466 if (!current) {
467 return NULL;
468 }
469 return current->next;
470}
471
472void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, bool exact) {
473 for (parameter_list_elem *elem = list.first; elem; elem = mp_int_fs_list_get_next(elem)) {
474 if (!elem->best_match) {
475 size_t name_len = strlen(elem->name);
476 struct mount_entry *best_match = NULL;
477
478 /* set best match if path name exactly matches a mounted device name */
479 for (struct mount_entry *mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) {
480 if (strcmp(mount_entry->me_devname, elem->name) == 0) {
481 struct fs_usage fsp;
482 if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) {
483 best_match = mount_entry;
484 }
485 }
486 }
487
488 /* set best match by directory name if no match was found by devname */
489 if (!best_match) {
490 size_t best_match_len = 0;
491 for (struct mount_entry *mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) {
492 size_t len = strlen(mount_entry->me_mountdir);
493
494 if ((!exact && (best_match_len <= len && len <= name_len &&
495 (len == 1 || strncmp(mount_entry->me_mountdir, elem->name, len) == 0))) ||
496 (exact && strcmp(mount_entry->me_mountdir, elem->name) == 0)) {
497 struct fs_usage fsp;
498
499 if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) {
500 best_match = mount_entry;
501 best_match_len = len;
502 }
503 }
504 }
505 }
506
507 if (best_match) {
508 elem->best_match = best_match;
509 } else {
510 elem->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */
511 }
512
513 // No filesystem without a mount_entry!
514 // assert(elem->best_match != NULL);
515 }
516 }
517}
diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h
new file mode 100644
index 00000000..6831d1fd
--- /dev/null
+++ b/plugins/check_disk.d/utils_disk.h
@@ -0,0 +1,157 @@
1#pragma once
2/* Header file for utils_disk */
3
4#include "../../config.h"
5#include "../../gl/mountlist.h"
6#include "../../lib/utils_base.h"
7#include "../../lib/output.h"
8#include "regex.h"
9#include <stdint.h>
10
11typedef unsigned long long byte_unit;
12
13typedef enum {
14 Humanized,
15 Bytes,
16 KibiBytes,
17 MebiBytes,
18 GibiBytes,
19 TebiBytes,
20 PebiBytes,
21 ExbiBytes,
22 KiloBytes,
23 MegaBytes,
24 GigaBytes,
25 TeraBytes,
26 PetaBytes,
27 ExaBytes,
28} byte_unit_enum;
29
30typedef struct name_list string_list;
31struct name_list {
32 char *name;
33 string_list *next;
34};
35
36struct regex_list {
37 regex_t regex;
38 struct regex_list *next;
39};
40
41typedef struct parameter_list parameter_list_elem;
42struct parameter_list {
43 char *name;
44 char *group;
45
46 mp_thresholds freespace_units;
47 mp_thresholds freespace_percent;
48 mp_thresholds freeinodes_percent;
49
50 struct mount_entry *best_match;
51
52 uintmax_t inodes_free_to_root;
53 uintmax_t inodes_free;
54 uintmax_t inodes_used;
55 uintmax_t inodes_total;
56
57 uint64_t used_bytes;
58 uint64_t free_bytes;
59 uint64_t total_bytes;
60
61 parameter_list_elem *next;
62 parameter_list_elem *prev;
63};
64
65typedef struct {
66 size_t length;
67 parameter_list_elem *first;
68} filesystem_list;
69
70filesystem_list filesystem_list_init();
71
72typedef struct {
73 char *name;
74 char *filesystem_type;
75 bool is_group;
76
77 mp_thresholds freespace_bytes_thresholds;
78 mp_thresholds freespace_percent_thresholds;
79 mp_thresholds freeinodes_percent_thresholds;
80
81 uintmax_t inodes_free_to_root;
82 uintmax_t inodes_free;
83 uintmax_t inodes_used;
84 uintmax_t inodes_total;
85
86 uintmax_t used_bytes;
87 uintmax_t free_bytes;
88 uintmax_t total_bytes;
89} measurement_unit;
90
91typedef struct measurement_unit_list measurement_unit_list;
92struct measurement_unit_list {
93 measurement_unit unit;
94 measurement_unit_list *next;
95};
96
97typedef struct {
98 // Output options
99 bool erronly;
100 bool display_mntp;
101 /* show only local filesystems. */
102 bool show_local_fs;
103 /* show only local filesystems but call stat() on remote ones. */
104 bool stat_remote_fs;
105 bool display_inodes_perfdata;
106
107 bool exact_match;
108 bool freespace_ignore_reserved;
109
110 bool ignore_missing;
111 bool path_ignored;
112
113 /* Linked list of filesystem types to omit.
114 If the list is empty, don't exclude any types. */
115 struct regex_list *fs_exclude_list;
116 /* Linked list of filesystem types to check.
117 If the list is empty, include all types. */
118 struct regex_list *fs_include_list;
119 struct name_list *device_path_exclude_list;
120 filesystem_list path_select_list;
121 /* Linked list of mounted filesystems. */
122 struct mount_entry *mount_list;
123 struct name_list *seen;
124
125 byte_unit_enum display_unit;
126 // byte_unit unit;
127
128 bool output_format_is_set;
129 mp_output_format output_format;
130} check_disk_config;
131
132void np_add_name(struct name_list **list, const char *name);
133bool np_find_name(struct name_list *list, const char *name);
134bool np_seen_name(struct name_list *list, const char *name);
135int np_add_regex(struct regex_list **list, const char *regex, int cflags);
136bool np_find_regmatch(struct regex_list *list, const char *name);
137
138parameter_list_elem parameter_list_init(const char *);
139
140parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name);
141parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name);
142parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item);
143parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current);
144void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, bool exact);
145
146measurement_unit measurement_unit_init();
147measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem);
148measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, parameter_list_elem filesystem);
149measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, bool display_mntp);
150
151int search_parameter_list(parameter_list_elem *list, const char *name);
152bool np_regex_match_mount_entry(struct mount_entry *, regex_t *);
153
154char *get_unit_string(byte_unit_enum);
155check_disk_config check_disk_config_init();
156
157char *humanize_byte_value(unsigned long long value, bool use_si_units);
diff --git a/plugins/check_fping.c b/plugins/check_fping.c
index ec7abb67..8018e06d 100644
--- a/plugins/check_fping.c
+++ b/plugins/check_fping.c
@@ -79,6 +79,24 @@ int main(int argc, char **argv) {
79 server = strscpy(server, config.server_name); 79 server = strscpy(server, config.server_name);
80 80
81 char *option_string = ""; 81 char *option_string = "";
82 char *fping_prog = NULL;
83
84 /* First determine if the target is dualstack or ipv6 only. */
85 bool server_is_inet6_addr = is_inet6_addr(server);
86
87 /*
88 * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack
89 * -> we use ipv6
90 * If the user requested -4 OR the user made no assertion and the address is v4 ONLY
91 * -> we use ipv4
92 */
93 if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) {
94 xasprintf(&option_string, "%s-6 ", option_string);
95 } else {
96 xasprintf(&option_string, "%s-4 ", option_string);
97 }
98 fping_prog = strdup(PATH_TO_FPING);
99
82 /* compose the command */ 100 /* compose the command */
83 if (config.target_timeout) { 101 if (config.target_timeout) {
84 xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout); 102 xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout);
@@ -99,19 +117,26 @@ int main(int argc, char **argv) {
99 xasprintf(&option_string, "%s-R ", option_string); 117 xasprintf(&option_string, "%s-R ", option_string);
100 } 118 }
101 119
102 char *fping_prog = NULL; 120 if (config.fwmark_set) {
103#ifdef PATH_TO_FPING6 121 xasprintf(&option_string, "%s--fwmark %u ", option_string, config.fwmark);
104 if (address_family != AF_INET && is_inet6_addr(server)) { 122 }
105 fping_prog = strdup(PATH_TO_FPING6); 123
106 } else { 124 if (config.icmp_timestamp) {
107 fping_prog = strdup(PATH_TO_FPING); 125 xasprintf(&option_string, "%s--icmp-timestamp ", option_string);
126 }
127
128 if (config.check_source) {
129 xasprintf(&option_string, "%s--check-source ", option_string);
108 } 130 }
109#else
110 fping_prog = strdup(PATH_TO_FPING);
111#endif
112 131
113 char *command_line = NULL; 132 char *command_line = NULL;
114 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server); 133
134 if (config.icmp_timestamp) {
135 // no packet size settable for ICMP timestamp
136 xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count, server);
137 } else {
138 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server);
139 }
115 140
116 if (verbose) { 141 if (verbose) {
117 printf("%s\n", command_line); 142 printf("%s\n", command_line);
@@ -268,13 +293,38 @@ mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double c
268 293
269/* process command-line arguments */ 294/* process command-line arguments */
270check_fping_config_wrapper process_arguments(int argc, char **argv) { 295check_fping_config_wrapper process_arguments(int argc, char **argv) {
271 static struct option longopts[] = { 296 enum {
272 {"hostname", required_argument, 0, 'H'}, {"sourceip", required_argument, 0, 'S'}, {"sourceif", required_argument, 0, 'I'}, 297 FWMARK_OPT = CHAR_MAX + 1,
273 {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, {"alive", no_argument, 0, 'a'}, 298 ICMP_TIMESTAMP_OPT,
274 {"bytes", required_argument, 0, 'b'}, {"number", required_argument, 0, 'n'}, {"target-timeout", required_argument, 0, 'T'}, 299 CHECK_SOURCE_OPT,
275 {"interval", required_argument, 0, 'i'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 300 };
276 {"help", no_argument, 0, 'h'}, {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, 301 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
277 {"dontfrag", no_argument, 0, 'M'}, {"random", no_argument, 0, 'R'}, {0, 0, 0, 0}}; 302 {"sourceip", required_argument, 0, 'S'},
303 {"sourceif", required_argument, 0, 'I'},
304 {"critical", required_argument, 0, 'c'},
305 {"warning", required_argument, 0, 'w'},
306 {"alive", no_argument, 0, 'a'},
307 {"bytes", required_argument, 0, 'b'},
308 {"number", required_argument, 0, 'n'},
309 {"target-timeout", required_argument, 0, 'T'},
310 {"interval", required_argument, 0, 'i'},
311 {"verbose", no_argument, 0, 'v'},
312 {"version", no_argument, 0, 'V'},
313 {"help", no_argument, 0, 'h'},
314 {"use-ipv4", no_argument, 0, '4'},
315 {"use-ipv6", no_argument, 0, '6'},
316 {"dontfrag", no_argument, 0, 'M'},
317 {"random", no_argument, 0, 'R'},
318#ifdef FPING_VERSION_5_2_OR_HIGHER
319 // only available with fping version >= 5.2
320 {"fwmark", required_argument, NULL, FWMARK_OPT},
321# ifdef FPING_VERSION_5_3_OR_HIGHER
322 // only available with fping version >= 5.3
323 {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT},
324 {"check-source", no_argument, NULL, CHECK_SOURCE_OPT},
325# endif
326#endif
327 {0, 0, 0, 0}};
278 328
279 char *rv[2]; 329 char *rv[2];
280 rv[PL] = NULL; 330 rv[PL] = NULL;
@@ -299,7 +349,7 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) {
299 argc--; 349 argc--;
300 } 350 }
301 351
302 while (1) { 352 while (true) {
303 int option_index = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); 353 int option_index = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option);
304 354
305 if (option_index == -1 || option_index == EOF || option_index == 1) { 355 if (option_index == -1 || option_index == EOF || option_index == 1) {
@@ -340,11 +390,7 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) {
340 address_family = AF_INET; 390 address_family = AF_INET;
341 break; 391 break;
342 case '6': /* IPv6 only */ 392 case '6': /* IPv6 only */
343#ifdef USE_IPV6
344 address_family = AF_INET6; 393 address_family = AF_INET6;
345#else
346 usage(_("IPv6 support not available\n"));
347#endif
348 break; 394 break;
349 case 'c': 395 case 'c':
350 get_threshold(optarg, rv); 396 get_threshold(optarg, rv);
@@ -406,6 +452,20 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) {
406 case 'M': 452 case 'M':
407 result.config.dontfrag = true; 453 result.config.dontfrag = true;
408 break; 454 break;
455 case FWMARK_OPT:
456 if (is_intpos(optarg)) {
457 result.config.fwmark = (unsigned int)atol(optarg);
458 result.config.fwmark_set = true;
459 } else {
460 usage(_("fwmark must be a positive integer"));
461 }
462 break;
463 case ICMP_TIMESTAMP_OPT:
464 result.config.icmp_timestamp = true;
465 break;
466 case CHECK_SOURCE_OPT:
467 result.config.check_source = true;
468 break;
409 } 469 }
410 } 470 }
411 471
@@ -493,6 +553,16 @@ void print_help(void) {
493 printf(" %s\n", _("set the Don't Fragment flag")); 553 printf(" %s\n", _("set the Don't Fragment flag"));
494 printf(" %s\n", "-R, --random"); 554 printf(" %s\n", "-R, --random");
495 printf(" %s\n", _("random packet data (to foil link data compression)")); 555 printf(" %s\n", _("random packet data (to foil link data compression)"));
556#ifdef FPING_VERSION_5_2_OR_HIGHER
557 printf(" %s\n", "--fwmark=INTEGER");
558 printf(" %s\n", _("set the routing mark to INTEGER (fping option)"));
559# ifdef FPING_VERSION_5_3_OR_HIGHER
560 printf(" %s\n", "--icmp-timestamp");
561 printf(" %s\n", _("use ICMP Timestamp instead of ICMP Echo (fping option)"));
562 printf(" %s\n", "--check-source");
563 printf(" %s\n", _("discard replies not from target address (fping option)"));
564# endif
565#endif
496 printf(UT_VERBOSE); 566 printf(UT_VERBOSE);
497 printf("\n"); 567 printf("\n");
498 printf(" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)")); 568 printf(" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)"));
diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h
index a0697bf3..d95e9ded 100644
--- a/plugins/check_fping.d/config.h
+++ b/plugins/check_fping.d/config.h
@@ -29,6 +29,21 @@ typedef struct {
29 bool cpl_p; 29 bool cpl_p;
30 int wpl; 30 int wpl;
31 bool wpl_p; 31 bool wpl_p;
32
33 // only available with fping version >= 5.2
34 // for a given uint _fwmark_ fping sets _fwmark_ as a firewall mark
35 // in the packets
36 unsigned int fwmark;
37 bool fwmark_set;
38
39
40 // only available with fping version >= 5.3
41 // Setting icmp_timestamp tells fping to use ICMP Timestamp (ICMP type 13) instead
42 // of ICMP Echo
43 bool icmp_timestamp;
44
45 // Setting check_source lets fping discard replies which are not from the target address
46 bool check_source;
32} check_fping_config; 47} check_fping_config;
33 48
34check_fping_config check_fping_config_init() { 49check_fping_config check_fping_config_init() {
@@ -53,6 +68,15 @@ check_fping_config check_fping_config_init() {
53 .cpl_p = false, 68 .cpl_p = false,
54 .wpl = 0, 69 .wpl = 0,
55 .wpl_p = false, 70 .wpl_p = false,
71
72 // only available with fping version >= 5.2
73 .fwmark = 0,
74 .fwmark_set = false, // just to be deterministic
75
76 // only available with fping version >= 5.3
77 .icmp_timestamp = false,
78 .check_source = false,
79
56 }; 80 };
57 return tmp; 81 return tmp;
58} 82}
diff --git a/plugins/check_http.c b/plugins/check_http.c
index baff682a..8e0c15ec 100644
--- a/plugins/check_http.c
+++ b/plugins/check_http.c
@@ -1724,6 +1724,16 @@ print_help (void)
1724 printf ("%s\n", _("strings and regular expressions, check connection times, and report on")); 1724 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1725 printf ("%s\n", _("certificate expiration times.")); 1725 printf ("%s\n", _("certificate expiration times."));
1726 1726
1727 printf ("\n");
1728 printf ("%s\n", _("ATTENTION!"));
1729 printf ("\n");
1730 printf ("%s\n", _("THIS PLUGIN IS DEPRECATED. The functionality was reimplemented by the"));
1731 printf ("%s\n", _("check_curl plugin, which can be used as a drop-in replacement. You should"));
1732 printf ("%s\n", _("migrate your checks over to check_curl, because check_http is going to be"));
1733 printf ("%s\n", _("removed sooner than later. Just replace check_http with check_curl in your"));
1734 printf ("%s\n", _("check command definitions."));
1735 printf ("%s\n", _("Report issues to: https://github.com/monitoring-plugins/monitoring-plugins/issues"));
1736
1727 printf ("\n\n"); 1737 printf ("\n\n");
1728 1738
1729 print_usage (); 1739 print_usage ();
diff --git a/plugins/check_ide_smart.c b/plugins/check_ide_smart.c
index 9640ef70..16fe3d01 100644
--- a/plugins/check_ide_smart.c
+++ b/plugins/check_ide_smart.c
@@ -56,7 +56,6 @@ void print_usage(void);
56# include <sys/device.h> 56# include <sys/device.h>
57# include <sys/param.h> 57# include <sys/param.h>
58# include <sys/sysctl.h> 58# include <sys/sysctl.h>
59# include <sys/videoio.h> /* for __u8 and friends */
60# include <sys/scsiio.h> 59# include <sys/scsiio.h>
61# include <sys/ataio.h> 60# include <sys/ataio.h>
62# include <dev/ata/atareg.h> 61# include <dev/ata/atareg.h>
@@ -79,48 +78,47 @@ void print_usage(void);
79#define UNKNOWN -1 78#define UNKNOWN -1
80 79
81typedef struct threshold_s { 80typedef struct threshold_s {
82 __u8 id; 81 uint8_t id;
83 __u8 threshold; 82 uint8_t threshold;
84 __u8 reserved[10]; 83 uint8_t reserved[10];
85} __attribute__((packed)) threshold_t; 84} __attribute__((packed)) threshold_t;
86 85
87typedef struct thresholds_s { 86typedef struct thresholds_s {
88 __u16 revision; 87 uint16_t revision;
89 threshold_t thresholds[NR_ATTRIBUTES]; 88 threshold_t thresholds[NR_ATTRIBUTES];
90 __u8 reserved[18]; 89 uint8_t reserved[18];
91 __u8 vendor[131]; 90 uint8_t vendor[131];
92 __u8 checksum; 91 uint8_t checksum;
93} __attribute__((packed)) thresholds_t; 92} __attribute__((packed)) thresholds_t;
94 93
95typedef struct value_s { 94typedef struct value_s {
96 __u8 id; 95 uint8_t id;
97 __u16 status; 96 uint16_t status;
98 __u8 value; 97 uint8_t value;
99 __u8 vendor[8]; 98 uint8_t vendor[8];
100} __attribute__((packed)) value_t; 99} __attribute__((packed)) value_t;
101 100
102typedef struct values_s { 101typedef struct values_s {
103 __u16 revision; 102 uint16_t revision;
104 value_t values[NR_ATTRIBUTES]; 103 value_t values[NR_ATTRIBUTES];
105 __u8 offline_status; 104 uint8_t offline_status;
106 __u8 vendor1; 105 uint8_t vendor1;
107 __u16 offline_timeout; 106 uint16_t offline_timeout;
108 __u8 vendor2; 107 uint8_t vendor2;
109 __u8 offline_capability; 108 uint8_t offline_capability;
110 __u16 smart_capability; 109 uint16_t smart_capability;
111 __u8 reserved[16]; 110 uint8_t reserved[16];
112 __u8 vendor[125]; 111 uint8_t vendor[125];
113 __u8 checksum; 112 uint8_t checksum;
114} __attribute__((packed)) values_t; 113} __attribute__((packed)) values_t;
115 114
116static struct { 115static struct {
117 __u8 value; 116 uint8_t value;
118 char *text; 117 char *text;
119} offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, 118} offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
120 {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
121 119
122static struct { 120static struct {
123 __u8 value; 121 uint8_t value;
124 char *text; 122 char *text;
125} smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"}, 123} smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"},
126 {SMART_DISABLE, "SMART_DISABLE"}, 124 {SMART_DISABLE, "SMART_DISABLE"},
@@ -140,7 +138,7 @@ static int smart_read_values(int, values_t *);
140static int nagios(values_t *, thresholds_t *); 138static int nagios(values_t *, thresholds_t *);
141static void print_value(value_t *, threshold_t *); 139static void print_value(value_t *, threshold_t *);
142static void print_values(values_t *, thresholds_t *); 140static void print_values(values_t *, thresholds_t *);
143static int smart_cmd_simple(int, enum SmartCommand, __u8, bool); 141static int smart_cmd_simple(int, enum SmartCommand, uint8_t, bool);
144static int smart_read_thresholds(int, thresholds_t *); 142static int smart_read_thresholds(int, thresholds_t *);
145static bool verbose = false; 143static bool verbose = false;
146 144
@@ -175,8 +173,9 @@ int main(int argc, char *argv[]) {
175 173
176 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex); 174 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex);
177 175
178 if (o == -1 || o == EOF || o == 1) 176 if (o == -1 || o == EOF || o == 1) {
179 break; 177 break;
178 }
180 179
181 switch (o) { 180 switch (o) {
182 case 'd': 181 case 'd':
@@ -234,8 +233,9 @@ int main(int argc, char *argv[]) {
234 smart_read_values(fd, &values); 233 smart_read_values(fd, &values);
235 smart_read_thresholds(fd, &thresholds); 234 smart_read_thresholds(fd, &thresholds);
236 retval = nagios(&values, &thresholds); 235 retval = nagios(&values, &thresholds);
237 if (verbose) 236 if (verbose) {
238 print_values(&values, &thresholds); 237 print_values(&values, &thresholds);
238 }
239 239
240 close(fd); 240 close(fd);
241 return retval; 241 return retval;
@@ -254,7 +254,7 @@ char *get_offline_text(int status) {
254int smart_read_values(int fd, values_t *values) { 254int smart_read_values(int fd, values_t *values) {
255#ifdef __linux__ 255#ifdef __linux__
256 int e; 256 int e;
257 __u8 args[4 + 512]; 257 uint8_t args[4 + 512];
258 args[0] = WIN_SMART; 258 args[0] = WIN_SMART;
259 args[1] = 0; 259 args[1] = 0;
260 args[2] = SMART_READ_VALUES; 260 args[2] = SMART_READ_VALUES;
@@ -282,8 +282,9 @@ int smart_read_values(int fd, values_t *values) {
282 req.cylinder = WDSMART_CYL; 282 req.cylinder = WDSMART_CYL;
283 283
284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
285 if (req.retsts != ATACMD_OK) 285 if (req.retsts != ATACMD_OK) {
286 errno = ENODEV; 286 errno = ENODEV;
287 }
287 } 288 }
288 289
289 if (errno != 0) { 290 if (errno != 0) {
@@ -370,22 +371,24 @@ void print_values(values_t *p, thresholds_t *t) {
370 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : ""); 371 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : "");
371} 372}
372 373
373int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_error) { 374int smart_cmd_simple(int fd, enum SmartCommand command, uint8_t val0, bool show_error) {
374 int e = STATE_UNKNOWN; 375 int e = STATE_UNKNOWN;
375#ifdef __linux__ 376#ifdef __linux__
376 __u8 args[4]; 377 uint8_t args[4];
377 args[0] = WIN_SMART; 378 args[0] = WIN_SMART;
378 args[1] = val0; 379 args[1] = val0;
379 args[2] = smart_command[command].value; 380 args[2] = smart_command[command].value;
380 args[3] = 0; 381 args[3] = 0;
381 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { 382 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
382 e = STATE_CRITICAL; 383 e = STATE_CRITICAL;
383 if (show_error) 384 if (show_error) {
384 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); 385 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
386 }
385 } else { 387 } else {
386 e = STATE_OK; 388 e = STATE_OK;
387 if (show_error) 389 if (show_error) {
388 printf(_("OK - Command sent (%s)\n"), smart_command[command].text); 390 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
391 }
389 } 392 }
390 393
391#endif /* __linux__ */ 394#endif /* __linux__ */
@@ -401,20 +404,24 @@ int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_err
401 req.sec_count = val0; 404 req.sec_count = val0;
402 405
403 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 406 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
404 if (req.retsts != ATACMD_OK) 407 if (req.retsts != ATACMD_OK) {
405 errno = ENODEV; 408 errno = ENODEV;
406 if (req.cylinder != WDSMART_CYL) 409 }
410 if (req.cylinder != WDSMART_CYL) {
407 errno = ENODEV; 411 errno = ENODEV;
412 }
408 } 413 }
409 414
410 if (errno != 0) { 415 if (errno != 0) {
411 e = STATE_CRITICAL; 416 e = STATE_CRITICAL;
412 if (show_error) 417 if (show_error) {
413 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); 418 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
419 }
414 } else { 420 } else {
415 e = STATE_OK; 421 e = STATE_OK;
416 if (show_error) 422 if (show_error) {
417 printf(_("OK - Command sent (%s)\n"), smart_command[command].text); 423 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
424 }
418 } 425 }
419 426
420#endif /* __NetBSD__ */ 427#endif /* __NetBSD__ */
@@ -424,7 +431,7 @@ int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_err
424int smart_read_thresholds(int fd, thresholds_t *thresholds) { 431int smart_read_thresholds(int fd, thresholds_t *thresholds) {
425#ifdef __linux__ 432#ifdef __linux__
426 int e; 433 int e;
427 __u8 args[4 + 512]; 434 uint8_t args[4 + 512];
428 args[0] = WIN_SMART; 435 args[0] = WIN_SMART;
429 args[1] = 0; 436 args[1] = 0;
430 args[2] = SMART_READ_THRESHOLDS; 437 args[2] = SMART_READ_THRESHOLDS;
@@ -452,8 +459,9 @@ int smart_read_thresholds(int fd, thresholds_t *thresholds) {
452 req.cylinder = WDSMART_CYL; 459 req.cylinder = WDSMART_CYL;
453 460
454 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 461 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
455 if (req.retsts != ATACMD_OK) 462 if (req.retsts != ATACMD_OK) {
456 errno = ENODEV; 463 errno = ENODEV;
464 }
457 } 465 }
458 466
459 if (errno != 0) { 467 if (errno != 0) {
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index f99e5032..6e76bf23 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -35,6 +35,7 @@
35 * 35 *
36 *****************************************************************************/ 36 *****************************************************************************/
37 37
38#include "thresholds.h"
38const char *progname = "check_ntp_peer"; 39const char *progname = "check_ntp_peer";
39const char *copyright = "2006-2024"; 40const char *copyright = "2006-2024";
40const char *email = "devel@monitoring-plugins.org"; 41const char *email = "devel@monitoring-plugins.org";
@@ -42,30 +43,18 @@ const char *email = "devel@monitoring-plugins.org";
42#include "common.h" 43#include "common.h"
43#include "netutils.h" 44#include "netutils.h"
44#include "utils.h" 45#include "utils.h"
46#include "../lib/states.h"
47#include "check_ntp_peer.d/config.h"
45 48
46static char *server_address = NULL;
47static int port = 123;
48static int verbose = 0; 49static int verbose = 0;
49static bool quiet = false;
50static char *owarn = "60";
51static char *ocrit = "120";
52static bool do_stratum = false;
53static char *swarn = "-1:16";
54static char *scrit = "-1:16";
55static bool do_jitter = false;
56static char *jwarn = "-1:5000";
57static char *jcrit = "-1:10000";
58static bool do_truechimers = false;
59static char *twarn = "0:";
60static char *tcrit = "0:";
61static bool syncsource_found = false; 50static bool syncsource_found = false;
62static bool li_alarm = false; 51static bool li_alarm = false;
63 52
64static int process_arguments(int /*argc*/, char ** /*argv*/); 53typedef struct {
65static thresholds *offset_thresholds = NULL; 54 int errorcode;
66static thresholds *jitter_thresholds = NULL; 55 check_ntp_peer_config config;
67static thresholds *stratum_thresholds = NULL; 56} check_ntp_peer_config_wrapper;
68static thresholds *truechimer_thresholds = NULL; 57static check_ntp_peer_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
69static void print_help(void); 58static void print_help(void);
70void print_usage(void); 59void print_usage(void);
71 60
@@ -157,25 +146,25 @@ typedef struct {
157 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ 146 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
158 } while (0); 147 } while (0);
159 148
160void print_ntp_control_message(const ntp_control_message *p) { 149void print_ntp_control_message(const ntp_control_message *message) {
161 printf("control packet contents:\n"); 150 printf("control packet contents:\n");
162 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); 151 printf("\tflags: 0x%.2x , 0x%.2x\n", message->flags, message->op);
163 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); 152 printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK);
164 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); 153 printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK);
165 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); 154 printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK);
166 printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP); 155 printf("\t response=%d (0x%.2x)\n", (message->op & REM_RESP) > 0, message->op & REM_RESP);
167 printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE); 156 printf("\t more=%d (0x%.2x)\n", (message->op & REM_MORE) > 0, message->op & REM_MORE);
168 printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR); 157 printf("\t error=%d (0x%.2x)\n", (message->op & REM_ERROR) > 0, message->op & REM_ERROR);
169 printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK); 158 printf("\t op=%d (0x%.2x)\n", message->op & OP_MASK, message->op & OP_MASK);
170 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); 159 printf("\tsequence: %d (0x%.2x)\n", ntohs(message->seq), ntohs(message->seq));
171 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); 160 printf("\tstatus: %d (0x%.2x)\n", ntohs(message->status), ntohs(message->status));
172 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); 161 printf("\tassoc: %d (0x%.2x)\n", ntohs(message->assoc), ntohs(message->assoc));
173 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); 162 printf("\toffset: %d (0x%.2x)\n", ntohs(message->offset), ntohs(message->offset));
174 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); 163 printf("\tcount: %d (0x%.2x)\n", ntohs(message->count), ntohs(message->count));
175 164
176 int numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair)); 165 int numpeers = ntohs(message->count) / (sizeof(ntp_assoc_status_pair));
177 if (p->op & REM_RESP && p->op & OP_READSTAT) { 166 if (message->op & REM_RESP && message->op & OP_READSTAT) {
178 const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)p->data; 167 const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)message->data;
179 for (int i = 0; i < numpeers; i++) { 168 for (int i = 0; i < numpeers; i++) {
180 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status)); 169 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
181 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) { 170 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
@@ -190,13 +179,13 @@ void print_ntp_control_message(const ntp_control_message *p) {
190 } 179 }
191} 180}
192 181
193void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) { 182void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_t seq) {
194 memset(p, 0, sizeof(ntp_control_message)); 183 memset(message, 0, sizeof(ntp_control_message));
195 LI_SET(p->flags, LI_NOWARNING); 184 LI_SET(message->flags, LI_NOWARNING);
196 VN_SET(p->flags, VN_RESERVED); 185 VN_SET(message->flags, VN_RESERVED);
197 MODE_SET(p->flags, MODE_CONTROLMSG); 186 MODE_SET(message->flags, MODE_CONTROLMSG);
198 OP_SET(p->op, opcode); 187 OP_SET(message->op, opcode);
199 p->seq = htons(seq); 188 message->seq = htons(seq);
200 /* Remaining fields are zero for requests */ 189 /* Remaining fields are zero for requests */
201} 190}
202 191
@@ -211,10 +200,23 @@ void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq)
211 * status is pretty much useless as syncsource_found is a global variable 200 * status is pretty much useless as syncsource_found is a global variable
212 * used later in main to check is the server was synchronized. It works 201 * used later in main to check is the server was synchronized. It works
213 * so I left it alone */ 202 * so I left it alone */
214int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum, int *num_truechimers) { 203typedef struct {
215 *offset_result = STATE_UNKNOWN; 204 mp_state_enum state;
216 *jitter = *stratum = -1; 205 mp_state_enum offset_result;
217 *num_truechimers = 0; 206 double offset;
207 double jitter;
208 long stratum;
209 int num_truechimers;
210} ntp_request_result;
211ntp_request_result ntp_request(const check_ntp_peer_config config) {
212
213 ntp_request_result result = {
214 .state = STATE_OK,
215 .offset_result = STATE_UNKNOWN,
216 .jitter = -1,
217 .stratum = -1,
218 .num_truechimers = 0,
219 };
218 220
219 /* Long-winded explanation: 221 /* Long-winded explanation:
220 * Getting the sync peer offset, jitter and stratum requires a number of 222 * Getting the sync peer offset, jitter and stratum requires a number of
@@ -237,10 +239,10 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
237 void *tmp; 239 void *tmp;
238 ntp_assoc_status_pair *peers = NULL; 240 ntp_assoc_status_pair *peers = NULL;
239 int peer_offset = 0; 241 int peer_offset = 0;
240 int peers_size = 0; 242 size_t peers_size = 0;
241 int npeers = 0; 243 size_t npeers = 0;
242 int conn = -1; 244 int conn = -1;
243 my_udp_connect(server_address, port, &conn); 245 my_udp_connect(config.server_address, config.port, &conn);
244 246
245 /* keep sending requests until the server stops setting the 247 /* keep sending requests until the server stops setting the
246 * REM_MORE bit, though usually this is only 1 packet. */ 248 * REM_MORE bit, though usually this is only 1 packet. */
@@ -255,24 +257,28 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
255 /* Attempt to read the largest size packet possible */ 257 /* Attempt to read the largest size packet possible */
256 req.count = htons(MAX_CM_SIZE); 258 req.count = htons(MAX_CM_SIZE);
257 DBG(printf("receiving READSTAT response")) 259 DBG(printf("receiving READSTAT response"))
258 if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) 260 if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) {
259 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 261 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
262 }
260 DBG(print_ntp_control_message(&req)); 263 DBG(print_ntp_control_message(&req));
261 /* discard obviously invalid packets */ 264 /* discard obviously invalid packets */
262 if (ntohs(req.count) > MAX_CM_SIZE) 265 if (ntohs(req.count) > MAX_CM_SIZE) {
263 die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n"); 266 die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n");
267 }
264 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); 268 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1));
265 269
266 if (LI(req.flags) == LI_ALARM) 270 if (LI(req.flags) == LI_ALARM) {
267 li_alarm = true; 271 li_alarm = true;
272 }
268 /* Each peer identifier is 4 bytes in the data section, which 273 /* Each peer identifier is 4 bytes in the data section, which
269 * we represent as a ntp_assoc_status_pair datatype. 274 * we represent as a ntp_assoc_status_pair datatype.
270 */ 275 */
271 peers_size += ntohs(req.count); 276 peers_size += ntohs(req.count);
272 if ((tmp = realloc(peers, peers_size)) == NULL) 277 if ((tmp = realloc(peers, peers_size)) == NULL) {
273 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 278 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
279 }
274 peers = tmp; 280 peers = tmp;
275 memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count)); 281 memcpy((peers + peer_offset), (void *)req.data, ntohs(req.count));
276 npeers = peers_size / sizeof(ntp_assoc_status_pair); 282 npeers = peers_size / sizeof(ntp_assoc_status_pair);
277 peer_offset += ntohs(req.count); 283 peer_offset += ntohs(req.count);
278 } while (req.op & REM_MORE); 284 } while (req.op & REM_MORE);
@@ -280,9 +286,9 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
280 /* first, let's find out if we have a sync source, or if there are 286 /* first, let's find out if we have a sync source, or if there are
281 * at least some candidates. In the latter case we'll issue 287 * at least some candidates. In the latter case we'll issue
282 * a warning but go ahead with the check on them. */ 288 * a warning but go ahead with the check on them. */
283 for (int i = 0; i < npeers; i++) { 289 for (size_t i = 0; i < npeers; i++) {
284 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { 290 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) {
285 (*num_truechimers)++; 291 result.num_truechimers++;
286 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { 292 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
287 num_candidates++; 293 num_candidates++;
288 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { 294 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
@@ -293,31 +299,35 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
293 } 299 }
294 } 300 }
295 301
296 if (verbose) 302 if (verbose) {
297 printf("%d candidate peers available\n", num_candidates); 303 printf("%d candidate peers available\n", num_candidates);
298 if (verbose && syncsource_found) 304 }
305 if (verbose && syncsource_found) {
299 printf("synchronization source found\n"); 306 printf("synchronization source found\n");
307 }
300 308
301 int status = STATE_OK;
302 if (!syncsource_found) { 309 if (!syncsource_found) {
303 status = STATE_WARNING; 310 result.state = STATE_WARNING;
304 if (verbose) 311 if (verbose) {
305 printf("warning: no synchronization source found\n"); 312 printf("warning: no synchronization source found\n");
313 }
306 } 314 }
307 if (li_alarm) { 315 if (li_alarm) {
308 status = STATE_WARNING; 316 result.state = STATE_WARNING;
309 if (verbose) 317 if (verbose) {
310 printf("warning: LI_ALARM bit is set\n"); 318 printf("warning: LI_ALARM bit is set\n");
319 }
311 } 320 }
312 321
313 const char *getvar = "stratum,offset,jitter"; 322 const char *getvar = "stratum,offset,jitter";
314 char *data; 323 char *data;
315 for (int i = 0; i < npeers; i++) { 324 for (size_t i = 0; i < npeers; i++) {
316 /* Only query this server if it is the current sync source */ 325 /* Only query this server if it is the current sync source */
317 /* If there's no sync.peer, query all candidates and use the best one */ 326 /* If there's no sync.peer, query all candidates and use the best one */
318 if (PEER_SEL(peers[i].status) >= min_peer_sel) { 327 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
319 if (verbose) 328 if (verbose) {
320 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); 329 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
330 }
321 xasprintf(&data, ""); 331 xasprintf(&data, "");
322 do { 332 do {
323 setup_control_request(&req, OP_READVAR, 2); 333 setup_control_request(&req, OP_READVAR, 2);
@@ -342,60 +352,68 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
342 DBG(print_ntp_control_message(&req)); 352 DBG(print_ntp_control_message(&req));
343 } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2)); 353 } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2));
344 354
345 if (!(req.op & REM_ERROR)) 355 if (!(req.op & REM_ERROR)) {
346 xasprintf(&data, "%s%s", data, req.data); 356 xasprintf(&data, "%s%s", data, req.data);
357 }
347 } while (req.op & REM_MORE); 358 } while (req.op & REM_MORE);
348 359
349 if (req.op & REM_ERROR) { 360 if (req.op & REM_ERROR) {
350 if (strstr(getvar, "jitter")) { 361 if (strstr(getvar, "jitter")) {
351 if (verbose) 362 if (verbose) {
352 printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with " 363 printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with "
353 "'dispersion'...\n"); 364 "'dispersion'...\n");
365 }
354 getvar = "stratum,offset,dispersion"; 366 getvar = "stratum,offset,dispersion";
355 i--; 367 i--;
356 continue; 368 continue;
357 } 369 }
358 if (strlen(getvar)) { 370 if (strlen(getvar)) {
359 if (verbose) 371 if (verbose) {
360 printf("Server didn't like dispersion either; will retrieve everything\n"); 372 printf("Server didn't like dispersion either; will retrieve everything\n");
373 }
361 getvar = ""; 374 getvar = "";
362 i--; 375 i--;
363 continue; 376 continue;
364 } 377 }
365 } 378 }
366 379
367 if (verbose > 1) 380 if (verbose > 1) {
368 printf("Server responded: >>>%s<<<\n", data); 381 printf("Server responded: >>>%s<<<\n", data);
382 }
369 383
370 double tmp_offset = 0; 384 double tmp_offset = 0;
371 char *value; 385 char *value;
372 char *nptr; 386 char *nptr;
373 /* get the offset */ 387 /* get the offset */
374 if (verbose) 388 if (verbose) {
375 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc)); 389 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc));
390 }
376 391
377 value = np_extract_ntpvar(data, "offset"); 392 value = np_extract_ntpvar(data, "offset");
378 nptr = NULL; 393 nptr = NULL;
379 /* Convert the value if we have one */ 394 /* Convert the value if we have one */
380 if (value != NULL) 395 if (value != NULL) {
381 tmp_offset = strtod(value, &nptr) / 1000; 396 tmp_offset = strtod(value, &nptr) / 1000;
397 }
382 /* If value is null or no conversion was performed */ 398 /* If value is null or no conversion was performed */
383 if (value == NULL || value == nptr) { 399 if (value == NULL || value == nptr) {
384 if (verbose) 400 if (verbose) {
385 printf("error: unable to read server offset response.\n"); 401 printf("error: unable to read server offset response.\n");
402 }
386 } else { 403 } else {
387 if (verbose) 404 if (verbose) {
388 printf("%.10g\n", tmp_offset); 405 printf("%.10g\n", tmp_offset);
389 if (*offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(*offset)) { 406 }
390 *offset = tmp_offset; 407 if (result.offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(result.offset)) {
391 *offset_result = STATE_OK; 408 result.offset = tmp_offset;
409 result.offset_result = STATE_OK;
392 } else { 410 } else {
393 /* Skip this one; move to the next */ 411 /* Skip this one; move to the next */
394 continue; 412 continue;
395 } 413 }
396 } 414 }
397 415
398 if (do_jitter) { 416 if (config.do_jitter) {
399 /* get the jitter */ 417 /* get the jitter */
400 if (verbose) { 418 if (verbose) {
401 printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", 419 printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter",
@@ -404,19 +422,21 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
404 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter"); 422 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter");
405 nptr = NULL; 423 nptr = NULL;
406 /* Convert the value if we have one */ 424 /* Convert the value if we have one */
407 if (value != NULL) 425 if (value != NULL) {
408 *jitter = strtod(value, &nptr); 426 result.jitter = strtod(value, &nptr);
427 }
409 /* If value is null or no conversion was performed */ 428 /* If value is null or no conversion was performed */
410 if (value == NULL || value == nptr) { 429 if (value == NULL || value == nptr) {
411 if (verbose) 430 if (verbose) {
412 printf("error: unable to read server jitter/dispersion response.\n"); 431 printf("error: unable to read server jitter/dispersion response.\n");
413 *jitter = -1; 432 }
433 result.jitter = -1;
414 } else if (verbose) { 434 } else if (verbose) {
415 printf("%.10g\n", *jitter); 435 printf("%.10g\n", result.jitter);
416 } 436 }
417 } 437 }
418 438
419 if (do_stratum) { 439 if (config.do_stratum) {
420 /* get the stratum */ 440 /* get the stratum */
421 if (verbose) { 441 if (verbose) {
422 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc)); 442 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc));
@@ -424,28 +444,32 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
424 value = np_extract_ntpvar(data, "stratum"); 444 value = np_extract_ntpvar(data, "stratum");
425 nptr = NULL; 445 nptr = NULL;
426 /* Convert the value if we have one */ 446 /* Convert the value if we have one */
427 if (value != NULL) 447 if (value != NULL) {
428 *stratum = strtol(value, &nptr, 10); 448 result.stratum = strtol(value, &nptr, 10);
449 }
429 if (value == NULL || value == nptr) { 450 if (value == NULL || value == nptr) {
430 if (verbose) 451 if (verbose) {
431 printf("error: unable to read server stratum response.\n"); 452 printf("error: unable to read server stratum response.\n");
432 *stratum = -1; 453 }
454 result.stratum = -1;
433 } else { 455 } else {
434 if (verbose) 456 if (verbose) {
435 printf("%i\n", *stratum); 457 printf("%li\n", result.stratum);
458 }
436 } 459 }
437 } 460 }
438 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */ 461 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */
439 } /* for (i = 0; i < npeers; i++) */ 462 } /* for (i = 0; i < npeers; i++) */
440 463
441 close(conn); 464 close(conn);
442 if (peers != NULL) 465 if (peers != NULL) {
443 free(peers); 466 free(peers);
467 }
444 468
445 return status; 469 return result;
446} 470}
447 471
448int process_arguments(int argc, char **argv) { 472check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
449 static struct option longopts[] = { 473 static struct option longopts[] = {
450 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, 474 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'},
451 {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, 475 {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'},
@@ -454,14 +478,21 @@ int process_arguments(int argc, char **argv) {
454 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, {"timeout", required_argument, 0, 't'}, 478 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, {"timeout", required_argument, 0, 't'},
455 {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; 479 {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}};
456 480
457 if (argc < 2) 481 if (argc < 2) {
458 usage("\n"); 482 usage("\n");
483 }
484
485 check_ntp_peer_config_wrapper result = {
486 .errorcode = OK,
487 .config = check_ntp_peer_config_init(),
488 };
459 489
460 while (true) { 490 while (true) {
461 int option = 0; 491 int option = 0;
462 int option_char = getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); 492 int option_char = getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option);
463 if (option_char == -1 || option_char == EOF || option_char == 1) 493 if (option_char == -1 || option_char == EOF || option_char == 1) {
464 break; 494 break;
495 }
465 496
466 switch (option_char) { 497 switch (option_char) {
467 case 'h': 498 case 'h':
@@ -476,45 +507,46 @@ int process_arguments(int argc, char **argv) {
476 verbose++; 507 verbose++;
477 break; 508 break;
478 case 'q': 509 case 'q':
479 quiet = true; 510 result.config.quiet = true;
480 break; 511 break;
481 case 'w': 512 case 'w':
482 owarn = optarg; 513 result.config.owarn = optarg;
483 break; 514 break;
484 case 'c': 515 case 'c':
485 ocrit = optarg; 516 result.config.ocrit = optarg;
486 break; 517 break;
487 case 'W': 518 case 'W':
488 do_stratum = true; 519 result.config.do_stratum = true;
489 swarn = optarg; 520 result.config.swarn = optarg;
490 break; 521 break;
491 case 'C': 522 case 'C':
492 do_stratum = true; 523 result.config.do_stratum = true;
493 scrit = optarg; 524 result.config.scrit = optarg;
494 break; 525 break;
495 case 'j': 526 case 'j':
496 do_jitter = true; 527 result.config.do_jitter = true;
497 jwarn = optarg; 528 result.config.jwarn = optarg;
498 break; 529 break;
499 case 'k': 530 case 'k':
500 do_jitter = true; 531 result.config.do_jitter = true;
501 jcrit = optarg; 532 result.config.jcrit = optarg;
502 break; 533 break;
503 case 'm': 534 case 'm':
504 do_truechimers = true; 535 result.config.do_truechimers = true;
505 twarn = optarg; 536 result.config.twarn = optarg;
506 break; 537 break;
507 case 'n': 538 case 'n':
508 do_truechimers = true; 539 result.config.do_truechimers = true;
509 tcrit = optarg; 540 result.config.tcrit = optarg;
510 break; 541 break;
511 case 'H': 542 case 'H':
512 if (!is_host(optarg)) 543 if (!is_host(optarg)) {
513 usage2(_("Invalid hostname/address"), optarg); 544 usage2(_("Invalid hostname/address"), optarg);
514 server_address = strdup(optarg); 545 }
546 result.config.server_address = strdup(optarg);
515 break; 547 break;
516 case 'p': 548 case 'p':
517 port = atoi(optarg); 549 result.config.port = atoi(optarg);
518 break; 550 break;
519 case 't': 551 case 't':
520 socket_timeout = atoi(optarg); 552 socket_timeout = atoi(optarg);
@@ -536,29 +568,34 @@ int process_arguments(int argc, char **argv) {
536 } 568 }
537 } 569 }
538 570
539 if (server_address == NULL) { 571 if (result.config.server_address == NULL) {
540 usage4(_("Hostname was not supplied")); 572 usage4(_("Hostname was not supplied"));
541 } 573 }
542 574
543 return 0; 575 set_thresholds(&result.config.offset_thresholds, result.config.owarn, result.config.ocrit);
576 set_thresholds(&result.config.jitter_thresholds, result.config.jwarn, result.config.jcrit);
577 set_thresholds(&result.config.stratum_thresholds, result.config.swarn, result.config.scrit);
578 set_thresholds(&result.config.truechimer_thresholds, result.config.twarn, result.config.tcrit);
579
580 return result;
544} 581}
545 582
546char *perfd_offset(double offset) { 583char *perfd_offset(double offset, thresholds *offset_thresholds) {
547 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, 584 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false,
548 0); 585 0);
549} 586}
550 587
551char *perfd_jitter(double jitter) { 588char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) {
552 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, jitter_thresholds->critical->end, true, 0, 589 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, jitter_thresholds->critical->end, true, 0,
553 false, 0); 590 false, 0);
554} 591}
555 592
556char *perfd_stratum(int stratum) { 593char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) {
557 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, do_stratum, 594 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, do_stratum,
558 (int)stratum_thresholds->critical->end, true, 0, true, 16); 595 (int)stratum_thresholds->critical->end, true, 0, true, 16);
559} 596}
560 597
561char *perfd_truechimers(int num_truechimers) { 598char *perfd_truechimers(int num_truechimers, const bool do_truechimers, thresholds *truechimer_thresholds) {
562 return perfdata("truechimers", num_truechimers, "", do_truechimers, (int)truechimer_thresholds->warning->end, do_truechimers, 599 return perfdata("truechimers", num_truechimers, "", do_truechimers, (int)truechimer_thresholds->warning->end, do_truechimers,
563 (int)truechimer_thresholds->critical->end, true, 0, false, 0); 600 (int)truechimer_thresholds->critical->end, true, 0, false, 0);
564} 601}
@@ -571,13 +608,13 @@ int main(int argc, char *argv[]) {
571 /* Parse extra opts if any */ 608 /* Parse extra opts if any */
572 argv = np_extra_opts(&argc, argv, progname); 609 argv = np_extra_opts(&argc, argv, progname);
573 610
574 if (process_arguments(argc, argv) == ERROR) 611 check_ntp_peer_config_wrapper tmp_config = process_arguments(argc, argv);
612
613 if (tmp_config.errorcode == ERROR) {
575 usage4(_("Could not parse arguments")); 614 usage4(_("Could not parse arguments"));
615 }
576 616
577 set_thresholds(&offset_thresholds, owarn, ocrit); 617 const check_ntp_peer_config config = tmp_config.config;
578 set_thresholds(&jitter_thresholds, jwarn, jcrit);
579 set_thresholds(&stratum_thresholds, swarn, scrit);
580 set_thresholds(&truechimer_thresholds, twarn, tcrit);
581 618
582 /* initialize alarm signal handling */ 619 /* initialize alarm signal handling */
583 signal(SIGALRM, socket_timeout_alarm_handler); 620 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -585,44 +622,40 @@ int main(int argc, char *argv[]) {
585 /* set socket timeout */ 622 /* set socket timeout */
586 alarm(socket_timeout); 623 alarm(socket_timeout);
587 624
588 int offset_result;
589 int stratum;
590 int num_truechimers;
591 double offset = 0;
592 double jitter = 0;
593 /* This returns either OK or WARNING (See comment preceding ntp_request) */ 625 /* This returns either OK or WARNING (See comment preceding ntp_request) */
594 int result = ntp_request(&offset, &offset_result, &jitter, &stratum, &num_truechimers); 626 ntp_request_result ntp_res = ntp_request(config);
627 mp_state_enum result = STATE_UNKNOWN;
595 628
596 if (offset_result == STATE_UNKNOWN) { 629 if (ntp_res.offset_result == STATE_UNKNOWN) {
597 /* if there's no sync peer (this overrides ntp_request output): */ 630 /* if there's no sync peer (this overrides ntp_request output): */
598 result = (quiet ? STATE_UNKNOWN : STATE_CRITICAL); 631 result = (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL);
599 } else { 632 } else {
600 /* Be quiet if there's no candidates either */ 633 /* Be quiet if there's no candidates either */
601 if (quiet && result == STATE_WARNING) 634 if (config.quiet && result == STATE_WARNING) {
602 result = STATE_UNKNOWN; 635 result = STATE_UNKNOWN;
603 result = max_state_alt(result, get_status(fabs(offset), offset_thresholds)); 636 }
637 result = max_state_alt(result, get_status(fabs(ntp_res.offset), config.offset_thresholds));
604 } 638 }
605 639
606 int oresult = result; 640 mp_state_enum oresult = result;
607 641 mp_state_enum tresult = STATE_UNKNOWN;
608 int tresult = STATE_UNKNOWN;
609 642
610 if (do_truechimers) { 643 if (config.do_truechimers) {
611 tresult = get_status(num_truechimers, truechimer_thresholds); 644 tresult = get_status(ntp_res.num_truechimers, config.truechimer_thresholds);
612 result = max_state_alt(result, tresult); 645 result = max_state_alt(result, tresult);
613 } 646 }
614 647
615 int sresult = STATE_UNKNOWN; 648 mp_state_enum sresult = STATE_UNKNOWN;
616 649
617 if (do_stratum) { 650 if (config.do_stratum) {
618 sresult = get_status(stratum, stratum_thresholds); 651 sresult = get_status((double)ntp_res.stratum, config.stratum_thresholds);
619 result = max_state_alt(result, sresult); 652 result = max_state_alt(result, sresult);
620 } 653 }
621 654
622 int jresult = STATE_UNKNOWN; 655 mp_state_enum jresult = STATE_UNKNOWN;
623 656
624 if (do_jitter) { 657 if (config.do_jitter) {
625 jresult = get_status(jitter, jitter_thresholds); 658 jresult = get_status(ntp_res.jitter, config.jitter_thresholds);
626 result = max_state_alt(result, jresult); 659 result = max_state_alt(result, jresult);
627 } 660 }
628 661
@@ -641,59 +674,67 @@ int main(int argc, char *argv[]) {
641 xasprintf(&result_line, _("NTP UNKNOWN:")); 674 xasprintf(&result_line, _("NTP UNKNOWN:"));
642 break; 675 break;
643 } 676 }
644 if (!syncsource_found) 677
678 if (!syncsource_found) {
645 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); 679 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized"));
646 else if (li_alarm) 680 } else if (li_alarm) {
647 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); 681 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set"));
682 }
648 683
649 char *perfdata_line; 684 char *perfdata_line;
650 if (offset_result == STATE_UNKNOWN) { 685 if (ntp_res.offset_result == STATE_UNKNOWN) {
651 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 686 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
652 xasprintf(&perfdata_line, ""); 687 xasprintf(&perfdata_line, "");
653 } else if (oresult == STATE_WARNING) { 688 } else if (oresult == STATE_WARNING) {
654 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), offset); 689 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), ntp_res.offset);
655 } else if (oresult == STATE_CRITICAL) { 690 } else if (oresult == STATE_CRITICAL) {
656 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), offset); 691 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), ntp_res.offset);
657 } else { 692 } else {
658 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 693 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), ntp_res.offset);
659 } 694 }
660 xasprintf(&perfdata_line, "%s", perfd_offset(offset)); 695 xasprintf(&perfdata_line, "%s", perfd_offset(ntp_res.offset, config.offset_thresholds));
661 696
662 if (do_jitter) { 697 if (config.do_jitter) {
663 if (jresult == STATE_WARNING) { 698 if (jresult == STATE_WARNING) {
664 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, jitter); 699 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, ntp_res.jitter);
665 } else if (jresult == STATE_CRITICAL) { 700 } else if (jresult == STATE_CRITICAL) {
666 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, jitter); 701 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, ntp_res.jitter);
667 } else { 702 } else {
668 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); 703 xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter);
669 } 704 }
670 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); 705 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds));
671 } 706 }
672 if (do_stratum) { 707
708 if (config.do_stratum) {
673 if (sresult == STATE_WARNING) { 709 if (sresult == STATE_WARNING) {
674 xasprintf(&result_line, "%s, stratum=%i (WARNING)", result_line, stratum); 710 xasprintf(&result_line, "%s, stratum=%l (WARNING)", result_line, ntp_res.stratum);
675 } else if (sresult == STATE_CRITICAL) { 711 } else if (sresult == STATE_CRITICAL) {
676 xasprintf(&result_line, "%s, stratum=%i (CRITICAL)", result_line, stratum); 712 xasprintf(&result_line, "%s, stratum=%l (CRITICAL)", result_line, ntp_res.stratum);
677 } else { 713 } else {
678 xasprintf(&result_line, "%s, stratum=%i", result_line, stratum); 714 xasprintf(&result_line, "%s, stratum=%l", result_line, ntp_res.stratum);
679 } 715 }
680 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(stratum)); 716 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds));
681 } 717 }
682 if (do_truechimers) { 718
719 if (config.do_truechimers) {
683 if (tresult == STATE_WARNING) { 720 if (tresult == STATE_WARNING) {
684 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, num_truechimers); 721 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, ntp_res.num_truechimers);
685 } else if (tresult == STATE_CRITICAL) { 722 } else if (tresult == STATE_CRITICAL) {
686 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, num_truechimers); 723 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, ntp_res.num_truechimers);
687 } else { 724 } else {
688 xasprintf(&result_line, "%s, truechimers=%i", result_line, num_truechimers); 725 xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers);
689 } 726 }
690 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_truechimers(num_truechimers)); 727 xasprintf(&perfdata_line, "%s %s", perfdata_line,
728 perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers, config.truechimer_thresholds));
691 } 729 }
730
692 printf("%s|%s\n", result_line, perfdata_line); 731 printf("%s|%s\n", result_line, perfdata_line);
693 732
694 if (server_address != NULL) 733 if (config.server_address != NULL) {
695 free(server_address); 734 free(config.server_address);
696 return result; 735 }
736
737 exit(result);
697} 738}
698 739
699void print_help(void) { 740void print_help(void) {
diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h
new file mode 100644
index 00000000..00e6b05d
--- /dev/null
+++ b/plugins/check_ntp_peer.d/config.h
@@ -0,0 +1,67 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7enum {
8 DEFAULT_NTP_PORT = 123,
9};
10
11typedef struct {
12 char *server_address;
13 int port;
14
15 bool quiet;
16
17 // truechimer stuff
18 bool do_truechimers;
19 char *twarn;
20 char *tcrit;
21 thresholds *truechimer_thresholds;
22
23 char *owarn;
24 char *ocrit;
25 thresholds *offset_thresholds;
26
27 // stratum stuff
28 bool do_stratum;
29 char *swarn;
30 char *scrit;
31 thresholds *stratum_thresholds;
32
33 // jitter stuff
34 bool do_jitter;
35 char *jwarn;
36 char *jcrit;
37 thresholds *jitter_thresholds;
38
39} check_ntp_peer_config;
40
41check_ntp_peer_config check_ntp_peer_config_init() {
42 check_ntp_peer_config tmp = {
43 .server_address = NULL,
44 .port = DEFAULT_NTP_PORT,
45
46 .quiet = false,
47 .do_truechimers = false,
48 .twarn = "0:",
49 .tcrit = "0:",
50 .truechimer_thresholds = NULL,
51
52 .owarn = "60",
53 .ocrit = "120",
54 .offset_thresholds = NULL,
55
56 .do_stratum = false,
57 .swarn = "-1:16",
58 .scrit = "-1:16",
59 .stratum_thresholds = NULL,
60
61 .do_jitter = false,
62 .jwarn = "-1:5000",
63 .jcrit = "-1:10000",
64 .jitter_thresholds = NULL,
65 };
66 return tmp;
67}
diff --git a/plugins/check_nwstat.c b/plugins/check_nwstat.c
deleted file mode 100644
index 176dfbc8..00000000
--- a/plugins/check_nwstat.c
+++ /dev/null
@@ -1,1527 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_nwstat plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains the check_nwstat plugin
11 *
12 * This plugin attempts to contact the MRTGEXT NLM running on a
13 * Novell server to gather the requested system information.
14 *
15 *
16 * This program is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 *
29 *
30 *****************************************************************************/
31
32const char *progname = "check_nwstat";
33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h"
37#include "netutils.h"
38#include "utils.h"
39
40enum checkvar {
41 NONE,
42 LOAD1, /* check 1 minute CPU load */
43 LOAD5, /* check 5 minute CPU load */
44 LOAD15, /* check 15 minute CPU load */
45 CONNS, /* check number of connections */
46 VPF, /* check % free space on volume */
47 VMF, /* check MB free space on volume */
48 VMU, /* check MB used space on volume */
49 VPU, /* check % used space on volume */
50 VMP, /* check MB purgeable space on volume */
51 VKF, /* check KB free space on volume */
52 LTCH, /* check long-term cache hit percentage */
53 CBUFF, /* check total cache buffers */
54 CDBUFF, /* check dirty cache buffers */
55 LRUM, /* check LRU sitting time in minutes */
56 DSDB, /* check to see if DS Database is open */
57 LOGINS, /* check to see if logins are enabled */
58 NRMH, /* check to see NRM Health Status */
59 PUPRB, /* check % of used packet receive buffers */
60 UPRB, /* check used packet receive buffers */
61 SAPENTRIES, /* check SAP entries */
62 OFILES, /* check number of open files */
63 VKP, /* check KB purgeable space on volume */
64 VPP, /* check % purgeable space on volume */
65 VKNP, /* check KB not yet purgeable space on volume */
66 VPNP, /* check % not yet purgeable space on volume */
67 ABENDS, /* check abended thread count */
68 CSPROCS, /* check number of current service processes */
69 TSYNC, /* check timesync status 0=no 1=yes in sync to the network */
70 LRUS, /* check LRU sitting time in seconds */
71 DCB, /* check dirty cache buffers as a percentage of the total */
72 TCB, /* check total cache buffers as a percentage of the original */
73 DSVER, /* check NDS version */
74 UPTIME, /* check server uptime */
75 NLM, /* check NLM loaded */
76 NRMP, /* check NRM Process Values */
77 NRMM, /* check NRM Memory Values */
78 NRMS, /* check NRM Values */
79 NSS1, /* check Statistics from _Admin:Manage_NSS\GeneralStats.xml */
80 NSS2, /* check Statistics from _Admin:Manage_NSS\BufferCache.xml */
81 NSS3, /* check statistics from _Admin:Manage_NSS\NameCache.xml */
82 NSS4, /* check statistics from _Admin:Manage_NSS\FileStats.xml */
83 NSS5, /* check statistics from _Admin:Manage_NSS\ObjectCache.xml */
84 NSS6, /* check statistics from _Admin:Manage_NSS\Thread.xml */
85 NSS7 /* check statistics from _Admin:Manage_NSS\AuthorizationCache.xml */
86};
87
88enum {
89 PORT = 9999
90};
91
92static char *server_address = NULL;
93static char *volume_name = NULL;
94static char *nlm_name = NULL;
95static char *nrmp_name = NULL;
96static char *nrmm_name = NULL;
97static char *nrms_name = NULL;
98static char *nss1_name = NULL;
99static char *nss2_name = NULL;
100static char *nss3_name = NULL;
101static char *nss4_name = NULL;
102static char *nss5_name = NULL;
103static char *nss6_name = NULL;
104static char *nss7_name = NULL;
105static int server_port = PORT;
106static unsigned long warning_value = 0L;
107static unsigned long critical_value = 0L;
108static bool check_warning_value = false;
109static bool check_critical_value = false;
110static bool check_netware_version = false;
111static enum checkvar vars_to_check = NONE;
112static int sap_number = -1;
113
114static int process_arguments(int /*argc*/, char ** /*argv*/);
115static void print_help(void);
116void print_usage(void);
117
118int main(int argc, char **argv) {
119 int result = STATE_UNKNOWN;
120 int sd;
121 char *send_buffer = NULL;
122 char recv_buffer[MAX_INPUT_BUFFER];
123 char *output_message = NULL;
124 char *temp_buffer = NULL;
125 char *netware_version = NULL;
126
127 int time_sync_status = 0;
128 int nrm_health_status = 0;
129 unsigned long total_cache_buffers = 0;
130 unsigned long dirty_cache_buffers = 0;
131 unsigned long open_files = 0;
132 unsigned long abended_threads = 0;
133 unsigned long max_service_processes = 0;
134 unsigned long current_service_processes = 0;
135 unsigned long free_disk_space = 0L;
136 unsigned long nrmp_value = 0L;
137 unsigned long nrmm_value = 0L;
138 unsigned long nrms_value = 0L;
139 unsigned long nss1_value = 0L;
140 unsigned long nss2_value = 0L;
141 unsigned long nss3_value = 0L;
142 unsigned long nss4_value = 0L;
143 unsigned long nss5_value = 0L;
144 unsigned long nss6_value = 0L;
145 unsigned long nss7_value = 0L;
146 unsigned long total_disk_space = 0L;
147 unsigned long used_disk_space = 0L;
148 unsigned long percent_used_disk_space = 0L;
149 unsigned long purgeable_disk_space = 0L;
150 unsigned long non_purgeable_disk_space = 0L;
151 unsigned long percent_free_space = 0;
152 unsigned long percent_purgeable_space = 0;
153 unsigned long percent_non_purgeable_space = 0;
154 unsigned long current_connections = 0L;
155 unsigned long utilization = 0L;
156 unsigned long cache_hits = 0;
157 unsigned long cache_buffers = 0L;
158 unsigned long lru_time = 0L;
159 unsigned long max_packet_receive_buffers = 0;
160 unsigned long used_packet_receive_buffers = 0;
161 unsigned long percent_used_packet_receive_buffers = 0L;
162 unsigned long sap_entries = 0;
163 char uptime[MAX_INPUT_BUFFER];
164
165 setlocale(LC_ALL, "");
166 bindtextdomain(PACKAGE, LOCALEDIR);
167 textdomain(PACKAGE);
168
169 /* Parse extra opts if any */
170 argv = np_extra_opts(&argc, argv, progname);
171
172 if (process_arguments(argc, argv) == ERROR)
173 usage4(_("Could not parse arguments"));
174
175 /* initialize alarm signal handling */
176 signal(SIGALRM, socket_timeout_alarm_handler);
177
178 /* set socket timeout */
179 alarm(socket_timeout);
180
181 /* open connection */
182 my_tcp_connect(server_address, server_port, &sd);
183
184 /* get OS version string */
185 if (check_netware_version) {
186 send_buffer = strdup("S19\r\n");
187 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
188 if (result != STATE_OK)
189 return result;
190 if (!strcmp(recv_buffer, "-1\n"))
191 netware_version = strdup("");
192 else {
193 recv_buffer[strlen(recv_buffer) - 1] = 0;
194 xasprintf(&netware_version, _("NetWare %s: "), recv_buffer);
195 }
196 } else
197 netware_version = strdup("");
198
199 /* check CPU load */
200 if (vars_to_check == LOAD1 || vars_to_check == LOAD5 || vars_to_check == LOAD15) {
201
202 switch (vars_to_check) {
203 case LOAD1:
204 temp_buffer = strdup("1");
205 break;
206 case LOAD5:
207 temp_buffer = strdup("5");
208 break;
209 default:
210 temp_buffer = strdup("15");
211 break;
212 }
213
214 close(sd);
215 my_tcp_connect(server_address, server_port, &sd);
216
217 xasprintf(&send_buffer, "UTIL%s\r\n", temp_buffer);
218 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
219 if (result != STATE_OK)
220 return result;
221 utilization = strtoul(recv_buffer, NULL, 10);
222
223 close(sd);
224 my_tcp_connect(server_address, server_port, &sd);
225
226 send_buffer = strdup("UPTIME\r\n");
227 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
228 if (result != STATE_OK)
229 return result;
230 recv_buffer[strlen(recv_buffer) - 1] = 0;
231 sprintf(uptime, _("Up %s,"), recv_buffer);
232
233 if (check_critical_value && utilization >= critical_value)
234 result = STATE_CRITICAL;
235 else if (check_warning_value && utilization >= warning_value)
236 result = STATE_WARNING;
237
238 xasprintf(&output_message, _("Load %s - %s %s-min load average = %lu%%|load%s=%lu;%lu;%lu;0;100"), state_text(result), uptime,
239 temp_buffer, utilization, temp_buffer, utilization, warning_value, critical_value);
240
241 /* check number of user connections */
242 } else if (vars_to_check == CONNS) {
243
244 close(sd);
245 my_tcp_connect(server_address, server_port, &sd);
246
247 send_buffer = strdup("CONNECT\r\n");
248 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
249 if (result != STATE_OK)
250 return result;
251 current_connections = strtoul(recv_buffer, NULL, 10);
252
253 if (check_critical_value && current_connections >= critical_value)
254 result = STATE_CRITICAL;
255 else if (check_warning_value && current_connections >= warning_value)
256 result = STATE_WARNING;
257
258 xasprintf(&output_message, _("Conns %s - %lu current connections|Conns=%lu;%lu;%lu;;"), state_text(result), current_connections,
259 current_connections, warning_value, critical_value);
260
261 /* check % long term cache hits */
262 } else if (vars_to_check == LTCH) {
263
264 close(sd);
265 my_tcp_connect(server_address, server_port, &sd);
266
267 send_buffer = strdup("S1\r\n");
268 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
269 if (result != STATE_OK)
270 return result;
271 cache_hits = atoi(recv_buffer);
272
273 if (check_critical_value && cache_hits <= critical_value)
274 result = STATE_CRITICAL;
275 else if (check_warning_value && cache_hits <= warning_value)
276 result = STATE_WARNING;
277
278 xasprintf(&output_message, _("%s: Long term cache hits = %lu%%"), state_text(result), cache_hits);
279
280 /* check cache buffers */
281 } else if (vars_to_check == CBUFF) {
282
283 close(sd);
284 my_tcp_connect(server_address, server_port, &sd);
285
286 send_buffer = strdup("S2\r\n");
287 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
288 if (result != STATE_OK)
289 return result;
290 cache_buffers = strtoul(recv_buffer, NULL, 10);
291
292 if (check_critical_value && cache_buffers <= critical_value)
293 result = STATE_CRITICAL;
294 else if (check_warning_value && cache_buffers <= warning_value)
295 result = STATE_WARNING;
296
297 xasprintf(&output_message, _("%s: Total cache buffers = %lu|Cachebuffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers,
298 cache_buffers, warning_value, critical_value);
299
300 /* check dirty cache buffers */
301 } else if (vars_to_check == CDBUFF) {
302
303 close(sd);
304 my_tcp_connect(server_address, server_port, &sd);
305
306 send_buffer = strdup("S3\r\n");
307 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
308 if (result != STATE_OK)
309 return result;
310 cache_buffers = strtoul(recv_buffer, NULL, 10);
311
312 if (check_critical_value && cache_buffers >= critical_value)
313 result = STATE_CRITICAL;
314 else if (check_warning_value && cache_buffers >= warning_value)
315 result = STATE_WARNING;
316
317 xasprintf(&output_message, _("%s: Dirty cache buffers = %lu|Dirty-Cache-Buffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers,
318 cache_buffers, warning_value, critical_value);
319
320 /* check LRU sitting time in minutes */
321 } else if (vars_to_check == LRUM) {
322
323 close(sd);
324 my_tcp_connect(server_address, server_port, &sd);
325
326 send_buffer = strdup("S5\r\n");
327 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
328 if (result != STATE_OK)
329 return result;
330 lru_time = strtoul(recv_buffer, NULL, 10);
331
332 if (check_critical_value && lru_time <= critical_value)
333 result = STATE_CRITICAL;
334 else if (check_warning_value && lru_time <= warning_value)
335 result = STATE_WARNING;
336
337 xasprintf(&output_message, _("%s: LRU sitting time = %lu minutes"), state_text(result), lru_time);
338
339 /* check KB free space on volume */
340 } else if (vars_to_check == VKF) {
341
342 close(sd);
343 my_tcp_connect(server_address, server_port, &sd);
344
345 xasprintf(&send_buffer, "VKF%s\r\n", volume_name);
346 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
347 if (result != STATE_OK)
348 return result;
349
350 if (!strcmp(recv_buffer, "-1\n")) {
351 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
352 result = STATE_CRITICAL;
353 } else {
354 free_disk_space = strtoul(recv_buffer, NULL, 10);
355 if (check_critical_value && free_disk_space <= critical_value)
356 result = STATE_CRITICAL;
357 else if (check_warning_value && free_disk_space <= warning_value)
358 result = STATE_WARNING;
359 xasprintf(&output_message, _("%s%lu KB free on volume %s|KBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
360 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
361 }
362
363 /* check MB free space on volume */
364 } else if (vars_to_check == VMF) {
365
366 xasprintf(&send_buffer, "VMF%s\r\n", volume_name);
367 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
368 if (result != STATE_OK)
369 return result;
370
371 if (!strcmp(recv_buffer, "-1\n")) {
372 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
373 result = STATE_CRITICAL;
374 } else {
375 free_disk_space = strtoul(recv_buffer, NULL, 10);
376 if (check_critical_value && free_disk_space <= critical_value)
377 result = STATE_CRITICAL;
378 else if (check_warning_value && free_disk_space <= warning_value)
379 result = STATE_WARNING;
380 xasprintf(&output_message, _("%s%lu MB free on volume %s|MBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
381 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
382 }
383 /* check MB used space on volume */
384 } else if (vars_to_check == VMU) {
385
386 xasprintf(&send_buffer, "VMU%s\r\n", volume_name);
387 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
388 if (result != STATE_OK)
389 return result;
390
391 if (!strcmp(recv_buffer, "-1\n")) {
392 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
393 result = STATE_CRITICAL;
394 } else {
395 free_disk_space = strtoul(recv_buffer, NULL, 10);
396 if (check_critical_value && free_disk_space <= critical_value)
397 result = STATE_CRITICAL;
398 else if (check_warning_value && free_disk_space <= warning_value)
399 result = STATE_WARNING;
400 xasprintf(&output_message, _("%s%lu MB used on volume %s|MBUsed%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
401 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
402 }
403 /* check % used space on volume */
404 } else if (vars_to_check == VPU) {
405 close(sd);
406 my_tcp_connect(server_address, server_port, &sd);
407
408 asprintf(&send_buffer, "VMU%s\r\n", volume_name);
409 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
410
411 if (result != STATE_OK)
412 return result;
413
414 if (!strcmp(recv_buffer, "-1\n")) {
415 asprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
416 result = STATE_CRITICAL;
417
418 } else {
419 used_disk_space = strtoul(recv_buffer, NULL, 10);
420 close(sd);
421 my_tcp_connect(server_address, server_port, &sd);
422 /* get total volume in MB */
423 asprintf(&send_buffer, "VMS%s\r\n", volume_name);
424 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
425 if (result != STATE_OK)
426 return result;
427 total_disk_space = strtoul(recv_buffer, NULL, 10);
428 /* calculate percent used on volume */
429 percent_used_disk_space = (unsigned long)(((double)used_disk_space / (double)total_disk_space) * 100.0);
430
431 if (check_critical_value && percent_used_disk_space >= critical_value)
432 result = STATE_CRITICAL;
433 else if (check_warning_value && percent_used_disk_space >= warning_value)
434 result = STATE_WARNING;
435
436 asprintf(&output_message, _("%lu MB (%lu%%) used on volume %s - total %lu MB|Used space in percent on %s=%lu;%lu;%lu;0;100"),
437 used_disk_space, percent_used_disk_space, volume_name, total_disk_space, volume_name, percent_used_disk_space,
438 warning_value, critical_value);
439 }
440
441 /* check % free space on volume */
442 } else if (vars_to_check == VPF) {
443
444 close(sd);
445 my_tcp_connect(server_address, server_port, &sd);
446
447 xasprintf(&send_buffer, "VKF%s\r\n", volume_name);
448 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
449 if (result != STATE_OK)
450 return result;
451
452 if (!strcmp(recv_buffer, "-1\n")) {
453
454 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
455 result = STATE_CRITICAL;
456
457 } else {
458
459 free_disk_space = strtoul(recv_buffer, NULL, 10);
460
461 close(sd);
462 my_tcp_connect(server_address, server_port, &sd);
463
464 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
465 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
466 if (result != STATE_OK)
467 return result;
468 total_disk_space = strtoul(recv_buffer, NULL, 10);
469
470 percent_free_space = (unsigned long)(((double)free_disk_space / (double)total_disk_space) * 100.0);
471
472 if (check_critical_value && percent_free_space <= critical_value)
473 result = STATE_CRITICAL;
474 else if (check_warning_value && percent_free_space <= warning_value)
475 result = STATE_WARNING;
476 free_disk_space /= 1024;
477 total_disk_space /= 1024;
478 xasprintf(&output_message, _("%lu MB (%lu%%) free on volume %s - total %lu MB|FreeMB%s=%lu;%lu;%lu;0;100"), free_disk_space,
479 percent_free_space, volume_name, total_disk_space, volume_name, percent_free_space, warning_value, critical_value);
480 }
481
482 /* check to see if DS Database is open or closed */
483 } else if (vars_to_check == DSDB) {
484
485 close(sd);
486 my_tcp_connect(server_address, server_port, &sd);
487
488 send_buffer = strdup("S11\r\n");
489 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
490 if (result != STATE_OK)
491 return result;
492 if (atoi(recv_buffer) == 1)
493 result = STATE_OK;
494 else
495 result = STATE_WARNING;
496
497 close(sd);
498 my_tcp_connect(server_address, server_port, &sd);
499
500 send_buffer = strdup("S13\r\n");
501 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
502 temp_buffer = strtok(recv_buffer, "\r\n");
503
504 xasprintf(&output_message, _("Directory Services Database is %s (DS version %s)"), (result == STATE_OK) ? "open" : "closed",
505 temp_buffer);
506
507 /* check to see if logins are enabled */
508 } else if (vars_to_check == LOGINS) {
509
510 close(sd);
511 my_tcp_connect(server_address, server_port, &sd);
512
513 send_buffer = strdup("S12\r\n");
514 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
515 if (result != STATE_OK)
516 return result;
517 if (atoi(recv_buffer) == 1)
518 result = STATE_OK;
519 else
520 result = STATE_WARNING;
521
522 xasprintf(&output_message, _("Logins are %s"), (result == STATE_OK) ? _("enabled") : _("disabled"));
523
524 /* check NRM Health Status Summary*/
525 } else if (vars_to_check == NRMH) {
526
527 xasprintf(&send_buffer, "NRMH\r\n");
528 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
529 if (result != STATE_OK)
530 return result;
531
532 nrm_health_status = atoi(recv_buffer);
533
534 if (nrm_health_status == 2) {
535 result = STATE_OK;
536 xasprintf(&output_message, _("CRITICAL - NRM Status is bad!"));
537 } else {
538 if (nrm_health_status == 1) {
539 result = STATE_WARNING;
540 xasprintf(&output_message, _("Warning - NRM Status is suspect!"));
541 }
542
543 xasprintf(&output_message, _("OK - NRM Status is good!"));
544 }
545
546 /* check packet receive buffers */
547 } else if (vars_to_check == UPRB || vars_to_check == PUPRB) {
548
549 close(sd);
550 my_tcp_connect(server_address, server_port, &sd);
551
552 xasprintf(&send_buffer, "S15\r\n");
553 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
554 if (result != STATE_OK)
555 return result;
556
557 used_packet_receive_buffers = atoi(recv_buffer);
558
559 close(sd);
560 my_tcp_connect(server_address, server_port, &sd);
561
562 xasprintf(&send_buffer, "S16\r\n");
563 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
564 if (result != STATE_OK)
565 return result;
566
567 max_packet_receive_buffers = atoi(recv_buffer);
568
569 percent_used_packet_receive_buffers =
570 (unsigned long)(((double)used_packet_receive_buffers / (double)max_packet_receive_buffers) * 100.0);
571
572 if (vars_to_check == UPRB) {
573 if (check_critical_value && used_packet_receive_buffers >= critical_value)
574 result = STATE_CRITICAL;
575 else if (check_warning_value && used_packet_receive_buffers >= warning_value)
576 result = STATE_WARNING;
577 } else {
578 if (check_critical_value && percent_used_packet_receive_buffers >= critical_value)
579 result = STATE_CRITICAL;
580 else if (check_warning_value && percent_used_packet_receive_buffers >= warning_value)
581 result = STATE_WARNING;
582 }
583
584 xasprintf(&output_message, _("%lu of %lu (%lu%%) packet receive buffers used"), used_packet_receive_buffers,
585 max_packet_receive_buffers, percent_used_packet_receive_buffers);
586
587 /* check SAP table entries */
588 } else if (vars_to_check == SAPENTRIES) {
589
590 close(sd);
591 my_tcp_connect(server_address, server_port, &sd);
592
593 if (sap_number == -1)
594 xasprintf(&send_buffer, "S9\r\n");
595 else
596 xasprintf(&send_buffer, "S9.%d\r\n", sap_number);
597 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
598 if (result != STATE_OK)
599 return result;
600
601 sap_entries = atoi(recv_buffer);
602
603 if (check_critical_value && sap_entries >= critical_value)
604 result = STATE_CRITICAL;
605 else if (check_warning_value && sap_entries >= warning_value)
606 result = STATE_WARNING;
607
608 if (sap_number == -1)
609 xasprintf(&output_message, _("%lu entries in SAP table"), sap_entries);
610 else
611 xasprintf(&output_message, _("%lu entries in SAP table for SAP type %d"), sap_entries, sap_number);
612
613 /* check KB purgeable space on volume */
614 } else if (vars_to_check == VKP) {
615
616 close(sd);
617 my_tcp_connect(server_address, server_port, &sd);
618
619 xasprintf(&send_buffer, "VKP%s\r\n", volume_name);
620 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
621 if (result != STATE_OK)
622 return result;
623
624 if (!strcmp(recv_buffer, "-1\n")) {
625 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
626 result = STATE_CRITICAL;
627 } else {
628 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
629 if (check_critical_value && purgeable_disk_space >= critical_value)
630 result = STATE_CRITICAL;
631 else if (check_warning_value && purgeable_disk_space >= warning_value)
632 result = STATE_WARNING;
633 xasprintf(&output_message, _("%s%lu KB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
634 purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value);
635 }
636 /* check MB purgeable space on volume */
637 } else if (vars_to_check == VMP) {
638
639 xasprintf(&send_buffer, "VMP%s\r\n", volume_name);
640 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
641 if (result != STATE_OK)
642 return result;
643
644 if (!strcmp(recv_buffer, "-1\n")) {
645 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
646 result = STATE_CRITICAL;
647 } else {
648 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
649 if (check_critical_value && purgeable_disk_space >= critical_value)
650 result = STATE_CRITICAL;
651 else if (check_warning_value && purgeable_disk_space >= warning_value)
652 result = STATE_WARNING;
653 xasprintf(&output_message, _("%s%lu MB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
654 purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value);
655 }
656
657 /* check % purgeable space on volume */
658 } else if (vars_to_check == VPP) {
659
660 close(sd);
661 my_tcp_connect(server_address, server_port, &sd);
662
663 xasprintf(&send_buffer, "VKP%s\r\n", volume_name);
664 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
665 if (result != STATE_OK)
666 return result;
667
668 if (!strcmp(recv_buffer, "-1\n")) {
669
670 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
671 result = STATE_CRITICAL;
672
673 } else {
674
675 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
676
677 close(sd);
678 my_tcp_connect(server_address, server_port, &sd);
679
680 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
681 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
682 if (result != STATE_OK)
683 return result;
684 total_disk_space = strtoul(recv_buffer, NULL, 10);
685
686 percent_purgeable_space = (unsigned long)(((double)purgeable_disk_space / (double)total_disk_space) * 100.0);
687
688 if (check_critical_value && percent_purgeable_space >= critical_value)
689 result = STATE_CRITICAL;
690 else if (check_warning_value && percent_purgeable_space >= warning_value)
691 result = STATE_WARNING;
692 purgeable_disk_space /= 1024;
693 xasprintf(&output_message, _("%lu MB (%lu%%) purgeable on volume %s|Purgeable%s=%lu;%lu;%lu;0;100"), purgeable_disk_space,
694 percent_purgeable_space, volume_name, volume_name, percent_purgeable_space, warning_value, critical_value);
695 }
696
697 /* check KB not yet purgeable space on volume */
698 } else if (vars_to_check == VKNP) {
699
700 close(sd);
701 my_tcp_connect(server_address, server_port, &sd);
702
703 xasprintf(&send_buffer, "VKNP%s\r\n", volume_name);
704 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
705 if (result != STATE_OK)
706 return result;
707
708 if (!strcmp(recv_buffer, "-1\n")) {
709 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
710 result = STATE_CRITICAL;
711 } else {
712 non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
713 if (check_critical_value && non_purgeable_disk_space >= critical_value)
714 result = STATE_CRITICAL;
715 else if (check_warning_value && non_purgeable_disk_space >= warning_value)
716 result = STATE_WARNING;
717 xasprintf(&output_message, _("%s%lu KB not yet purgeable on volume %s"), (result == STATE_OK) ? "" : _("Only "),
718 non_purgeable_disk_space, volume_name);
719 }
720
721 /* check % not yet purgeable space on volume */
722 } else if (vars_to_check == VPNP) {
723
724 close(sd);
725 my_tcp_connect(server_address, server_port, &sd);
726
727 xasprintf(&send_buffer, "VKNP%s\r\n", volume_name);
728 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
729 if (result != STATE_OK)
730 return result;
731
732 if (!strcmp(recv_buffer, "-1\n")) {
733
734 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
735 result = STATE_CRITICAL;
736
737 } else {
738
739 non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
740
741 close(sd);
742 my_tcp_connect(server_address, server_port, &sd);
743
744 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
745 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
746 if (result != STATE_OK)
747 return result;
748 total_disk_space = strtoul(recv_buffer, NULL, 10);
749
750 percent_non_purgeable_space = (unsigned long)(((double)non_purgeable_disk_space / (double)total_disk_space) * 100.0);
751
752 if (check_critical_value && percent_non_purgeable_space >= critical_value)
753 result = STATE_CRITICAL;
754 else if (check_warning_value && percent_non_purgeable_space >= warning_value)
755 result = STATE_WARNING;
756 purgeable_disk_space /= 1024;
757 xasprintf(&output_message, _("%lu MB (%lu%%) not yet purgeable on volume %s"), non_purgeable_disk_space,
758 percent_non_purgeable_space, volume_name);
759 }
760
761 /* check # of open files */
762 } else if (vars_to_check == OFILES) {
763
764 close(sd);
765 my_tcp_connect(server_address, server_port, &sd);
766
767 xasprintf(&send_buffer, "S18\r\n");
768 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
769 if (result != STATE_OK)
770 return result;
771
772 open_files = atoi(recv_buffer);
773
774 if (check_critical_value && open_files >= critical_value)
775 result = STATE_CRITICAL;
776 else if (check_warning_value && open_files >= warning_value)
777 result = STATE_WARNING;
778
779 xasprintf(&output_message, _("%lu open files|Openfiles=%lu;%lu;%lu;0,0"), open_files, open_files, warning_value, critical_value);
780
781 /* check # of abended threads (Netware > 5.x only) */
782 } else if (vars_to_check == ABENDS) {
783
784 close(sd);
785 my_tcp_connect(server_address, server_port, &sd);
786
787 xasprintf(&send_buffer, "S17\r\n");
788 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
789 if (result != STATE_OK)
790 return result;
791
792 abended_threads = atoi(recv_buffer);
793
794 if (check_critical_value && abended_threads >= critical_value)
795 result = STATE_CRITICAL;
796 else if (check_warning_value && abended_threads >= warning_value)
797 result = STATE_WARNING;
798
799 xasprintf(&output_message, _("%lu abended threads|Abends=%lu;%lu;%lu;;"), abended_threads, abended_threads, warning_value,
800 critical_value);
801
802 /* check # of current service processes (Netware 5.x only) */
803 } else if (vars_to_check == CSPROCS) {
804
805 close(sd);
806 my_tcp_connect(server_address, server_port, &sd);
807
808 xasprintf(&send_buffer, "S20\r\n");
809 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
810 if (result != STATE_OK)
811 return result;
812
813 max_service_processes = atoi(recv_buffer);
814
815 close(sd);
816 my_tcp_connect(server_address, server_port, &sd);
817
818 xasprintf(&send_buffer, "S21\r\n");
819 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
820 if (result != STATE_OK)
821 return result;
822
823 current_service_processes = atoi(recv_buffer);
824
825 if (check_critical_value && current_service_processes >= critical_value)
826 result = STATE_CRITICAL;
827 else if (check_warning_value && current_service_processes >= warning_value)
828 result = STATE_WARNING;
829
830 xasprintf(&output_message, _("%lu current service processes (%lu max)|Processes=%lu;%lu;%lu;0;%lu"), current_service_processes,
831 max_service_processes, current_service_processes, warning_value, critical_value, max_service_processes);
832
833 /* check # Timesync Status */
834 } else if (vars_to_check == TSYNC) {
835
836 close(sd);
837 my_tcp_connect(server_address, server_port, &sd);
838
839 xasprintf(&send_buffer, "S22\r\n");
840 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
841 if (result != STATE_OK)
842 return result;
843
844 time_sync_status = atoi(recv_buffer);
845
846 if (time_sync_status == 0) {
847 result = STATE_CRITICAL;
848 xasprintf(&output_message, _("CRITICAL - Time not in sync with network!"));
849 } else {
850 xasprintf(&output_message, _("OK - Time in sync with network!"));
851 }
852
853 /* check LRU sitting time in secondss */
854 } else if (vars_to_check == LRUS) {
855
856 close(sd);
857 my_tcp_connect(server_address, server_port, &sd);
858
859 send_buffer = strdup("S4\r\n");
860 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
861 if (result != STATE_OK)
862 return result;
863 lru_time = strtoul(recv_buffer, NULL, 10);
864
865 if (check_critical_value && lru_time <= critical_value)
866 result = STATE_CRITICAL;
867 else if (check_warning_value && lru_time <= warning_value)
868 result = STATE_WARNING;
869 xasprintf(&output_message, _("LRU sitting time = %lu seconds"), lru_time);
870
871 /* check % dirty cacheobuffers as a percentage of the total*/
872 } else if (vars_to_check == DCB) {
873
874 close(sd);
875 my_tcp_connect(server_address, server_port, &sd);
876
877 send_buffer = strdup("S6\r\n");
878 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
879 if (result != STATE_OK)
880 return result;
881 dirty_cache_buffers = atoi(recv_buffer);
882
883 if (check_critical_value && dirty_cache_buffers <= critical_value)
884 result = STATE_CRITICAL;
885 else if (check_warning_value && dirty_cache_buffers <= warning_value)
886 result = STATE_WARNING;
887 xasprintf(&output_message, _("Dirty cache buffers = %lu%% of the total|DCB=%lu;%lu;%lu;0;100"), dirty_cache_buffers,
888 dirty_cache_buffers, warning_value, critical_value);
889
890 /* check % total cache buffers as a percentage of the original*/
891 } else if (vars_to_check == TCB) {
892
893 close(sd);
894 my_tcp_connect(server_address, server_port, &sd);
895
896 send_buffer = strdup("S7\r\n");
897 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
898 if (result != STATE_OK)
899 return result;
900 total_cache_buffers = atoi(recv_buffer);
901
902 if (check_critical_value && total_cache_buffers <= critical_value)
903 result = STATE_CRITICAL;
904 else if (check_warning_value && total_cache_buffers <= warning_value)
905 result = STATE_WARNING;
906 xasprintf(&output_message, _("Total cache buffers = %lu%% of the original|TCB=%lu;%lu;%lu;0;100"), total_cache_buffers,
907 total_cache_buffers, warning_value, critical_value);
908
909 } else if (vars_to_check == DSVER) {
910
911 close(sd);
912 my_tcp_connect(server_address, server_port, &sd);
913
914 xasprintf(&send_buffer, "S13\r\n");
915 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
916 if (result != STATE_OK)
917 return result;
918
919 recv_buffer[strlen(recv_buffer) - 1] = 0;
920
921 xasprintf(&output_message, _("NDS Version %s"), recv_buffer);
922
923 } else if (vars_to_check == UPTIME) {
924
925 close(sd);
926 my_tcp_connect(server_address, server_port, &sd);
927
928 xasprintf(&send_buffer, "UPTIME\r\n");
929 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
930 if (result != STATE_OK)
931 return result;
932
933 recv_buffer[sizeof(recv_buffer) - 1] = 0;
934 recv_buffer[strlen(recv_buffer) - 1] = 0;
935
936 xasprintf(&output_message, _("Up %s"), recv_buffer);
937
938 } else if (vars_to_check == NLM) {
939
940 close(sd);
941 my_tcp_connect(server_address, server_port, &sd);
942
943 xasprintf(&send_buffer, "S24:%s\r\n", nlm_name);
944 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
945 if (result != STATE_OK)
946 return result;
947
948 recv_buffer[strlen(recv_buffer) - 1] = 0;
949 if (strcmp(recv_buffer, "-1")) {
950 xasprintf(&output_message, _("Module %s version %s is loaded"), nlm_name, recv_buffer);
951 } else {
952 result = STATE_CRITICAL;
953 xasprintf(&output_message, _("Module %s is not loaded"), nlm_name);
954 }
955 } else if (vars_to_check == NRMP) {
956
957 xasprintf(&send_buffer, "NRMP:%s\r\n", nrmp_name);
958 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
959 if (result != STATE_OK)
960 return result;
961
962 if (!strcmp(recv_buffer, "-1\n")) {
963 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmp_name);
964 result = STATE_CRITICAL;
965 } else {
966 nrmp_value = strtoul(recv_buffer, NULL, 10);
967 if (check_critical_value && nrmp_value <= critical_value)
968 result = STATE_CRITICAL;
969 else if (check_warning_value && nrmp_value <= warning_value)
970 result = STATE_WARNING;
971 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmp_name, nrmp_value, nrmp_name, nrmp_value, warning_value,
972 critical_value);
973 }
974
975 } else if (vars_to_check == NRMM) {
976
977 xasprintf(&send_buffer, "NRMM:%s\r\n", nrmm_name);
978 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
979 if (result != STATE_OK)
980 return result;
981
982 if (!strcmp(recv_buffer, "-1\n")) {
983 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmm_name);
984 result = STATE_CRITICAL;
985 } else {
986 nrmm_value = strtoul(recv_buffer, NULL, 10);
987 if (check_critical_value && nrmm_value <= critical_value)
988 result = STATE_CRITICAL;
989 else if (check_warning_value && nrmm_value <= warning_value)
990 result = STATE_WARNING;
991 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmm_name, nrmm_value, nrmm_name, nrmm_value, warning_value,
992 critical_value);
993 }
994
995 } else if (vars_to_check == NRMS) {
996
997 xasprintf(&send_buffer, "NRMS:%s\r\n", nrms_name);
998 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
999 if (result != STATE_OK)
1000 return result;
1001
1002 if (!strcmp(recv_buffer, "-1\n")) {
1003 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrms_name);
1004 result = STATE_CRITICAL;
1005 } else {
1006 nrms_value = strtoul(recv_buffer, NULL, 10);
1007 if (check_critical_value && nrms_value >= critical_value)
1008 result = STATE_CRITICAL;
1009 else if (check_warning_value && nrms_value >= warning_value)
1010 result = STATE_WARNING;
1011 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrms_name, nrms_value, nrms_name, nrms_value, warning_value,
1012 critical_value);
1013 }
1014
1015 } else if (vars_to_check == NSS1) {
1016
1017 xasprintf(&send_buffer, "NSS1:%s\r\n", nss1_name);
1018 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1019 if (result != STATE_OK)
1020 return result;
1021
1022 if (!strcmp(recv_buffer, "-1\n")) {
1023 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss1_name);
1024 result = STATE_CRITICAL;
1025 } else {
1026 nss1_value = strtoul(recv_buffer, NULL, 10);
1027 if (check_critical_value && nss1_value >= critical_value)
1028 result = STATE_CRITICAL;
1029 else if (check_warning_value && nss1_value >= warning_value)
1030 result = STATE_WARNING;
1031 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss1_name, nss1_value, nss1_name, nss1_value, warning_value,
1032 critical_value);
1033 }
1034
1035 } else if (vars_to_check == NSS2) {
1036
1037 xasprintf(&send_buffer, "NSS2:%s\r\n", nss2_name);
1038 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1039 if (result != STATE_OK)
1040 return result;
1041
1042 if (!strcmp(recv_buffer, "-1\n")) {
1043 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss2_name);
1044 result = STATE_CRITICAL;
1045 } else {
1046 nss2_value = strtoul(recv_buffer, NULL, 10);
1047 if (check_critical_value && nss2_value >= critical_value)
1048 result = STATE_CRITICAL;
1049 else if (check_warning_value && nss2_value >= warning_value)
1050 result = STATE_WARNING;
1051 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss2_name, nss2_value, nss2_name, nss2_value, warning_value,
1052 critical_value);
1053 }
1054
1055 } else if (vars_to_check == NSS3) {
1056
1057 xasprintf(&send_buffer, "NSS3:%s\r\n", nss3_name);
1058 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1059 if (result != STATE_OK)
1060 return result;
1061
1062 if (!strcmp(recv_buffer, "-1\n")) {
1063 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss3_name);
1064 result = STATE_CRITICAL;
1065 } else {
1066 nss3_value = strtoul(recv_buffer, NULL, 10);
1067 if (check_critical_value && nss3_value >= critical_value)
1068 result = STATE_CRITICAL;
1069 else if (check_warning_value && nss3_value >= warning_value)
1070 result = STATE_WARNING;
1071 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss3_name, nss3_value, nss3_name, nss3_value, warning_value,
1072 critical_value);
1073 }
1074
1075 } else if (vars_to_check == NSS4) {
1076
1077 xasprintf(&send_buffer, "NSS4:%s\r\n", nss4_name);
1078 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1079 if (result != STATE_OK)
1080 return result;
1081
1082 if (!strcmp(recv_buffer, "-1\n")) {
1083 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss4_name);
1084 result = STATE_CRITICAL;
1085 } else {
1086 nss4_value = strtoul(recv_buffer, NULL, 10);
1087 if (check_critical_value && nss4_value >= critical_value)
1088 result = STATE_CRITICAL;
1089 else if (check_warning_value && nss4_value >= warning_value)
1090 result = STATE_WARNING;
1091 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss4_name, nss4_value, nss4_name, nss4_value, warning_value,
1092 critical_value);
1093 }
1094
1095 } else if (vars_to_check == NSS5) {
1096
1097 xasprintf(&send_buffer, "NSS5:%s\r\n", nss5_name);
1098 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1099 if (result != STATE_OK)
1100 return result;
1101
1102 if (!strcmp(recv_buffer, "-1\n")) {
1103 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss5_name);
1104 result = STATE_CRITICAL;
1105 } else {
1106 nss5_value = strtoul(recv_buffer, NULL, 10);
1107 if (check_critical_value && nss5_value >= critical_value)
1108 result = STATE_CRITICAL;
1109 else if (check_warning_value && nss5_value >= warning_value)
1110 result = STATE_WARNING;
1111 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss5_name, nss5_value, nss5_name, nss5_value, warning_value,
1112 critical_value);
1113 }
1114
1115 } else if (vars_to_check == NSS6) {
1116
1117 xasprintf(&send_buffer, "NSS6:%s\r\n", nss6_name);
1118 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1119 if (result != STATE_OK)
1120 return result;
1121
1122 if (!strcmp(recv_buffer, "-1\n")) {
1123 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss6_name);
1124 result = STATE_CRITICAL;
1125 } else {
1126 nss6_value = strtoul(recv_buffer, NULL, 10);
1127 if (check_critical_value && nss6_value >= critical_value)
1128 result = STATE_CRITICAL;
1129 else if (check_warning_value && nss6_value >= warning_value)
1130 result = STATE_WARNING;
1131 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss6_name, nss6_value, nss6_name, nss6_value, warning_value,
1132 critical_value);
1133 }
1134
1135 } else if (vars_to_check == NSS7) {
1136
1137 xasprintf(&send_buffer, "NSS7:%s\r\n", nss7_name);
1138 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1139 if (result != STATE_OK)
1140 return result;
1141
1142 if (!strcmp(recv_buffer, "-1\n")) {
1143 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss7_name);
1144 result = STATE_CRITICAL;
1145 } else {
1146 nss7_value = strtoul(recv_buffer, NULL, 10);
1147 if (check_critical_value && nss7_value >= critical_value)
1148 result = STATE_CRITICAL;
1149 else if (check_warning_value && nss7_value >= warning_value)
1150 result = STATE_WARNING;
1151 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss7_name, nss7_value, nss7_name, nss7_value, warning_value,
1152 critical_value);
1153 }
1154
1155 } else {
1156
1157 output_message = strdup(_("Nothing to check!\n"));
1158 result = STATE_UNKNOWN;
1159 }
1160
1161 close(sd);
1162
1163 /* reset timeout */
1164 alarm(0);
1165
1166 printf("%s%s\n", netware_version, output_message);
1167
1168 return result;
1169}
1170
1171/* process command-line arguments */
1172int process_arguments(int argc, char **argv) {
1173 int c;
1174
1175 int option = 0;
1176 static struct option longopts[] = {{"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'},
1177 {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'},
1178 {"variable", required_argument, 0, 'v'}, {"hostname", required_argument, 0, 'H'},
1179 {"osversion", no_argument, 0, 'o'}, {"version", no_argument, 0, 'V'},
1180 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
1181
1182 /* no options were supplied */
1183 if (argc < 2)
1184 return ERROR;
1185
1186 /* backwards compatibility */
1187 if (!is_option(argv[1])) {
1188 server_address = argv[1];
1189 argv[1] = argv[0];
1190 argv = &argv[1];
1191 argc--;
1192 }
1193
1194 for (c = 1; c < argc; c++) {
1195 if (strcmp("-to", argv[c]) == 0)
1196 strcpy(argv[c], "-t");
1197 else if (strcmp("-wv", argv[c]) == 0)
1198 strcpy(argv[c], "-w");
1199 else if (strcmp("-cv", argv[c]) == 0)
1200 strcpy(argv[c], "-c");
1201 }
1202
1203 while (1) {
1204 c = getopt_long(argc, argv, "+hoVH:t:c:w:p:v:", longopts, &option);
1205
1206 if (c == -1 || c == EOF || c == 1)
1207 break;
1208
1209 switch (c) {
1210 case '?': /* print short usage statement if args not parsable */
1211 usage5();
1212 case 'h': /* help */
1213 print_help();
1214 exit(STATE_UNKNOWN);
1215 case 'V': /* version */
1216 print_revision(progname, NP_VERSION);
1217 exit(STATE_UNKNOWN);
1218 case 'H': /* hostname */
1219 server_address = optarg;
1220 break;
1221 case 'o': /* display nos version */
1222 check_netware_version = true;
1223 break;
1224 case 'p': /* port */
1225 if (is_intnonneg(optarg))
1226 server_port = atoi(optarg);
1227 else
1228 die(STATE_UNKNOWN, _("Server port an integer\n"));
1229 break;
1230 case 'v':
1231 if (strlen(optarg) < 3)
1232 return ERROR;
1233 if (!strcmp(optarg, "LOAD1"))
1234 vars_to_check = LOAD1;
1235 else if (!strcmp(optarg, "LOAD5"))
1236 vars_to_check = LOAD5;
1237 else if (!strcmp(optarg, "LOAD15"))
1238 vars_to_check = LOAD15;
1239 else if (!strcmp(optarg, "CONNS"))
1240 vars_to_check = CONNS;
1241 else if (!strcmp(optarg, "LTCH"))
1242 vars_to_check = LTCH;
1243 else if (!strcmp(optarg, "DCB"))
1244 vars_to_check = DCB;
1245 else if (!strcmp(optarg, "TCB"))
1246 vars_to_check = TCB;
1247 else if (!strcmp(optarg, "CBUFF"))
1248 vars_to_check = CBUFF;
1249 else if (!strcmp(optarg, "CDBUFF"))
1250 vars_to_check = CDBUFF;
1251 else if (!strcmp(optarg, "LRUM"))
1252 vars_to_check = LRUM;
1253 else if (!strcmp(optarg, "LRUS"))
1254 vars_to_check = LRUS;
1255 else if (strncmp(optarg, "VPF", 3) == 0) {
1256 vars_to_check = VPF;
1257 volume_name = strdup(optarg + 3);
1258 if (!strcmp(volume_name, ""))
1259 volume_name = strdup("SYS");
1260 } else if (strncmp(optarg, "VKF", 3) == 0) {
1261 vars_to_check = VKF;
1262 volume_name = strdup(optarg + 3);
1263 if (!strcmp(volume_name, ""))
1264 volume_name = strdup("SYS");
1265 } else if (strncmp(optarg, "VMF", 3) == 0) {
1266 vars_to_check = VMF;
1267 volume_name = strdup(optarg + 3);
1268 if (!strcmp(volume_name, ""))
1269 volume_name = strdup("SYS");
1270 } else if (!strcmp(optarg, "DSDB"))
1271 vars_to_check = DSDB;
1272 else if (!strcmp(optarg, "LOGINS"))
1273 vars_to_check = LOGINS;
1274 else if (!strcmp(optarg, "NRMH"))
1275 vars_to_check = NRMH;
1276 else if (!strcmp(optarg, "UPRB"))
1277 vars_to_check = UPRB;
1278 else if (!strcmp(optarg, "PUPRB"))
1279 vars_to_check = PUPRB;
1280 else if (!strncmp(optarg, "SAPENTRIES", 10)) {
1281 vars_to_check = SAPENTRIES;
1282 if (strlen(optarg) > 10)
1283 sap_number = atoi(optarg + 10);
1284 else
1285 sap_number = -1;
1286 } else if (!strcmp(optarg, "OFILES"))
1287 vars_to_check = OFILES;
1288 else if (strncmp(optarg, "VKP", 3) == 0) {
1289 vars_to_check = VKP;
1290 volume_name = strdup(optarg + 3);
1291 if (!strcmp(volume_name, ""))
1292 volume_name = strdup("SYS");
1293 } else if (strncmp(optarg, "VMP", 3) == 0) {
1294 vars_to_check = VMP;
1295 volume_name = strdup(optarg + 3);
1296 if (!strcmp(volume_name, ""))
1297 volume_name = strdup("SYS");
1298 } else if (strncmp(optarg, "VMU", 3) == 0) {
1299 vars_to_check = VMU;
1300 volume_name = strdup(optarg + 3);
1301 if (!strcmp(volume_name, ""))
1302 volume_name = strdup("SYS");
1303 } else if (strncmp(optarg, "VPU", 3) == 0) {
1304 vars_to_check = VPU;
1305 volume_name = strdup(optarg + 3);
1306 if (!strcmp(volume_name, ""))
1307 volume_name = strdup("SYS");
1308 } else if (strncmp(optarg, "VPP", 3) == 0) {
1309 vars_to_check = VPP;
1310 volume_name = strdup(optarg + 3);
1311 if (!strcmp(volume_name, ""))
1312 volume_name = strdup("SYS");
1313 } else if (strncmp(optarg, "VKNP", 4) == 0) {
1314 vars_to_check = VKNP;
1315 volume_name = strdup(optarg + 4);
1316 if (!strcmp(volume_name, ""))
1317 volume_name = strdup("SYS");
1318 } else if (strncmp(optarg, "VPNP", 4) == 0) {
1319 vars_to_check = VPNP;
1320 volume_name = strdup(optarg + 4);
1321 if (!strcmp(volume_name, ""))
1322 volume_name = strdup("SYS");
1323 } else if (!strcmp(optarg, "ABENDS"))
1324 vars_to_check = ABENDS;
1325 else if (!strcmp(optarg, "CSPROCS"))
1326 vars_to_check = CSPROCS;
1327 else if (!strcmp(optarg, "TSYNC"))
1328 vars_to_check = TSYNC;
1329 else if (!strcmp(optarg, "DSVER"))
1330 vars_to_check = DSVER;
1331 else if (!strcmp(optarg, "UPTIME")) {
1332 vars_to_check = UPTIME;
1333 } else if (strncmp(optarg, "NLM:", 4) == 0) {
1334 vars_to_check = NLM;
1335 nlm_name = strdup(optarg + 4);
1336 } else if (strncmp(optarg, "NRMP", 4) == 0) {
1337 vars_to_check = NRMP;
1338 nrmp_name = strdup(optarg + 4);
1339 if (!strcmp(nrmp_name, ""))
1340 nrmp_name = strdup("AVAILABLE_MEMORY");
1341 } else if (strncmp(optarg, "NRMM", 4) == 0) {
1342 vars_to_check = NRMM;
1343 nrmm_name = strdup(optarg + 4);
1344 if (!strcmp(nrmm_name, ""))
1345 nrmm_name = strdup("AVAILABLE_CACHE_MEMORY");
1346
1347 }
1348
1349 else if (strncmp(optarg, "NRMS", 4) == 0) {
1350 vars_to_check = NRMS;
1351 nrms_name = strdup(optarg + 4);
1352 if (!strcmp(nrms_name, ""))
1353 nrms_name = strdup("USED_SWAP_SPACE");
1354
1355 }
1356
1357 else if (strncmp(optarg, "NSS1", 4) == 0) {
1358 vars_to_check = NSS1;
1359 nss1_name = strdup(optarg + 4);
1360 if (!strcmp(nss1_name, ""))
1361 nss1_name = strdup("CURRENTBUFFERCACHESIZE");
1362
1363 }
1364
1365 else if (strncmp(optarg, "NSS2", 4) == 0) {
1366 vars_to_check = NSS2;
1367 nss2_name = strdup(optarg + 4);
1368 if (!strcmp(nss2_name, ""))
1369 nss2_name = strdup("CACHEHITS");
1370
1371 }
1372
1373 else if (strncmp(optarg, "NSS3", 4) == 0) {
1374 vars_to_check = NSS3;
1375 nss3_name = strdup(optarg + 4);
1376 if (!strcmp(nss3_name, ""))
1377 nss3_name = strdup("CACHEGITPERCENT");
1378
1379 }
1380
1381 else if (strncmp(optarg, "NSS4", 4) == 0) {
1382 vars_to_check = NSS4;
1383 nss4_name = strdup(optarg + 4);
1384 if (!strcmp(nss4_name, ""))
1385 nss4_name = strdup("CURRENTOPENCOUNT");
1386
1387 }
1388
1389 else if (strncmp(optarg, "NSS5", 4) == 0) {
1390 vars_to_check = NSS5;
1391 nss5_name = strdup(optarg + 4);
1392 if (!strcmp(nss5_name, ""))
1393 nss5_name = strdup("CACHEMISSES");
1394
1395 }
1396
1397 else if (strncmp(optarg, "NSS6", 4) == 0) {
1398 vars_to_check = NSS6;
1399 nss6_name = strdup(optarg + 4);
1400 if (!strcmp(nss6_name, ""))
1401 nss6_name = strdup("PENDINGWORKSCOUNT");
1402
1403 }
1404
1405 else if (strncmp(optarg, "NSS7", 4) == 0) {
1406 vars_to_check = NSS7;
1407 nss7_name = strdup(optarg + 4);
1408 if (!strcmp(nss7_name, ""))
1409 nss7_name = strdup("CACHESIZE");
1410
1411 }
1412
1413 else
1414 return ERROR;
1415 break;
1416 case 'w': /* warning threshold */
1417 warning_value = strtoul(optarg, NULL, 10);
1418 check_warning_value = true;
1419 break;
1420 case 'c': /* critical threshold */
1421 critical_value = strtoul(optarg, NULL, 10);
1422 check_critical_value = true;
1423 break;
1424 case 't': /* timeout */
1425 socket_timeout = atoi(optarg);
1426 if (socket_timeout <= 0)
1427 return ERROR;
1428 }
1429 }
1430
1431 return OK;
1432}
1433
1434void print_help(void) {
1435 char *myport;
1436 xasprintf(&myport, "%d", PORT);
1437
1438 print_revision(progname, NP_VERSION);
1439
1440 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1441 printf(COPYRIGHT, copyright, email);
1442
1443 printf("%s\n", _("This plugin attempts to contact the MRTGEXT NLM running on a"));
1444 printf("%s\n", _("Novell server to gather the requested system information."));
1445
1446 printf("\n\n");
1447
1448 print_usage();
1449
1450 printf(UT_HELP_VRSN);
1451 printf(UT_EXTRA_OPTS);
1452
1453 printf(UT_HOST_PORT, 'p', myport);
1454
1455 printf(" %s\n", "-v, --variable=STRING");
1456 printf(" %s\n", _("Variable to check. Valid variables include:"));
1457 printf(" %s\n", _("LOAD1 = 1 minute average CPU load"));
1458 printf(" %s\n", _("LOAD5 = 5 minute average CPU load"));
1459 printf(" %s\n", _("LOAD15 = 15 minute average CPU load"));
1460 printf(" %s\n", _("CSPROCS = number of current service processes (NW 5.x only)"));
1461 printf(" %s\n", _("ABENDS = number of abended threads (NW 5.x only)"));
1462 printf(" %s\n", _("UPTIME = server uptime"));
1463 printf(" %s\n", _("LTCH = percent long term cache hits"));
1464 printf(" %s\n", _("CBUFF = current number of cache buffers"));
1465 printf(" %s\n", _("CDBUFF = current number of dirty cache buffers"));
1466 printf(" %s\n", _("DCB = dirty cache buffers as a percentage of the total"));
1467 printf(" %s\n", _("TCB = dirty cache buffers as a percentage of the original"));
1468 printf(" %s\n", _("OFILES = number of open files"));
1469 printf(" %s\n", _(" VMF<vol> = MB of free space on Volume <vol>"));
1470 printf(" %s\n", _(" VMU<vol> = MB used space on Volume <vol>"));
1471 printf(" %s\n", _(" VPU<vol> = percent used space on Volume <vol>"));
1472 printf(" %s\n", _(" VMP<vol> = MB of purgeable space on Volume <vol>"));
1473 printf(" %s\n", _(" VPF<vol> = percent free space on volume <vol>"));
1474 printf(" %s\n", _(" VKF<vol> = KB of free space on volume <vol>"));
1475 printf(" %s\n", _(" VPP<vol> = percent purgeable space on volume <vol>"));
1476 printf(" %s\n", _(" VKP<vol> = KB of purgeable space on volume <vol>"));
1477 printf(" %s\n", _(" VPNP<vol> = percent not yet purgeable space on volume <vol>"));
1478 printf(" %s\n", _(" VKNP<vol> = KB of not yet purgeable space on volume <vol>"));
1479 printf(" %s\n", _(" LRUM = LRU sitting time in minutes"));
1480 printf(" %s\n", _(" LRUS = LRU sitting time in seconds"));
1481 printf(" %s\n", _(" DSDB = check to see if DS Database is open"));
1482 printf(" %s\n", _(" DSVER = NDS version"));
1483 printf(" %s\n", _(" UPRB = used packet receive buffers"));
1484 printf(" %s\n", _(" PUPRB = percent (of max) used packet receive buffers"));
1485 printf(" %s\n", _(" SAPENTRIES = number of entries in the SAP table"));
1486 printf(" %s\n", _(" SAPENTRIES<n> = number of entries in the SAP table for SAP type <n>"));
1487 printf(" %s\n", _(" TSYNC = timesync status"));
1488 printf(" %s\n", _(" LOGINS = check to see if logins are enabled"));
1489 printf(" %s\n", _(" CONNS = number of currently licensed connections"));
1490 printf(" %s\n", _(" NRMH = NRM Summary Status"));
1491 printf(" %s\n", _(" NRMP<stat> = Returns the current value for a NRM health item"));
1492 printf(" %s\n", _(" NRMM<stat> = Returns the current memory stats from NRM"));
1493 printf(" %s\n", _(" NRMS<stat> = Returns the current Swapfile stats from NRM"));
1494 printf(" %s\n", _(" NSS1<stat> = Statistics from _Admin:Manage_NSS\\GeneralStats.xml"));
1495 printf(" %s\n", _(" NSS3<stat> = Statistics from _Admin:Manage_NSS\\NameCache.xml"));
1496 printf(" %s\n", _(" NSS4<stat> = Statistics from _Admin:Manage_NSS\\FileStats.xml"));
1497 printf(" %s\n", _(" NSS5<stat> = Statistics from _Admin:Manage_NSS\\ObjectCache.xml"));
1498 printf(" %s\n", _(" NSS6<stat> = Statistics from _Admin:Manage_NSS\\Thread.xml"));
1499 printf(" %s\n", _(" NSS7<stat> = Statistics from _Admin:Manage_NSS\\AuthorizationCache.xml"));
1500 printf(" %s\n", _(" NLM:<nlm> = check if NLM is loaded and report version"));
1501 printf(" %s\n", _(" (e.g. NLM:TSANDS.NLM)"));
1502 printf("\n");
1503 printf(" %s\n", "-w, --warning=INTEGER");
1504 printf(" %s\n", _("Threshold which will result in a warning status"));
1505 printf(" %s\n", "-c, --critical=INTEGER");
1506 printf(" %s\n", _("Threshold which will result in a critical status"));
1507 printf(" %s\n", "-o, --osversion");
1508 printf(" %s\n", _("Include server version string in results"));
1509
1510 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1511
1512 printf("\n");
1513 printf("%s\n", _("Notes:"));
1514 printf(" %s\n", _("- This plugin requires that the MRTGEXT.NLM file from James Drews' MRTG"));
1515 printf(" %s\n", _(" extension for NetWare be loaded on the Novell servers you wish to check."));
1516 printf(" %s\n", _(" (available from http://www.engr.wisc.edu/~drews/mrtg/)"));
1517 printf(" %s\n", _("- Values for critical thresholds should be lower than warning thresholds"));
1518 printf(" %s\n", _(" when the following variables are checked: VPF, VKF, LTCH, CBUFF, DCB, "));
1519 printf(" %s\n", _(" TCB, LRUS and LRUM."));
1520
1521 printf(UT_SUPPORT);
1522}
1523
1524void print_usage(void) {
1525 printf("%s\n", _("Usage:"));
1526 printf("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname);
1527}
diff --git a/plugins/check_ping.c b/plugins/check_ping.c
index 4aafaf41..6bcdeaad 100644
--- a/plugins/check_ping.c
+++ b/plugins/check_ping.c
@@ -36,61 +36,52 @@ const char *email = "devel@monitoring-plugins.org";
36#include "netutils.h" 36#include "netutils.h"
37#include "popen.h" 37#include "popen.h"
38#include "utils.h" 38#include "utils.h"
39#include "check_ping.d/config.h"
40#include "../lib/states.h"
39 41
40#include <signal.h> 42#include <signal.h>
41 43
42#define WARN_DUPLICATES "DUPLICATES FOUND! " 44#define WARN_DUPLICATES "DUPLICATES FOUND! "
43#define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
44 45
45enum { 46typedef struct {
46 UNKNOWN_PACKET_LOSS = 200, /* 200% */ 47 int errorcode;
47 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */ 48 check_ping_config config;
48}; 49} check_ping_config_wrapper;
50static check_ping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
51static check_ping_config_wrapper validate_arguments(check_ping_config_wrapper /*config_wrapper*/);
49 52
50static int process_arguments(int /*argc*/, char ** /*argv*/); 53static int get_threshold(char * /*arg*/, double * /*trta*/, int * /*tpl*/);
51static int get_threshold(char * /*arg*/, float * /*trta*/, int * /*tpl*/); 54
52static int validate_arguments(void); 55typedef struct {
53static int run_ping(const char *cmd, const char *addr); 56 mp_state_enum state;
54static int error_scan(char buf[MAX_INPUT_BUFFER], const char *addr); 57 double round_trip_average;
58 int packet_loss;
59} ping_result;
60static ping_result run_ping(const char *cmd, const char *addr, double /*crta*/);
61
62static mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr);
55static void print_help(void); 63static void print_help(void);
56void print_usage(void); 64void print_usage(void);
57 65
58static bool display_html = false;
59static int wpl = UNKNOWN_PACKET_LOSS;
60static int cpl = UNKNOWN_PACKET_LOSS;
61static float wrta = UNKNOWN_TRIP_TIME;
62static float crta = UNKNOWN_TRIP_TIME;
63static char **addresses = NULL;
64static int n_addresses = 0;
65static int max_addr = 1;
66static int max_packets = -1;
67static int verbose = 0; 66static int verbose = 0;
68 67
69static float rta = UNKNOWN_TRIP_TIME;
70static int pl = UNKNOWN_PACKET_LOSS;
71
72static char *warn_text; 68static char *warn_text;
73 69
74int main(int argc, char **argv) { 70int main(int argc, char **argv) {
75 char *cmd = NULL;
76 char *rawcmd = NULL;
77 int result = STATE_UNKNOWN;
78 int this_result = STATE_UNKNOWN;
79 int i;
80
81 setlocale(LC_ALL, ""); 71 setlocale(LC_ALL, "");
82 setlocale(LC_NUMERIC, "C"); 72 setlocale(LC_NUMERIC, "C");
83 bindtextdomain(PACKAGE, LOCALEDIR); 73 bindtextdomain(PACKAGE, LOCALEDIR);
84 textdomain(PACKAGE); 74 textdomain(PACKAGE);
85 75
86 addresses = malloc(sizeof(char *) * max_addr);
87 addresses[0] = NULL;
88
89 /* Parse extra opts if any */ 76 /* Parse extra opts if any */
90 argv = np_extra_opts(&argc, argv, progname); 77 argv = np_extra_opts(&argc, argv, progname);
91 78
92 if (process_arguments(argc, argv) == ERROR) 79 check_ping_config_wrapper tmp_config = process_arguments(argc, argv);
80 if (tmp_config.errorcode == ERROR) {
93 usage4(_("Could not parse arguments")); 81 usage4(_("Could not parse arguments"));
82 }
83
84 const check_ping_config config = tmp_config.config;
94 85
95 /* Set signal handling and alarm */ 86 /* Set signal handling and alarm */
96 if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) { 87 if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
@@ -105,71 +96,86 @@ int main(int argc, char **argv) {
105 alarm(timeout_interval); 96 alarm(timeout_interval);
106#endif 97#endif
107 98
108 for (i = 0; i < n_addresses; i++) { 99 int result = STATE_UNKNOWN;
109 100 char *rawcmd = NULL;
101 for (size_t i = 0; i < config.n_addresses; i++) {
110#ifdef PING6_COMMAND 102#ifdef PING6_COMMAND
111 if (address_family != AF_INET && is_inet6_addr(addresses[i])) 103 if (address_family != AF_INET && is_inet6_addr(config.addresses[i])) {
112 rawcmd = strdup(PING6_COMMAND); 104 rawcmd = strdup(PING6_COMMAND);
113 else 105 } else {
114 rawcmd = strdup(PING_COMMAND); 106 rawcmd = strdup(PING_COMMAND);
107 }
115#else 108#else
116 rawcmd = strdup(PING_COMMAND); 109 rawcmd = strdup(PING_COMMAND);
117#endif 110#endif
118 111
119 /* does the host address of number of packets argument come first? */ 112 char *cmd = NULL;
113
114 /* does the host address of number of packets argument come first? */
120#ifdef PING_PACKETS_FIRST 115#ifdef PING_PACKETS_FIRST
121# ifdef PING_HAS_TIMEOUT 116# ifdef PING_HAS_TIMEOUT
122 xasprintf(&cmd, rawcmd, timeout_interval, max_packets, addresses[i]); 117 xasprintf(&cmd, rawcmd, timeout_interval, config.max_packets, config.addresses[i]);
123# else 118# else
124 xasprintf(&cmd, rawcmd, max_packets, addresses[i]); 119 xasprintf(&cmd, rawcmd, config.max_packets, config.addresses[i]);
125# endif 120# endif
126#else 121#else
127 xasprintf(&cmd, rawcmd, addresses[i], max_packets); 122 xasprintf(&cmd, rawcmd, config.addresses[i], config.max_packets);
128#endif 123#endif
129 124
130 if (verbose >= 2) 125 if (verbose >= 2) {
131 printf("CMD: %s\n", cmd); 126 printf("CMD: %s\n", cmd);
127 }
132 128
133 /* run the command */ 129 /* run the command */
134 this_result = run_ping(cmd, addresses[i]);
135 130
136 if (pl == UNKNOWN_PACKET_LOSS || rta < 0.0) { 131 ping_result pinged = run_ping(cmd, config.addresses[i], config.crta);
132
133 if (pinged.packet_loss == UNKNOWN_PACKET_LOSS || pinged.round_trip_average < 0.0) {
137 printf("%s\n", cmd); 134 printf("%s\n", cmd);
138 die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n")); 135 die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n"));
139 } 136 }
140 137
141 if (pl >= cpl || rta >= crta || rta < 0) 138 if (pinged.packet_loss >= config.cpl || pinged.round_trip_average >= config.crta || pinged.round_trip_average < 0) {
142 this_result = STATE_CRITICAL; 139 pinged.state = STATE_CRITICAL;
143 else if (pl >= wpl || rta >= wrta) 140 } else if (pinged.packet_loss >= config.wpl || pinged.round_trip_average >= config.wrta) {
144 this_result = STATE_WARNING; 141 pinged.state = STATE_WARNING;
145 else if (pl >= 0 && rta >= 0) 142 } else if (pinged.packet_loss >= 0 && pinged.round_trip_average >= 0) {
146 this_result = max_state(STATE_OK, this_result); 143 pinged.state = max_state(STATE_OK, pinged.state);
147 144 }
148 if (n_addresses > 1 && this_result != STATE_UNKNOWN) 145
149 die(STATE_OK, "%s is alive\n", addresses[i]); 146 if (config.n_addresses > 1 && pinged.state != STATE_UNKNOWN) {
150 147 die(STATE_OK, "%s is alive\n", config.addresses[i]);
151 if (display_html == true) 148 }
152 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]); 149
153 if (pl == 100) 150 if (config.display_html) {
154 printf(_("PING %s - %sPacket loss = %d%%"), state_text(this_result), warn_text, pl); 151 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, config.addresses[i]);
155 else 152 }
156 printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(this_result), warn_text, pl, rta); 153 if (pinged.packet_loss == 100) {
157 if (display_html == true) 154 printf(_("PING %s - %sPacket loss = %d%%"), state_text(pinged.state), warn_text, pinged.packet_loss);
155 } else {
156 printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(pinged.state), warn_text, pinged.packet_loss,
157 pinged.round_trip_average);
158 }
159 if (config.display_html) {
158 printf("</A>"); 160 printf("</A>");
161 }
159 162
160 /* Print performance data */ 163 /* Print performance data */
161 if (pl != 100) { 164 if (pinged.packet_loss != 100) {
162 printf("|%s", 165 printf("|%s", fperfdata("rta", pinged.round_trip_average, "ms", (bool)(config.wrta > 0), config.wrta, (bool)(config.crta > 0),
163 fperfdata("rta", (double)rta, "ms", wrta > 0 ? true : false, wrta, crta > 0 ? true : false, crta, true, 0, false, 0)); 166 config.crta, true, 0, false, 0));
164 } else { 167 } else {
165 printf("| rta=U;%f;%f;;", wrta, crta); 168 printf("| rta=U;%f;%f;;", config.wrta, config.crta);
166 } 169 }
167 printf(" %s\n", perfdata("pl", (long)pl, "%", wpl > 0 ? true : false, wpl, cpl > 0 ? true : false, cpl, true, 0, false, 0));
168 170
169 if (verbose >= 2) 171 printf(" %s\n", perfdata("pl", (long)pinged.packet_loss, "%", (bool)(config.wpl > 0), config.wpl, (bool)(config.cpl > 0),
170 printf("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl); 172 config.cpl, true, 0, false, 0));
173
174 if (verbose >= 2) {
175 printf("%f:%d%% %f:%d%%\n", config.wrta, config.wpl, config.crta, config.cpl);
176 }
171 177
172 result = max_state(result, this_result); 178 result = max_state(result, pinged.state);
173 free(rawcmd); 179 free(rawcmd);
174 free(cmd); 180 free(cmd);
175 } 181 }
@@ -178,11 +184,7 @@ int main(int argc, char **argv) {
178} 184}
179 185
180/* process command-line arguments */ 186/* process command-line arguments */
181int process_arguments(int argc, char **argv) { 187check_ping_config_wrapper process_arguments(int argc, char **argv) {
182 int c = 1;
183 char *ptr;
184
185 int option = 0;
186 static struct option longopts[] = {STD_LONG_OPTS, 188 static struct option longopts[] = {STD_LONG_OPTS,
187 {"packets", required_argument, 0, 'p'}, 189 {"packets", required_argument, 0, 'p'},
188 {"nohtml", no_argument, 0, 'n'}, 190 {"nohtml", no_argument, 0, 'n'},
@@ -191,23 +193,35 @@ int process_arguments(int argc, char **argv) {
191 {"use-ipv6", no_argument, 0, '6'}, 193 {"use-ipv6", no_argument, 0, '6'},
192 {0, 0, 0, 0}}; 194 {0, 0, 0, 0}};
193 195
194 if (argc < 2) 196 check_ping_config_wrapper result = {
195 return ERROR; 197 .errorcode = OK,
198 .config = check_ping_config_init(),
199 };
200
201 if (argc < 2) {
202 result.errorcode = ERROR;
203 return result;
204 }
196 205
197 for (c = 1; c < argc; c++) { 206 for (int index = 1; index < argc; index++) {
198 if (strcmp("-to", argv[c]) == 0) 207 if (strcmp("-to", argv[index]) == 0) {
199 strcpy(argv[c], "-t"); 208 strcpy(argv[index], "-t");
200 if (strcmp("-nohtml", argv[c]) == 0) 209 }
201 strcpy(argv[c], "-n"); 210 if (strcmp("-nohtml", argv[index]) == 0) {
211 strcpy(argv[index], "-n");
212 }
202 } 213 }
203 214
204 while (1) { 215 int option = 0;
205 c = getopt_long(argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option); 216 size_t max_addr = MAX_ADDR_START;
217 while (true) {
218 int option_index = getopt_long(argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option);
206 219
207 if (c == -1 || c == EOF) 220 if (option_index == -1 || option_index == EOF) {
208 break; 221 break;
222 }
209 223
210 switch (c) { 224 switch (option_index) {
211 case '?': /* usage */ 225 case '?': /* usage */
212 usage5(); 226 usage5();
213 case 'h': /* help */ 227 case 'h': /* help */
@@ -234,17 +248,18 @@ int process_arguments(int argc, char **argv) {
234 usage(_("IPv6 support not available\n")); 248 usage(_("IPv6 support not available\n"));
235#endif 249#endif
236 break; 250 break;
237 case 'H': /* hostname */ 251 case 'H': /* hostname */ {
238 ptr = optarg; 252 char *ptr = optarg;
239 while (1) { 253 while (true) {
240 n_addresses++; 254 result.config.n_addresses++;
241 if (n_addresses > max_addr) { 255 if (result.config.n_addresses > max_addr) {
242 max_addr *= 2; 256 max_addr *= 2;
243 addresses = realloc(addresses, sizeof(char *) * max_addr); 257 result.config.addresses = realloc(result.config.addresses, sizeof(char *) * max_addr);
244 if (addresses == NULL) 258 if (result.config.addresses == NULL) {
245 die(STATE_UNKNOWN, _("Could not realloc() addresses\n")); 259 die(STATE_UNKNOWN, _("Could not realloc() addresses\n"));
260 }
246 } 261 }
247 addresses[n_addresses - 1] = ptr; 262 result.config.addresses[result.config.n_addresses - 1] = ptr;
248 if ((ptr = index(ptr, ','))) { 263 if ((ptr = index(ptr, ','))) {
249 strcpy(ptr, ""); 264 strcpy(ptr, "");
250 ptr += sizeof(char); 265 ptr += sizeof(char);
@@ -252,203 +267,248 @@ int process_arguments(int argc, char **argv) {
252 break; 267 break;
253 } 268 }
254 } 269 }
255 break; 270 } break;
256 case 'p': /* number of packets to send */ 271 case 'p': /* number of packets to send */
257 if (is_intnonneg(optarg)) 272 if (is_intnonneg(optarg)) {
258 max_packets = atoi(optarg); 273 result.config.max_packets = atoi(optarg);
259 else 274 } else {
260 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg); 275 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg);
276 }
261 break; 277 break;
262 case 'n': /* no HTML */ 278 case 'n': /* no HTML */
263 display_html = false; 279 result.config.display_html = false;
264 break; 280 break;
265 case 'L': /* show HTML */ 281 case 'L': /* show HTML */
266 display_html = true; 282 result.config.display_html = true;
267 break; 283 break;
268 case 'c': 284 case 'c':
269 get_threshold(optarg, &crta, &cpl); 285 get_threshold(optarg, &result.config.crta, &result.config.cpl);
270 break; 286 break;
271 case 'w': 287 case 'w':
272 get_threshold(optarg, &wrta, &wpl); 288 get_threshold(optarg, &result.config.wrta, &result.config.wpl);
273 break; 289 break;
274 } 290 }
275 } 291 }
276 292
277 c = optind; 293 int arg_counter = optind;
278 if (c == argc) 294 if (arg_counter == argc) {
279 return validate_arguments(); 295 return validate_arguments(result);
296 }
280 297
281 if (addresses[0] == NULL) { 298 if (result.config.addresses[0] == NULL) {
282 if (!is_host(argv[c])) { 299 if (!is_host(argv[arg_counter])) {
283 usage2(_("Invalid hostname/address"), argv[c]); 300 usage2(_("Invalid hostname/address"), argv[arg_counter]);
284 } else { 301 } else {
285 addresses[0] = argv[c++]; 302 result.config.addresses[0] = argv[arg_counter++];
286 n_addresses++; 303 result.config.n_addresses++;
287 if (c == argc) 304 if (arg_counter == argc) {
288 return validate_arguments(); 305 return validate_arguments(result);
306 }
289 } 307 }
290 } 308 }
291 309
292 if (wpl == UNKNOWN_PACKET_LOSS) { 310 if (result.config.wpl == UNKNOWN_PACKET_LOSS) {
293 if (!is_intpercent(argv[c])) { 311 if (!is_intpercent(argv[arg_counter])) {
294 printf(_("<wpl> (%s) must be an integer percentage\n"), argv[c]); 312 printf(_("<wpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
295 return ERROR; 313 result.errorcode = ERROR;
296 } else { 314 return result;
297 wpl = atoi(argv[c++]); 315 }
298 if (c == argc) 316 result.config.wpl = atoi(argv[arg_counter++]);
299 return validate_arguments(); 317 if (arg_counter == argc) {
318 return validate_arguments(result);
300 } 319 }
301 } 320 }
302 321
303 if (cpl == UNKNOWN_PACKET_LOSS) { 322 if (result.config.cpl == UNKNOWN_PACKET_LOSS) {
304 if (!is_intpercent(argv[c])) { 323 if (!is_intpercent(argv[arg_counter])) {
305 printf(_("<cpl> (%s) must be an integer percentage\n"), argv[c]); 324 printf(_("<cpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
306 return ERROR; 325 result.errorcode = ERROR;
307 } else { 326 return result;
308 cpl = atoi(argv[c++]); 327 }
309 if (c == argc) 328 result.config.cpl = atoi(argv[arg_counter++]);
310 return validate_arguments(); 329 if (arg_counter == argc) {
330 return validate_arguments(result);
311 } 331 }
312 } 332 }
313 333
314 if (wrta < 0.0) { 334 if (result.config.wrta < 0.0) {
315 if (is_negative(argv[c])) { 335 if (is_negative(argv[arg_counter])) {
316 printf(_("<wrta> (%s) must be a non-negative number\n"), argv[c]); 336 printf(_("<wrta> (%s) must be a non-negative number\n"), argv[arg_counter]);
317 return ERROR; 337 result.errorcode = ERROR;
318 } else { 338 return result;
319 wrta = atof(argv[c++]); 339 }
320 if (c == argc) 340 result.config.wrta = atof(argv[arg_counter++]);
321 return validate_arguments(); 341 if (arg_counter == argc) {
342 return validate_arguments(result);
322 } 343 }
323 } 344 }
324 345
325 if (crta < 0.0) { 346 if (result.config.crta < 0.0) {
326 if (is_negative(argv[c])) { 347 if (is_negative(argv[arg_counter])) {
327 printf(_("<crta> (%s) must be a non-negative number\n"), argv[c]); 348 printf(_("<crta> (%s) must be a non-negative number\n"), argv[arg_counter]);
328 return ERROR; 349 result.errorcode = ERROR;
329 } else { 350 return result;
330 crta = atof(argv[c++]); 351 }
331 if (c == argc) 352 result.config.crta = atof(argv[arg_counter++]);
332 return validate_arguments(); 353 if (arg_counter == argc) {
354 return validate_arguments(result);
333 } 355 }
334 } 356 }
335 357
336 if (max_packets == -1) { 358 if (result.config.max_packets == -1) {
337 if (is_intnonneg(argv[c])) { 359 if (is_intnonneg(argv[arg_counter])) {
338 max_packets = atoi(argv[c++]); 360 result.config.max_packets = atoi(argv[arg_counter++]);
339 } else { 361 } else {
340 printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[c]); 362 printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[arg_counter]);
341 return ERROR; 363 result.errorcode = ERROR;
364 return result;
342 } 365 }
343 } 366 }
344 367
345 return validate_arguments(); 368 return validate_arguments(result);
346} 369}
347 370
348int get_threshold(char *arg, float *trta, int *tpl) { 371int get_threshold(char *arg, double *trta, int *tpl) {
349 if (is_intnonneg(arg) && sscanf(arg, "%f", trta) == 1) 372 if (is_intnonneg(arg) && sscanf(arg, "%lf", trta) == 1) {
350 return OK; 373 return OK;
351 else if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%f%*[:,]%d%%", trta, tpl) == 2) 374 }
375
376 if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%lf%*[:,]%d%%", trta, tpl) == 2) {
352 return OK; 377 return OK;
353 else if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) 378 }
379
380 if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) {
354 return OK; 381 return OK;
382 }
355 383
356 usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg); 384 usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg);
357 return STATE_UNKNOWN; 385 return STATE_UNKNOWN;
358} 386}
359 387
360int validate_arguments() { 388check_ping_config_wrapper validate_arguments(check_ping_config_wrapper config_wrapper) {
361 float max_seconds; 389 if (config_wrapper.config.wrta < 0.0) {
362 int i;
363
364 if (wrta < 0.0) {
365 printf(_("<wrta> was not set\n")); 390 printf(_("<wrta> was not set\n"));
366 return ERROR; 391 config_wrapper.errorcode = ERROR;
367 } else if (crta < 0.0) { 392 return config_wrapper;
393 }
394
395 if (config_wrapper.config.crta < 0.0) {
368 printf(_("<crta> was not set\n")); 396 printf(_("<crta> was not set\n"));
369 return ERROR; 397 config_wrapper.errorcode = ERROR;
370 } else if (wpl == UNKNOWN_PACKET_LOSS) { 398 return config_wrapper;
399 }
400
401 if (config_wrapper.config.wpl == UNKNOWN_PACKET_LOSS) {
371 printf(_("<wpl> was not set\n")); 402 printf(_("<wpl> was not set\n"));
372 return ERROR; 403 config_wrapper.errorcode = ERROR;
373 } else if (cpl == UNKNOWN_PACKET_LOSS) { 404 return config_wrapper;
405 }
406
407 if (config_wrapper.config.cpl == UNKNOWN_PACKET_LOSS) {
374 printf(_("<cpl> was not set\n")); 408 printf(_("<cpl> was not set\n"));
375 return ERROR; 409 config_wrapper.errorcode = ERROR;
376 } else if (wrta > crta) { 410 return config_wrapper;
377 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta); 411 }
378 return ERROR; 412
379 } else if (wpl > cpl) { 413 if (config_wrapper.config.wrta > config_wrapper.config.crta) {
380 printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl); 414 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), config_wrapper.config.wrta, config_wrapper.config.crta);
381 return ERROR; 415 config_wrapper.errorcode = ERROR;
416 return config_wrapper;
382 } 417 }
383 418
384 if (max_packets == -1) 419 if (config_wrapper.config.wpl > config_wrapper.config.cpl) {
385 max_packets = DEFAULT_MAX_PACKETS; 420 printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), config_wrapper.config.wpl, config_wrapper.config.cpl);
421 config_wrapper.errorcode = ERROR;
422 return config_wrapper;
423 }
386 424
387 max_seconds = crta / 1000.0 * max_packets + max_packets; 425 if (config_wrapper.config.max_packets == -1) {
388 if (max_seconds > timeout_interval) 426 config_wrapper.config.max_packets = DEFAULT_MAX_PACKETS;
389 timeout_interval = (int)max_seconds; 427 }
390 428
391 for (i = 0; i < n_addresses; i++) { 429 double max_seconds = (config_wrapper.config.crta / 1000.0 * config_wrapper.config.max_packets) + config_wrapper.config.max_packets;
392 if (!is_host(addresses[i])) 430 if (max_seconds > timeout_interval) {
393 usage2(_("Invalid hostname/address"), addresses[i]); 431 timeout_interval = (unsigned int)max_seconds;
432 }
433
434 for (size_t i = 0; i < config_wrapper.config.n_addresses; i++) {
435 if (!is_host(config_wrapper.config.addresses[i])) {
436 usage2(_("Invalid hostname/address"), config_wrapper.config.addresses[i]);
437 }
394 } 438 }
395 439
396 if (n_addresses == 0) { 440 if (config_wrapper.config.n_addresses == 0) {
397 usage(_("You must specify a server address or host name")); 441 usage(_("You must specify a server address or host name"));
398 } 442 }
399 443
400 return OK; 444 return config_wrapper;
401} 445}
402 446
403int run_ping(const char *cmd, const char *addr) { 447ping_result run_ping(const char *cmd, const char *addr, double crta) {
404 char buf[MAX_INPUT_BUFFER]; 448 if ((child_process = spopen(cmd)) == NULL) {
405 int result = STATE_UNKNOWN;
406 int match;
407
408 if ((child_process = spopen(cmd)) == NULL)
409 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 449 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
450 }
410 451
411 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); 452 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
412 if (child_stderr == NULL) 453 if (child_stderr == NULL) {
413 printf(_("Cannot open stderr for %s\n"), cmd); 454 printf(_("Cannot open stderr for %s\n"), cmd);
455 }
414 456
415 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) { 457 char buf[MAX_INPUT_BUFFER];
458 ping_result result = {
459 .state = STATE_UNKNOWN,
460 .packet_loss = UNKNOWN_PACKET_LOSS,
461 .round_trip_average = UNKNOWN_TRIP_TIME,
462 };
416 463
417 if (verbose >= 3) 464 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) {
465 if (verbose >= 3) {
418 printf("Output: %s", buf); 466 printf("Output: %s", buf);
467 }
419 468
420 result = max_state(result, error_scan(buf, addr)); 469 result.state = max_state(result.state, error_scan(buf, addr));
421 470
422 /* get the percent loss statistics */ 471 /* get the percent loss statistics */
423 match = 0; 472 int match = 0;
424 if ((sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 473 if ((sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n", &result.packet_loss, &match) ==
425 (sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss%n", &pl, &match) && match) || 474 1 &&
426 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n", &pl, &match) && match) || 475 match) ||
427 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n", &pl, &match) && match) || 476 (sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss%n", &result.packet_loss,
428 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n", &pl, &match) && match) || 477 &match) == 1 &&
429 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n", &pl, &match) && match) || 478 match) ||
430 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n", &pl, &match) && match) || 479 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n", &result.packet_loss, &match) == 1 &&
431 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 480 match) ||
432 (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 481 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n", &result.packet_loss, &match) == 1 && match) ||
433 (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &pl, &match) && match)) 482 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n", &result.packet_loss, &match) == 1 && match) ||
483 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n", &result.packet_loss, &match) == 1 && match) ||
484 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n", &result.packet_loss, &match) == 1 && match) ==
485 1 ||
486 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n", &result.packet_loss, &match) == 1 &&
487 match) ||
488 (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n", &result.packet_loss, &match) == 1 &&
489 match) ||
490 (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &result.packet_loss, &match) == 1 && match)) {
434 continue; 491 continue;
492 }
435 493
436 /* get the round trip average */ 494 /* get the round trip average */
437 else if ((sscanf(buf, "round-trip min/avg/max = %*f/%f/%*f%n", &rta, &match) && match) || 495 if ((sscanf(buf, "round-trip min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average, &match) == 1 && match) ||
438 (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 496 (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%lf/%*f/%*f%n", &result.round_trip_average, &match) == 1 && match) ||
439 (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 497 (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%lf/%*f/%*f%n", &result.round_trip_average, &match) == 1 && match) ||
440 (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 498 (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%lf/%*f/%*f%n", &result.round_trip_average, &match) == 1 && match) ||
441 (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 499 (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%lf/%*f/%*f%n", &result.round_trip_average, &match) == 1 && match) ||
442 (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%f/%*f%n", &rta, &match) && match) || 500 (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average, &match) == 1 && match) ||
443 (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 501 (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%lf/%*f/%*f%n", &result.round_trip_average, &match) == 1 && match) ||
444 (sscanf(buf, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms%n", &rta, &match) && match) || 502 (sscanf(buf, "rtt min/avg/max/mdev = %*f/%lf/%*f/%*f ms%n", &result.round_trip_average, &match) == 1 && match) ||
445 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %fms%n", &rta, &match) && match)) 503 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %lfms%n", &result.round_trip_average, &match) == 1 && match)) {
446 continue; 504 continue;
505 }
447 } 506 }
448 507
449 /* this is needed because there is no rta if all packets are lost */ 508 /* this is needed because there is no rta if all packets are lost */
450 if (pl == 100) 509 if (result.packet_loss == 100) {
451 rta = crta; 510 result.round_trip_average = crta;
511 }
452 512
453 /* check stderr, setting at least WARNING if there is output here */ 513 /* check stderr, setting at least WARNING if there is output here */
454 /* Add warning into warn_text */ 514 /* Add warning into warn_text */
@@ -459,8 +519,8 @@ int run_ping(const char *cmd, const char *addr) {
459 if (verbose >= 3) { 519 if (verbose >= 3) {
460 printf("Got stderr: %s", buf); 520 printf("Got stderr: %s", buf);
461 } 521 }
462 if ((result = error_scan(buf, addr)) == STATE_OK) { 522 if ((result.state = error_scan(buf, addr)) == STATE_OK) {
463 result = STATE_WARNING; 523 result.state = STATE_WARNING;
464 if (warn_text == NULL) { 524 if (warn_text == NULL) {
465 warn_text = strdup(_("System call sent warnings to stderr ")); 525 warn_text = strdup(_("System call sent warnings to stderr "));
466 } else { 526 } else {
@@ -474,43 +534,46 @@ int run_ping(const char *cmd, const char *addr) {
474 534
475 spclose(child_process); 535 spclose(child_process);
476 536
477 if (warn_text == NULL) 537 if (warn_text == NULL) {
478 warn_text = strdup(""); 538 warn_text = strdup("");
539 }
479 540
480 return result; 541 return result;
481} 542}
482 543
483int error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) { 544mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) {
484 if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") || strstr(buf, "No route")) 545 if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") || strstr(buf, "No route")) {
485 die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr); 546 die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr);
486 else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) 547 } else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) {
487 die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr); 548 die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr);
488 else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) 549 } else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) {
489 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr); 550 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr);
490 else if (strstr(buf, "Destination Protocol Unreachable")) 551 } else if (strstr(buf, "Destination Protocol Unreachable")) {
491 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr); 552 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr);
492 else if (strstr(buf, "Destination Net Prohibited")) 553 } else if (strstr(buf, "Destination Net Prohibited")) {
493 die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr); 554 die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr);
494 else if (strstr(buf, "Destination Host Prohibited")) 555 } else if (strstr(buf, "Destination Host Prohibited")) {
495 die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr); 556 die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr);
496 else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) 557 } else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) {
497 die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr); 558 die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr);
498 else if (strstr(buf, "unknown host")) 559 } else if (strstr(buf, "unknown host")) {
499 die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr); 560 die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr);
500 else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) 561 } else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) {
501 die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr); 562 die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr);
502 else if (strstr(buf, "Destination unreachable: ")) 563 } else if (strstr(buf, "Destination unreachable: ")) {
503 die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr); 564 die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr);
565 }
504 566
505 if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) { 567 if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) {
506 if (warn_text == NULL) 568 if (warn_text == NULL) {
507 warn_text = strdup(_(WARN_DUPLICATES)); 569 warn_text = strdup(_(WARN_DUPLICATES));
508 else if (!strstr(warn_text, _(WARN_DUPLICATES)) && xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) 570 } else if (!strstr(warn_text, _(WARN_DUPLICATES)) && xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) {
509 die(STATE_UNKNOWN, _("Unable to realloc warn_text\n")); 571 die(STATE_UNKNOWN, _("Unable to realloc warn_text\n"));
510 return (STATE_WARNING); 572 }
573 return STATE_WARNING;
511 } 574 }
512 575
513 return (STATE_OK); 576 return STATE_OK;
514} 577}
515 578
516void print_help(void) { 579void print_help(void) {
@@ -551,9 +614,7 @@ void print_help(void) {
551 614
552 printf("\n"); 615 printf("\n");
553 printf("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss")); 616 printf("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss"));
554 printf("%s\n", _("(percentage) and round trip average (milliseconds). It can produce HTML output")); 617 printf("%s\n", _("(percentage) and round trip average (milliseconds). It can produce HTML output."));
555 printf("%s\n", _("linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in"));
556 printf("%s\n", _("the contrib area of the downloads section at http://www.nagios.org/"));
557 618
558 printf(UT_SUPPORT); 619 printf(UT_SUPPORT);
559} 620}
diff --git a/plugins/check_ping.d/config.h b/plugins/check_ping.d/config.h
new file mode 100644
index 00000000..eb2735a7
--- /dev/null
+++ b/plugins/check_ping.d/config.h
@@ -0,0 +1,46 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7enum {
8 UNKNOWN_PACKET_LOSS = 200, /* 200% */
9 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */
10};
11
12#define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
13
14#define MAX_ADDR_START 1
15
16typedef struct {
17 bool display_html;
18 int max_packets;
19
20 char **addresses;
21 size_t n_addresses;
22
23 int wpl;
24 int cpl;
25 double wrta;
26 double crta;
27} check_ping_config;
28
29check_ping_config check_ping_config_init() {
30 check_ping_config tmp = {
31 .display_html = false,
32 .max_packets = -1,
33
34 .addresses = NULL,
35 .n_addresses = 0,
36
37 .wpl = UNKNOWN_PACKET_LOSS,
38 .cpl = UNKNOWN_PACKET_LOSS,
39 .wrta = UNKNOWN_TRIP_TIME,
40 .crta = UNKNOWN_TRIP_TIME,
41 };
42
43 tmp.addresses = calloc(MAX_ADDR_START, sizeof(char *));
44 tmp.addresses[0] = NULL;
45 return tmp;
46}
diff --git a/plugins/check_procs.c b/plugins/check_procs.c
index 1d78ccee..83e6864e 100644
--- a/plugins/check_procs.c
+++ b/plugins/check_procs.c
@@ -1,41 +1,41 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_procs plugin 3 * Monitoring check_procs plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_procs plugin 10 * This file contains the check_procs plugin
11* 11 *
12* Checks all processes and generates WARNING or CRITICAL states if the 12 * Checks all processes and generates WARNING or CRITICAL states if the
13* specified metric is outside the required threshold ranges. The metric 13 * specified metric is outside the required threshold ranges. The metric
14* defaults to number of processes. Search filters can be applied to limit 14 * defaults to number of processes. Search filters can be applied to limit
15* the processes to check. 15 * the processes to check.
16* 16 *
17* The parent process, check_procs itself and any child process of 17 * The parent process, check_procs itself and any child process of
18* check_procs (ps) are excluded from any checks to prevent false positives. 18 * check_procs (ps) are excluded from any checks to prevent false positives.
19* 19 *
20* 20 *
21* This program is free software: you can redistribute it and/or modify 21 * This program is free software: you can redistribute it and/or modify
22* it under the terms of the GNU General Public License as published by 22 * it under the terms of the GNU General Public License as published by
23* the Free Software Foundation, either version 3 of the License, or 23 * the Free Software Foundation, either version 3 of the License, or
24* (at your option) any later version. 24 * (at your option) any later version.
25* 25 *
26* This program is distributed in the hope that it will be useful, 26 * This program is distributed in the hope that it will be useful,
27* but WITHOUT ANY WARRANTY; without even the implied warranty of 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29* GNU General Public License for more details. 29 * GNU General Public License for more details.
30* 30 *
31* You should have received a copy of the GNU General Public License 31 * You should have received a copy of the GNU General Public License
32* along with this program. If not, see <http://www.gnu.org/licenses/>. 32 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33* 33 *
34* 34 *
35*****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_procs"; 37const char *progname = "check_procs";
38const char *program_name = "check_procs"; /* Required for coreutils libs */ 38const char *program_name = "check_procs"; /* Required for coreutils libs */
39const char *copyright = "2000-2024"; 39const char *copyright = "2000-2024";
40const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
41 41
@@ -43,313 +43,288 @@ const char *email = "devel@monitoring-plugins.org";
43#include "utils.h" 43#include "utils.h"
44#include "utils_cmd.h" 44#include "utils_cmd.h"
45#include "regex.h" 45#include "regex.h"
46#include "states.h"
47#include "check_procs.d/config.h"
46 48
47#include <pwd.h> 49#include <pwd.h>
48#include <errno.h> 50#include <errno.h>
49 51
50#ifdef HAVE_SYS_STAT_H 52#ifdef HAVE_SYS_STAT_H
51#include <sys/stat.h> 53# include <sys/stat.h>
52#endif 54#endif
53 55
54static int process_arguments (int /*argc*/, char ** /*argv*/); 56typedef struct {
55static int validate_arguments (void); 57 int errorcode;
56static int convert_to_seconds (char * /*etime*/); 58 check_procs_config config;
57static void print_help (void); 59} check_procs_config_wrapper;
58void print_usage (void); 60static check_procs_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
59 61static check_procs_config_wrapper validate_arguments(check_procs_config_wrapper /*config_wrapper*/);
60static char *warning_range = NULL; 62
61static char *critical_range = NULL; 63static int convert_to_seconds(char * /*etime*/, enum metric /*metric*/);
62static thresholds *procs_thresholds = NULL; 64static void print_help(void);
63 65void print_usage(void);
64static int options = 0; /* bitmask of filter criteria to test against */ 66
65#define ALL 1 67#define ALL 1
66#define STAT 2 68#define STAT 2
67#define PPID 4 69#define PPID 4
68#define USER 8 70#define USER 8
69#define PROG 16 71#define PROG 16
70#define ARGS 32 72#define ARGS 32
71#define VSZ 64 73#define VSZ 64
72#define RSS 128 74#define RSS 128
73#define PCPU 256 75#define PCPU 256
74#define ELAPSED 512 76#define ELAPSED 512
75#define EREG_ARGS 1024 77#define EREG_ARGS 1024
76#define EXCLUDE_PROGS 2048 78#define EXCLUDE_PROGS 2048
77 79
78#define KTHREAD_PARENT "kthreadd" /* the parent process of kernel threads: 80#define KTHREAD_PARENT \
79 ppid of procs are compared to pid of this proc*/ 81 "kthreadd" /* the parent process of kernel threads: \
80 82 ppid of procs are compared to pid of this proc*/
81/* Different metrics */
82char *metric_name;
83enum metric {
84 METRIC_PROCS,
85 METRIC_VSZ,
86 METRIC_RSS,
87 METRIC_CPU,
88 METRIC_ELAPSED
89};
90enum metric metric = METRIC_PROCS;
91 83
92static int verbose = 0; 84static int verbose = 0;
93static int uid; 85
94static pid_t ppid; 86static int stat_exe(const pid_t pid, struct stat *buf) {
95static int vsz;
96static int rss;
97static float pcpu;
98static char *statopts;
99static char *prog;
100static char *exclude_progs;
101static char **exclude_progs_arr = NULL;
102static char exclude_progs_counter = 0;
103static char *args;
104static char *input_filename = NULL;
105static regex_t re_args;
106static char *fmt;
107static char *fails;
108static char tmp[MAX_INPUT_BUFFER];
109static int kthread_filter = 0;
110static int usepid = 0; /* whether to test for pid or /proc/pid/exe */
111
112static int
113stat_exe (const pid_t pid, struct stat *buf) {
114 char *path; 87 char *path;
115 int ret;
116 xasprintf(&path, "/proc/%d/exe", pid); 88 xasprintf(&path, "/proc/%d/exe", pid);
117 ret = stat(path, buf); 89 int ret = stat(path, buf);
118 free(path); 90 free(path);
119 return ret; 91 return ret;
120} 92}
121 93
122 94int main(int argc, char **argv) {
123int 95 setlocale(LC_ALL, "");
124main (int argc, char **argv)
125{
126 char *input_buffer;
127 char *input_line;
128 char *procprog;
129
130 pid_t mypid = 0;
131 pid_t myppid = 0;
132 struct stat statbuf;
133 dev_t mydev = 0;
134 ino_t myino = 0;
135 int procuid = 0;
136 pid_t procpid = 0;
137 pid_t procppid = 0;
138 pid_t kthread_ppid = 0;
139 int procvsz = 0;
140 int procrss = 0;
141 int procseconds = 0;
142 float procpcpu = 0;
143 char procstat[8];
144 char procetime[MAX_INPUT_BUFFER] = { '\0' };
145 char *procargs;
146
147 const char *zombie = "Z";
148
149 int resultsum = 0; /* bitmask of the filter criteria met by a process */
150 int found = 0; /* counter for number of lines returned in `ps` output */
151 int procs = 0; /* counter for number of processes meeting filter criteria */
152 int pos; /* number of spaces before 'args' in `ps` output */
153 int cols; /* number of columns in ps output */
154 int expected_cols = PS_COLS - 1;
155 int warn = 0; /* number of processes in warn state */
156 int crit = 0; /* number of processes in crit state */
157 int i = 0;
158 int result = STATE_UNKNOWN;
159 int ret = 0;
160 output chld_out, chld_err;
161
162 setlocale (LC_ALL, "");
163 bindtextdomain (PACKAGE, LOCALEDIR);
164 textdomain (PACKAGE);
165 setlocale(LC_NUMERIC, "POSIX"); 96 setlocale(LC_NUMERIC, "POSIX");
166 97 bindtextdomain(PACKAGE, LOCALEDIR);
167 input_buffer = malloc (MAX_INPUT_BUFFER); 98 textdomain(PACKAGE);
168 procprog = malloc (MAX_INPUT_BUFFER);
169
170 xasprintf (&metric_name, "PROCS");
171 metric = METRIC_PROCS;
172 99
173 /* Parse extra opts if any */ 100 /* Parse extra opts if any */
174 argv=np_extra_opts (&argc, argv, progname); 101 argv = np_extra_opts(&argc, argv, progname);
102
103 check_procs_config_wrapper tmp_config = process_arguments(argc, argv);
104 if (tmp_config.errorcode == ERROR) {
105 usage4(_("Could not parse arguments"));
106 }
175 107
176 if (process_arguments (argc, argv) == ERROR) 108 check_procs_config config = tmp_config.config;
177 usage4 (_("Could not parse arguments"));
178 109
179 /* find ourself */ 110 /* find ourself */
180 mypid = getpid(); 111 pid_t mypid = getpid();
181 myppid = getppid(); 112 pid_t myppid = getppid();
182 if (usepid || stat_exe(mypid, &statbuf) == -1) { 113 dev_t mydev = 0;
114 ino_t myino = 0;
115 struct stat statbuf;
116 if (config.usepid || stat_exe(mypid, &statbuf) == -1) {
183 /* usepid might have been set by -T */ 117 /* usepid might have been set by -T */
184 usepid = 1; 118 config.usepid = true;
185 } else { 119 } else {
186 usepid = 0; 120 config.usepid = false;
187 mydev = statbuf.st_dev; 121 mydev = statbuf.st_dev;
188 myino = statbuf.st_ino; 122 myino = statbuf.st_ino;
189 } 123 }
190 124
191 /* Set signal handling and alarm timeout */ 125 /* Set signal handling and alarm timeout */
192 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 126 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
193 die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); 127 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
194 } 128 }
195 (void) alarm ((unsigned) timeout_interval); 129 (void)alarm(timeout_interval);
196 130
197 if (verbose >= 2) 131 if (verbose >= 2) {
198 printf (_("CMD: %s\n"), PS_COMMAND); 132 printf(_("CMD: %s\n"), PS_COMMAND);
133 }
199 134
200 if (input_filename == NULL) { 135 output chld_out;
201 result = cmd_run( PS_COMMAND, &chld_out, &chld_err, 0); 136 output chld_err;
137 mp_state_enum result = STATE_UNKNOWN;
138 if (config.input_filename == NULL) {
139 result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0);
202 if (chld_err.lines > 0) { 140 if (chld_err.lines > 0) {
203 printf ("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); 141 printf("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]);
204 exit(STATE_WARNING); 142 exit(STATE_WARNING);
205 } 143 }
206 } else { 144 } else {
207 result = cmd_file_read( input_filename, &chld_out, 0); 145 result = cmd_file_read(config.input_filename, &chld_out, 0);
208 } 146 }
209 147
148 int pos; /* number of spaces before 'args' in `ps` output */
149 uid_t procuid = 0;
150 pid_t procpid = 0;
151 pid_t procppid = 0;
152 pid_t kthread_ppid = 0;
153 int warn = 0; /* number of processes in warn state */
154 int crit = 0; /* number of processes in crit state */
155 int procvsz = 0;
156 int procrss = 0;
157 int procseconds = 0;
158 float procpcpu = 0;
159 char procstat[8];
160 char procetime[MAX_INPUT_BUFFER] = {'\0'};
161 int resultsum = 0; /* bitmask of the filter criteria met by a process */
162 int found = 0; /* counter for number of lines returned in `ps` output */
163 int procs = 0; /* counter for number of processes meeting filter criteria */
164 char *input_buffer = malloc(MAX_INPUT_BUFFER);
165 char *procprog = malloc(MAX_INPUT_BUFFER);
166 const int expected_cols = PS_COLS - 1;
167
210 /* flush first line: j starts at 1 */ 168 /* flush first line: j starts at 1 */
211 for (size_t j = 1; j < chld_out.lines; j++) { 169 for (size_t j = 1; j < chld_out.lines; j++) {
212 input_line = chld_out.line[j]; 170 char *input_line = chld_out.line[j];
213 171
214 if (verbose >= 3) 172 if (verbose >= 3) {
215 printf ("%s", input_line); 173 printf("%s", input_line);
174 }
216 175
217 strcpy (procprog, ""); 176 strcpy(procprog, "");
218 xasprintf (&procargs, "%s", ""); 177 char *procargs;
178 xasprintf(&procargs, "%s", "");
219 179
220 cols = sscanf (input_line, PS_FORMAT, PS_VARLIST); 180 /* number of columns in ps output */
181 int cols = sscanf(input_line, PS_FORMAT, PS_VARLIST);
221 182
222 /* Zombie processes do not give a procprog command */ 183 /* Zombie processes do not give a procprog command */
223 if ( cols < expected_cols && strstr(procstat, zombie) ) { 184 const char *zombie = "Z";
185 if (cols < expected_cols && strstr(procstat, zombie)) {
224 cols = expected_cols; 186 cols = expected_cols;
225 } 187 }
226 if ( cols >= expected_cols ) { 188 if (cols >= expected_cols) {
227 resultsum = 0; 189 resultsum = 0;
228 xasprintf (&procargs, "%s", input_line + pos); 190 xasprintf(&procargs, "%s", input_line + pos);
229 strip (procargs); 191 strip(procargs);
230 192
231 /* Some ps return full pathname for command. This removes path */ 193 /* Some ps return full pathname for command. This removes path */
232 strcpy(procprog, base_name(procprog)); 194 strcpy(procprog, base_name(procprog));
233 195
234 /* we need to convert the elapsed time to seconds */ 196 /* we need to convert the elapsed time to seconds */
235 procseconds = convert_to_seconds(procetime); 197 procseconds = convert_to_seconds(procetime, config.metric);
236 198
237 if (verbose >= 3) 199 if (verbose >= 3) {
238 printf ("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 200 printf("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", procs, procuid, procvsz,
239 procs, procuid, procvsz, procrss, 201 procrss, procpid, procppid, procpcpu, procstat, procetime, procprog, procargs);
240 procpid, procppid, procpcpu, procstat, 202 }
241 procetime, procprog, procargs);
242 203
243 /* Ignore self */ 204 /* Ignore self */
244 if ((usepid && mypid == procpid) || 205 int ret = 0;
245 ( ((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || 206 if ((config.usepid && mypid == procpid) ||
246 (ret == -1 && errno == ENOENT)) 207 (((!config.usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) ||
247 ) { 208 (ret == -1 && errno == ENOENT))) {
248 if (verbose >= 3) 209 if (verbose >= 3) {
249 printf("not considering - is myself or gone\n"); 210 printf("not considering - is myself or gone\n");
211 }
250 continue; 212 continue;
251 } 213 }
252 /* Ignore parent*/ 214 /* Ignore parent*/
253 else if (myppid == procpid) { 215 if (myppid == procpid) {
254 if (verbose >= 3) 216 if (verbose >= 3) {
255 printf("not considering - is parent\n"); 217 printf("not considering - is parent\n");
218 }
256 continue; 219 continue;
257 } 220 }
258 221
259 /* Ignore our own children */ 222 /* Ignore our own children */
260 if (procppid == mypid) { 223 if (procppid == mypid) {
261 if (verbose >= 3) 224 if (verbose >= 3) {
262 printf("not considering - is our child\n"); 225 printf("not considering - is our child\n");
226 }
263 continue; 227 continue;
264 } 228 }
265 229
266 /* Ignore excluded processes by name */ 230 /* Ignore excluded processes by name */
267 if(options & EXCLUDE_PROGS) { 231 if (config.options & EXCLUDE_PROGS) {
268 int found = 0; 232 bool found = false;
269 int i = 0; 233 for (int i = 0; i < (config.exclude_progs_counter); i++) {
270 234 if (!strcmp(procprog, config.exclude_progs_arr[i])) {
271 for(i=0; i < (exclude_progs_counter); i++) { 235 found = true;
272 if(!strcmp(procprog, exclude_progs_arr[i])) { 236 }
273 found = 1; 237 }
274 } 238 if (!found) {
275 } 239 resultsum |= EXCLUDE_PROGS;
276 if(found == 0) { 240 } else {
277 resultsum |= EXCLUDE_PROGS; 241 if (verbose >= 3) {
278 } else 242 printf("excluding - by ignorelist\n");
279 { 243 }
280 if(verbose >= 3) 244 }
281 printf("excluding - by ignorelist\n");
282 }
283 } 245 }
284 246
285 /* filter kernel threads (children of KTHREAD_PARENT)*/ 247 /* filter kernel threads (children of KTHREAD_PARENT)*/
286 /* TODO adapt for other OSes than GNU/Linux 248 /* TODO adapt for other OSes than GNU/Linux
287 sorry for not doing that, but I've no other OSes to test :-( */ 249 sorry for not doing that, but I've no other OSes to test :-( */
288 if (kthread_filter == 1) { 250 if (config.kthread_filter) {
289 /* get pid KTHREAD_PARENT */ 251 /* get pid KTHREAD_PARENT */
290 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT) ) 252 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) {
291 kthread_ppid = procpid; 253 kthread_ppid = procpid;
254 }
292 255
293 if (kthread_ppid == procppid) { 256 if (kthread_ppid == procppid) {
294 if (verbose >= 2) 257 if (verbose >= 2) {
295 printf ("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs); 258 printf("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs);
259 }
296 continue; 260 continue;
297 } 261 }
298 } 262 }
299 263
300 if ((options & STAT) && (strstr (procstat, statopts))) 264 if ((config.options & STAT) && (strstr(procstat, config.statopts))) {
301 resultsum |= STAT; 265 resultsum |= STAT;
302 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL)) 266 }
267 if ((config.options & ARGS) && procargs && (strstr(procargs, config.args) != NULL)) {
303 resultsum |= ARGS; 268 resultsum |= ARGS;
304 if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t) 0, NULL, 0) == 0)) 269 }
270 if ((config.options & EREG_ARGS) && procargs && (regexec(&config.re_args, procargs, (size_t)0, NULL, 0) == 0)) {
305 resultsum |= EREG_ARGS; 271 resultsum |= EREG_ARGS;
306 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0)) 272 }
273 if ((config.options & PROG) && procprog && (strcmp(config.prog, procprog) == 0)) {
307 resultsum |= PROG; 274 resultsum |= PROG;
308 if ((options & PPID) && (procppid == ppid)) 275 }
276 if ((config.options & PPID) && (procppid == config.ppid)) {
309 resultsum |= PPID; 277 resultsum |= PPID;
310 if ((options & USER) && (procuid == uid)) 278 }
279 if ((config.options & USER) && (procuid == config.uid)) {
311 resultsum |= USER; 280 resultsum |= USER;
312 if ((options & VSZ) && (procvsz >= vsz)) 281 }
282 if ((config.options & VSZ) && (procvsz >= config.vsz)) {
313 resultsum |= VSZ; 283 resultsum |= VSZ;
314 if ((options & RSS) && (procrss >= rss)) 284 }
285 if ((config.options & RSS) && (procrss >= config.rss)) {
315 resultsum |= RSS; 286 resultsum |= RSS;
316 if ((options & PCPU) && (procpcpu >= pcpu)) 287 }
288 if ((config.options & PCPU) && (procpcpu >= config.pcpu)) {
317 resultsum |= PCPU; 289 resultsum |= PCPU;
290 }
318 291
319 found++; 292 found++;
320 293
321 /* Next line if filters not matched */ 294 /* Next line if filters not matched */
322 if (!(options == resultsum || options == ALL)) 295 if (!(config.options == resultsum || config.options == ALL)) {
323 continue; 296 continue;
297 }
324 298
325 procs++; 299 procs++;
326 if (verbose >= 2) { 300 if (verbose >= 2) {
327 printf ("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 301 printf("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", procuid, procvsz,
328 procuid, procvsz, procrss, 302 procrss, procpid, procppid, procpcpu, procstat, procetime, procprog, procargs);
329 procpid, procppid, procpcpu, procstat,
330 procetime, procprog, procargs);
331 } 303 }
332 304
333 if (metric == METRIC_VSZ) 305 mp_state_enum temporary_result = STATE_OK;
334 i = get_status ((double)procvsz, procs_thresholds); 306 if (config.metric == METRIC_VSZ) {
335 else if (metric == METRIC_RSS) 307 temporary_result = get_status((double)procvsz, config.procs_thresholds);
336 i = get_status ((double)procrss, procs_thresholds); 308 } else if (config.metric == METRIC_RSS) {
309 temporary_result = get_status((double)procrss, config.procs_thresholds);
310 }
337 /* TODO? float thresholds for --metric=CPU */ 311 /* TODO? float thresholds for --metric=CPU */
338 else if (metric == METRIC_CPU) 312 else if (config.metric == METRIC_CPU) {
339 i = get_status (procpcpu, procs_thresholds); 313 temporary_result = get_status(procpcpu, config.procs_thresholds);
340 else if (metric == METRIC_ELAPSED) 314 } else if (config.metric == METRIC_ELAPSED) {
341 i = get_status ((double)procseconds, procs_thresholds); 315 temporary_result = get_status((double)procseconds, config.procs_thresholds);
316 }
342 317
343 if (metric != METRIC_PROCS) { 318 if (config.metric != METRIC_PROCS) {
344 if (i == STATE_WARNING) { 319 if (temporary_result == STATE_WARNING) {
345 warn++; 320 warn++;
346 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 321 xasprintf(&config.fails, "%s%s%s", config.fails, (strcmp(config.fails, "") ? ", " : ""), procprog);
347 result = max_state (result, i); 322 result = max_state(result, temporary_result);
348 } 323 }
349 if (i == STATE_CRITICAL) { 324 if (temporary_result == STATE_CRITICAL) {
350 crit++; 325 crit++;
351 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 326 xasprintf(&config.fails, "%s%s%s", config.fails, (strcmp(config.fails, "") ? ", " : ""), procprog);
352 result = max_state (result, i); 327 result = max_state(result, temporary_result);
353 } 328 }
354 } 329 }
355 } 330 }
@@ -359,339 +334,350 @@ main (int argc, char **argv)
359 } 334 }
360 } 335 }
361 336
362 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ 337 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
363 printf (_("Unable to read output\n")); 338 printf(_("Unable to read output\n"));
364 return STATE_UNKNOWN; 339 return STATE_UNKNOWN;
365 } 340 }
366 341
367 if ( result == STATE_UNKNOWN ) 342 if (result == STATE_UNKNOWN) {
368 result = STATE_OK; 343 result = STATE_OK;
344 }
369 345
370 /* Needed if procs found, but none match filter */ 346 /* Needed if procs found, but none match filter */
371 if ( metric == METRIC_PROCS ) { 347 if (config.metric == METRIC_PROCS) {
372 result = max_state (result, get_status ((double)procs, procs_thresholds) ); 348 result = max_state(result, get_status((double)procs, config.procs_thresholds));
373 } 349 }
374 350
375 if ( result == STATE_OK ) { 351 if (result == STATE_OK) {
376 printf ("%s %s: ", metric_name, _("OK")); 352 printf("%s %s: ", config.metric_name, _("OK"));
377 } else if (result == STATE_WARNING) { 353 } else if (result == STATE_WARNING) {
378 printf ("%s %s: ", metric_name, _("WARNING")); 354 printf("%s %s: ", config.metric_name, _("WARNING"));
379 if ( metric != METRIC_PROCS ) { 355 if (config.metric != METRIC_PROCS) {
380 printf (_("%d warn out of "), warn); 356 printf(_("%d warn out of "), warn);
381 } 357 }
382 } else if (result == STATE_CRITICAL) { 358 } else if (result == STATE_CRITICAL) {
383 printf ("%s %s: ", metric_name, _("CRITICAL")); 359 printf("%s %s: ", config.metric_name, _("CRITICAL"));
384 if (metric != METRIC_PROCS) { 360 if (config.metric != METRIC_PROCS) {
385 printf (_("%d crit, %d warn out of "), crit, warn); 361 printf(_("%d crit, %d warn out of "), crit, warn);
386 } 362 }
387 } 363 }
388 printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs); 364 printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs);
389 365
390 if (strcmp(fmt,"") != 0) { 366 if (strcmp(config.fmt, "") != 0) {
391 printf (_(" with %s"), fmt); 367 printf(_(" with %s"), config.fmt);
392 } 368 }
393 369
394 if ( verbose >= 1 && strcmp(fails,"") ) 370 if (verbose >= 1 && strcmp(config.fails, "")) {
395 printf (" [%s]", fails); 371 printf(" [%s]", config.fails);
372 }
396 373
397 if (metric == METRIC_PROCS) 374 if (config.metric == METRIC_PROCS) {
398 printf (" | procs=%d;%s;%s;0;", procs, 375 printf(" | procs=%d;%s;%s;0;", procs, config.warning_range ? config.warning_range : "",
399 warning_range ? warning_range : "", 376 config.critical_range ? config.critical_range : "");
400 critical_range ? critical_range : ""); 377 } else {
401 else 378 printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit);
402 printf (" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); 379 }
403 380
404 printf ("\n"); 381 printf("\n");
405 return result; 382 exit(result);
406} 383}
407 384
408
409
410/* process command-line arguments */ 385/* process command-line arguments */
411int 386check_procs_config_wrapper process_arguments(int argc, char **argv) {
412process_arguments (int argc, char **argv) 387 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
413{ 388 {"critical", required_argument, 0, 'c'},
414 int c = 1; 389 {"metric", required_argument, 0, 'm'},
415 char *user; 390 {"timeout", required_argument, 0, 't'},
416 struct passwd *pw; 391 {"status", required_argument, 0, 's'},
417 int option = 0; 392 {"ppid", required_argument, 0, 'p'},
418 int err; 393 {"user", required_argument, 0, 'u'},
419 int cflags = REG_NOSUB | REG_EXTENDED; 394 {"command", required_argument, 0, 'C'},
420 char errbuf[MAX_INPUT_BUFFER]; 395 {"vsz", required_argument, 0, 'z'},
421 char *temp_string; 396 {"rss", required_argument, 0, 'r'},
422 int i=0; 397 {"pcpu", required_argument, 0, 'P'},
423 static struct option longopts[] = { 398 {"elapsed", required_argument, 0, 'e'},
424 {"warning", required_argument, 0, 'w'}, 399 {"argument-array", required_argument, 0, 'a'},
425 {"critical", required_argument, 0, 'c'}, 400 {"help", no_argument, 0, 'h'},
426 {"metric", required_argument, 0, 'm'}, 401 {"version", no_argument, 0, 'V'},
427 {"timeout", required_argument, 0, 't'}, 402 {"verbose", no_argument, 0, 'v'},
428 {"status", required_argument, 0, 's'}, 403 {"ereg-argument-array", required_argument, 0, CHAR_MAX + 1},
429 {"ppid", required_argument, 0, 'p'}, 404 {"input-file", required_argument, 0, CHAR_MAX + 2},
430 {"user", required_argument, 0, 'u'}, 405 {"no-kthreads", required_argument, 0, 'k'},
431 {"command", required_argument, 0, 'C'}, 406 {"traditional-filter", no_argument, 0, 'T'},
432 {"vsz", required_argument, 0, 'z'}, 407 {"exclude-process", required_argument, 0, 'X'},
433 {"rss", required_argument, 0, 'r'}, 408 {0, 0, 0, 0}};
434 {"pcpu", required_argument, 0, 'P'}, 409
435 {"elapsed", required_argument, 0, 'e'}, 410 for (int index = 1; index < argc; index++) {
436 {"argument-array", required_argument, 0, 'a'}, 411 if (strcmp("-to", argv[index]) == 0) {
437 {"help", no_argument, 0, 'h'}, 412 strcpy(argv[index], "-t");
438 {"version", no_argument, 0, 'V'}, 413 }
439 {"verbose", no_argument, 0, 'v'}, 414 }
440 {"ereg-argument-array", required_argument, 0, CHAR_MAX+1},
441 {"input-file", required_argument, 0, CHAR_MAX+2},
442 {"no-kthreads", required_argument, 0, 'k'},
443 {"traditional-filter", no_argument, 0, 'T'},
444 {"exclude-process", required_argument, 0, 'X'},
445 {0, 0, 0, 0}
446 };
447 415
448 for (c = 1; c < argc; c++) 416 check_procs_config_wrapper result = {
449 if (strcmp ("-to", argv[c]) == 0) 417 .errorcode = OK,
450 strcpy (argv[c], "-t"); 418 .config = check_procs_config_init(),
419 };
451 420
452 while (1) { 421 while (true) {
453 c = getopt_long (argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", 422 int option = 0;
454 longopts, &option); 423 int option_index = getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option);
455 424
456 if (c == -1 || c == EOF) 425 if (option_index == -1 || option_index == EOF) {
457 break; 426 break;
427 }
458 428
459 switch (c) { 429 switch (option_index) {
460 case '?': /* help */ 430 case '?': /* help */
461 usage5 (); 431 usage5();
462 case 'h': /* help */ 432 case 'h': /* help */
463 print_help (); 433 print_help();
464 exit (STATE_UNKNOWN); 434 exit(STATE_UNKNOWN);
465 case 'V': /* version */ 435 case 'V': /* version */
466 print_revision (progname, NP_VERSION); 436 print_revision(progname, NP_VERSION);
467 exit (STATE_UNKNOWN); 437 exit(STATE_UNKNOWN);
468 case 't': /* timeout period */ 438 case 't': /* timeout period */
469 if (!is_integer (optarg)) 439 if (!is_integer(optarg)) {
470 usage2 (_("Timeout interval must be a positive integer"), optarg); 440 usage2(_("Timeout interval must be a positive integer"), optarg);
471 else 441 } else {
472 timeout_interval = atoi (optarg); 442 timeout_interval = atoi(optarg);
443 }
473 break; 444 break;
474 case 'c': /* critical threshold */ 445 case 'c': /* critical threshold */
475 critical_range = optarg; 446 result.config.critical_range = optarg;
476 break; 447 break;
477 case 'w': /* warning threshold */ 448 case 'w': /* warning threshold */
478 warning_range = optarg; 449 result.config.warning_range = optarg;
479 break; 450 break;
480 case 'p': /* process id */ 451 case 'p': { /* process id */
481 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) { 452 static char tmp[MAX_INPUT_BUFFER];
482 xasprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid); 453 if (sscanf(optarg, "%d%[^0-9]", &result.config.ppid, tmp) == 1) {
483 options |= PPID; 454 xasprintf(&result.config.fmt, "%s%sPPID = %d", (result.config.fmt ? result.config.fmt : ""),
455 (result.config.options ? ", " : ""), result.config.ppid);
456 result.config.options |= PPID;
484 break; 457 break;
485 } 458 }
486 usage4 (_("Parent Process ID must be an integer!")); 459 usage4(_("Parent Process ID must be an integer!"));
487 case 's': /* status */ 460 }
488 if (statopts) 461 case 's': /* status */
462 if (result.config.statopts) {
489 break; 463 break;
490 else 464 } else {
491 statopts = optarg; 465 result.config.statopts = optarg;
492 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 466 }
493 options |= STAT; 467 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"), (result.config.fmt ? result.config.fmt : ""),
468 (result.config.options ? ", " : ""), result.config.statopts);
469 result.config.options |= STAT;
494 break; 470 break;
495 case 'u': /* user or user id */ 471 case 'u': /* user or user id */ {
496 if (is_integer (optarg)) { 472 struct passwd *pw;
497 uid = atoi (optarg); 473 if (is_integer(optarg)) {
498 pw = getpwuid ((uid_t) uid); 474 result.config.uid = atoi(optarg);
475 pw = getpwuid(result.config.uid);
499 /* check to be sure user exists */ 476 /* check to be sure user exists */
500 if (pw == NULL) 477 if (pw == NULL) {
501 usage2 (_("UID was not found"), optarg); 478 usage2(_("UID was not found"), optarg);
502 } 479 }
503 else { 480 } else {
504 pw = getpwnam (optarg); 481 pw = getpwnam(optarg);
505 /* check to be sure user exists */ 482 /* check to be sure user exists */
506 if (pw == NULL) 483 if (pw == NULL) {
507 usage2 (_("User name was not found"), optarg); 484 usage2(_("User name was not found"), optarg);
485 }
508 /* then get uid */ 486 /* then get uid */
509 uid = pw->pw_uid; 487 result.config.uid = pw->pw_uid;
510 } 488 }
511 user = pw->pw_name; 489
512 xasprintf (&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), 490 char *user = pw->pw_name;
513 uid, user); 491 xasprintf(&result.config.fmt, "%s%sUID = %d (%s)", (result.config.fmt ? result.config.fmt : ""),
514 options |= USER; 492 (result.config.options ? ", " : ""), result.config.uid, user);
515 break; 493 result.config.options |= USER;
516 case 'C': /* command */ 494 } break;
495 case 'C': /* command */
517 /* TODO: allow this to be passed in with --metric */ 496 /* TODO: allow this to be passed in with --metric */
518 if (prog) 497 if (result.config.prog) {
519 break; 498 break;
520 else 499 } else {
521 prog = optarg; 500 result.config.prog = optarg;
522 xasprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 501 }
523 prog); 502 xasprintf(&result.config.fmt, _("%s%scommand name '%s'"), (result.config.fmt ? result.config.fmt : ""),
524 options |= PROG; 503 (result.config.options ? ", " : ""), result.config.prog);
504 result.config.options |= PROG;
525 break; 505 break;
526 case 'X': 506 case 'X':
527 if(exclude_progs) 507 if (result.config.exclude_progs) {
528 break; 508 break;
529 else 509 } else {
530 exclude_progs = optarg; 510 result.config.exclude_progs = optarg;
531 xasprintf (&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 511 }
532 exclude_progs); 512 xasprintf(&result.config.fmt, _("%s%sexclude progs '%s'"), (result.config.fmt ? result.config.fmt : ""),
533 char *p = strtok(exclude_progs, ","); 513 (result.config.options ? ", " : ""), result.config.exclude_progs);
534 514 char *tmp_pointer = strtok(result.config.exclude_progs, ",");
535 while(p){ 515
536 exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char*) * ++exclude_progs_counter); 516 while (tmp_pointer) {
537 exclude_progs_arr[exclude_progs_counter-1] = p; 517 result.config.exclude_progs_arr =
538 p = strtok(NULL, ","); 518 realloc(result.config.exclude_progs_arr, sizeof(char *) * ++result.config.exclude_progs_counter);
519 result.config.exclude_progs_arr[result.config.exclude_progs_counter - 1] = tmp_pointer;
520 tmp_pointer = strtok(NULL, ",");
539 } 521 }
540 522
541 options |= EXCLUDE_PROGS; 523 result.config.options |= EXCLUDE_PROGS;
542 break; 524 break;
543 case 'a': /* args (full path name with args) */ 525 case 'a': /* args (full path name with args) */
544 /* TODO: allow this to be passed in with --metric */ 526 /* TODO: allow this to be passed in with --metric */
545 if (args) 527 if (result.config.args) {
546 break; 528 break;
547 else 529 } else {
548 args = optarg; 530 result.config.args = optarg;
549 xasprintf (&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); 531 }
550 options |= ARGS; 532 xasprintf(&result.config.fmt, "%s%sargs '%s'", (result.config.fmt ? result.config.fmt : ""),
533 (result.config.options ? ", " : ""), result.config.args);
534 result.config.options |= ARGS;
551 break; 535 break;
552 case CHAR_MAX+1: 536 case CHAR_MAX + 1: {
553 err = regcomp(&re_args, optarg, cflags); 537 int cflags = REG_NOSUB | REG_EXTENDED;
538 int err = regcomp(&result.config.re_args, optarg, cflags);
554 if (err != 0) { 539 if (err != 0) {
555 regerror (err, &re_args, errbuf, MAX_INPUT_BUFFER); 540 char errbuf[MAX_INPUT_BUFFER];
556 die (STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 541 regerror(err, &result.config.re_args, errbuf, MAX_INPUT_BUFFER);
542 die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf);
557 } 543 }
558 /* Strip off any | within the regex optarg */ 544 /* Strip off any | within the regex optarg */
559 temp_string = strdup(optarg); 545 char *temp_string = strdup(optarg);
560 while(temp_string[i]!='\0'){ 546 int index = 0;
561 if(temp_string[i]=='|') 547 while (temp_string[index] != '\0') {
562 temp_string[i]=','; 548 if (temp_string[index] == '|') {
563 i++; 549 temp_string[index] = ',';
564 } 550 }
565 xasprintf (&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); 551 index++;
566 options |= EREG_ARGS; 552 }
567 break; 553 xasprintf(&result.config.fmt, "%s%sregex args '%s'", (result.config.fmt ? result.config.fmt : ""),
568 case 'r': /* RSS */ 554 (result.config.options ? ", " : ""), temp_string);
569 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) { 555 result.config.options |= EREG_ARGS;
570 xasprintf (&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); 556 } break;
571 options |= RSS; 557 case 'r': { /* RSS */
558 static char tmp[MAX_INPUT_BUFFER];
559 if (sscanf(optarg, "%d%[^0-9]", &result.config.rss, tmp) == 1) {
560 xasprintf(&result.config.fmt, "%s%sRSS >= %d", (result.config.fmt ? result.config.fmt : ""),
561 (result.config.options ? ", " : ""), result.config.rss);
562 result.config.options |= RSS;
572 break; 563 break;
573 } 564 }
574 usage4 (_("RSS must be an integer!")); 565 usage4(_("RSS must be an integer!"));
575 case 'z': /* VSZ */ 566 }
576 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) { 567 case 'z': { /* VSZ */
577 xasprintf (&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); 568 static char tmp[MAX_INPUT_BUFFER];
578 options |= VSZ; 569 if (sscanf(optarg, "%d%[^0-9]", &result.config.vsz, tmp) == 1) {
570 xasprintf(&result.config.fmt, "%s%sVSZ >= %d", (result.config.fmt ? result.config.fmt : ""),
571 (result.config.options ? ", " : ""), result.config.vsz);
572 result.config.options |= VSZ;
579 break; 573 break;
580 } 574 }
581 usage4 (_("VSZ must be an integer!")); 575 usage4(_("VSZ must be an integer!"));
582 case 'P': /* PCPU */ 576 }
577 case 'P': { /* PCPU */
583 /* TODO: -P 1.5.5 is accepted */ 578 /* TODO: -P 1.5.5 is accepted */
584 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { 579 static char tmp[MAX_INPUT_BUFFER];
585 xasprintf (&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); 580 if (sscanf(optarg, "%f%[^0-9.]", &result.config.pcpu, tmp) == 1) {
586 options |= PCPU; 581 xasprintf(&result.config.fmt, "%s%sPCPU >= %.2f", (result.config.fmt ? result.config.fmt : ""),
582 (result.config.options ? ", " : ""), result.config.pcpu);
583 result.config.options |= PCPU;
587 break; 584 break;
588 } 585 }
589 usage4 (_("PCPU must be a float!")); 586 usage4(_("PCPU must be a float!"));
587 }
590 case 'm': 588 case 'm':
591 xasprintf (&metric_name, "%s", optarg); 589 xasprintf(&result.config.metric_name, "%s", optarg);
592 if ( strcmp(optarg, "PROCS") == 0) { 590 if (strcmp(optarg, "PROCS") == 0) {
593 metric = METRIC_PROCS; 591 result.config.metric = METRIC_PROCS;
594 break; 592 break;
595 } 593 }
596 else if ( strcmp(optarg, "VSZ") == 0) { 594 if (strcmp(optarg, "VSZ") == 0) {
597 metric = METRIC_VSZ; 595 result.config.metric = METRIC_VSZ;
598 break; 596 break;
599 } 597 }
600 else if ( strcmp(optarg, "RSS") == 0 ) { 598 if (strcmp(optarg, "RSS") == 0) {
601 metric = METRIC_RSS; 599 result.config.metric = METRIC_RSS;
602 break; 600 break;
603 } 601 }
604 else if ( strcmp(optarg, "CPU") == 0 ) { 602 if (strcmp(optarg, "CPU") == 0) {
605 metric = METRIC_CPU; 603 result.config.metric = METRIC_CPU;
606 break; 604 break;
607 } 605 }
608 else if ( strcmp(optarg, "ELAPSED") == 0) { 606 if (strcmp(optarg, "ELAPSED") == 0) {
609 metric = METRIC_ELAPSED; 607 result.config.metric = METRIC_ELAPSED;
610 break; 608 break;
611 } 609 }
612 610
613 usage4 (_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); 611 usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!"));
614 case 'k': /* linux kernel thread filter */ 612 case 'k': /* linux kernel thread filter */
615 kthread_filter = 1; 613 result.config.kthread_filter = true;
616 break; 614 break;
617 case 'v': /* command */ 615 case 'v': /* command */
618 verbose++; 616 verbose++;
619 break; 617 break;
620 case 'T': 618 case 'T':
621 usepid = 1; 619 result.config.usepid = true;
622 break; 620 break;
623 case CHAR_MAX+2: 621 case CHAR_MAX + 2:
624 input_filename = optarg; 622 result.config.input_filename = optarg;
625 break; 623 break;
626 } 624 }
627 } 625 }
628 626
629 c = optind; 627 int index = optind;
630 if ((! warning_range) && argv[c]) 628 if ((!result.config.warning_range) && argv[index]) {
631 warning_range = argv[c++]; 629 result.config.warning_range = argv[index++];
632 if ((! critical_range) && argv[c]) 630 }
633 critical_range = argv[c++]; 631 if ((!result.config.critical_range) && argv[index]) {
634 if (statopts == NULL && argv[c]) { 632 result.config.critical_range = argv[index++];
635 xasprintf (&statopts, "%s", argv[c++]); 633 }
636 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 634 if (result.config.statopts == NULL && argv[index]) {
637 options |= STAT; 635 xasprintf(&result.config.statopts, "%s", argv[index++]);
636 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"), (result.config.fmt ? result.config.fmt : ""),
637 (result.config.options ? ", " : ""), result.config.statopts);
638 result.config.options |= STAT;
638 } 639 }
639 640
640 /* this will abort in case of invalid ranges */ 641 /* this will abort in case of invalid ranges */
641 set_thresholds (&procs_thresholds, warning_range, critical_range); 642 set_thresholds(&result.config.procs_thresholds, result.config.warning_range, result.config.critical_range);
642 643
643 return validate_arguments (); 644 return validate_arguments(result);
644} 645}
645 646
647check_procs_config_wrapper validate_arguments(check_procs_config_wrapper config_wrapper) {
648 if (config_wrapper.config.options == 0) {
649 config_wrapper.config.options = ALL;
650 }
646 651
652 if (config_wrapper.config.statopts == NULL) {
653 config_wrapper.config.statopts = strdup("");
654 }
647 655
648int 656 if (config_wrapper.config.prog == NULL) {
649validate_arguments () 657 config_wrapper.config.prog = strdup("");
650{ 658 }
651 if (options == 0)
652 options = ALL;
653
654 if (statopts==NULL)
655 statopts = strdup("");
656
657 if (prog==NULL)
658 prog = strdup("");
659 659
660 if (args==NULL) 660 if (config_wrapper.config.args == NULL) {
661 args = strdup(""); 661 config_wrapper.config.args = strdup("");
662 }
662 663
663 if (fmt==NULL) 664 if (config_wrapper.config.fmt == NULL) {
664 fmt = strdup(""); 665 config_wrapper.config.fmt = strdup("");
666 }
665 667
666 if (fails==NULL) 668 if (config_wrapper.config.fails == NULL) {
667 fails = strdup(""); 669 config_wrapper.config.fails = strdup("");
670 }
668 671
669 return options; 672 // return options;
673 return config_wrapper;
670} 674}
671 675
672
673/* convert the elapsed time to seconds */ 676/* convert the elapsed time to seconds */
674int 677int convert_to_seconds(char *etime, enum metric metric) {
675convert_to_seconds(char *etime) { 678 int hyphcnt = 0;
676 679 int coloncnt = 0;
677 char *ptr; 680 for (char *ptr = etime; *ptr != '\0'; ptr++) {
678 int total;
679
680 int hyphcnt;
681 int coloncnt;
682 int days;
683 int hours;
684 int minutes;
685 int seconds;
686
687 hyphcnt = 0;
688 coloncnt = 0;
689 days = 0;
690 hours = 0;
691 minutes = 0;
692 seconds = 0;
693
694 for (ptr = etime; *ptr != '\0'; ptr++) {
695 681
696 if (*ptr == '-') { 682 if (*ptr == '-') {
697 hyphcnt++; 683 hyphcnt++;
@@ -703,9 +689,12 @@ convert_to_seconds(char *etime) {
703 } 689 }
704 } 690 }
705 691
692 int days = 0;
693 int hours = 0;
694 int minutes = 0;
695 int seconds = 0;
706 if (hyphcnt > 0) { 696 if (hyphcnt > 0) {
707 sscanf(etime, "%d-%d:%d:%d", 697 sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds);
708 &days, &hours, &minutes, &seconds);
709 /* linux 2.6.5/2.6.6 reporting some processes with infinite 698 /* linux 2.6.5/2.6.6 reporting some processes with infinite
710 * elapsed times for some reason */ 699 * elapsed times for some reason */
711 if (days == 49710) { 700 if (days == 49710) {
@@ -713,135 +702,125 @@ convert_to_seconds(char *etime) {
713 } 702 }
714 } else { 703 } else {
715 if (coloncnt == 2) { 704 if (coloncnt == 2) {
716 sscanf(etime, "%d:%d:%d", 705 sscanf(etime, "%d:%d:%d", &hours, &minutes, &seconds);
717 &hours, &minutes, &seconds);
718 } else if (coloncnt == 1) { 706 } else if (coloncnt == 1) {
719 sscanf(etime, "%d:%d", 707 sscanf(etime, "%d:%d", &minutes, &seconds);
720 &minutes, &seconds);
721 } 708 }
722 } 709 }
723 710
724 total = (days * 86400) + 711 int total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds;
725 (hours * 3600) +
726 (minutes * 60) +
727 seconds;
728 712
729 if (verbose >= 3 && metric == METRIC_ELAPSED) { 713 if (verbose >= 3 && metric == METRIC_ELAPSED) {
730 printf("seconds: %d\n", total); 714 printf("seconds: %d\n", total);
731 } 715 }
732 return total; 716 return total;
733} 717}
734 718
719void print_help(void) {
720 print_revision(progname, NP_VERSION);
735 721
736void 722 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
737print_help (void) 723 printf(COPYRIGHT, copyright, email);
738{
739 print_revision (progname, NP_VERSION);
740
741 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
742 printf (COPYRIGHT, copyright, email);
743 724
744 printf ("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified")); 725 printf("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified"));
745 printf ("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); 726 printf("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number"));
746 printf ("%s\n", _("of processes. Search filters can be applied to limit the processes to check.")); 727 printf("%s\n", _("of processes. Search filters can be applied to limit the processes to check."));
747 728
748 printf ("\n\n"); 729 printf("\n\n");
749 730
750 printf ("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); 731 printf("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)"));
751 printf ("%s\n", _("are excluded from any checks to prevent false positives.")); 732 printf("%s\n", _("are excluded from any checks to prevent false positives."));
752 733
753 printf ("\n\n"); 734 printf("\n\n");
754 735
755 print_usage (); 736 print_usage();
756 737
757 printf (UT_HELP_VRSN); 738 printf(UT_HELP_VRSN);
758 printf (UT_EXTRA_OPTS); 739 printf(UT_EXTRA_OPTS);
759 printf (" %s\n", "-w, --warning=RANGE"); 740 printf(" %s\n", "-w, --warning=RANGE");
760 printf (" %s\n", _("Generate warning state if metric is outside this range")); 741 printf(" %s\n", _("Generate warning state if metric is outside this range"));
761 printf (" %s\n", "-c, --critical=RANGE"); 742 printf(" %s\n", "-c, --critical=RANGE");
762 printf (" %s\n", _("Generate critical state if metric is outside this range")); 743 printf(" %s\n", _("Generate critical state if metric is outside this range"));
763 printf (" %s\n", "-m, --metric=TYPE"); 744 printf(" %s\n", "-m, --metric=TYPE");
764 printf (" %s\n", _("Check thresholds against metric. Valid types:")); 745 printf(" %s\n", _("Check thresholds against metric. Valid types:"));
765 printf (" %s\n", _("PROCS - number of processes (default)")); 746 printf(" %s\n", _("PROCS - number of processes (default)"));
766 printf (" %s\n", _("VSZ - virtual memory size")); 747 printf(" %s\n", _("VSZ - virtual memory size"));
767 printf (" %s\n", _("RSS - resident set memory size")); 748 printf(" %s\n", _("RSS - resident set memory size"));
768 printf (" %s\n", _("CPU - percentage CPU")); 749 printf(" %s\n", _("CPU - percentage CPU"));
769/* only linux etime is support currently */ 750/* only linux etime is support currently */
770#if defined( __linux__ ) 751#if defined(__linux__)
771 printf (" %s\n", _("ELAPSED - time elapsed in seconds")); 752 printf(" %s\n", _("ELAPSED - time elapsed in seconds"));
772#endif /* defined(__linux__) */ 753#endif /* defined(__linux__) */
773 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 754 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
774 755
775 printf (" %s\n", "-v, --verbose"); 756 printf(" %s\n", "-v, --verbose");
776 printf (" %s\n", _("Extra information. Up to 3 verbosity levels")); 757 printf(" %s\n", _("Extra information. Up to 3 verbosity levels"));
777 758
778 printf (" %s\n", "-T, --traditional"); 759 printf(" %s\n", "-T, --traditional");
779 printf (" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); 760 printf(" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe"));
780 761
781 printf ("\n"); 762 printf("\n");
782 printf ("%s\n", "Filters:"); 763 printf("%s\n", "Filters:");
783 printf (" %s\n", "-s, --state=STATUSFLAGS"); 764 printf(" %s\n", "-s, --state=STATUSFLAGS");
784 printf (" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); 765 printf(" %s\n", _("Only scan for processes that have, in the output of `ps`, one or"));
785 printf (" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); 766 printf(" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,"));
786 printf (" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); 767 printf(" %s\n", _("RSZDT, plus others based on the output of your 'ps' command)."));
787 printf (" %s\n", "-p, --ppid=PPID"); 768 printf(" %s\n", "-p, --ppid=PPID");
788 printf (" %s\n", _("Only scan for children of the parent process ID indicated.")); 769 printf(" %s\n", _("Only scan for children of the parent process ID indicated."));
789 printf (" %s\n", "-z, --vsz=VSZ"); 770 printf(" %s\n", "-z, --vsz=VSZ");
790 printf (" %s\n", _("Only scan for processes with VSZ higher than indicated.")); 771 printf(" %s\n", _("Only scan for processes with VSZ higher than indicated."));
791 printf (" %s\n", "-r, --rss=RSS"); 772 printf(" %s\n", "-r, --rss=RSS");
792 printf (" %s\n", _("Only scan for processes with RSS higher than indicated.")); 773 printf(" %s\n", _("Only scan for processes with RSS higher than indicated."));
793 printf (" %s\n", "-P, --pcpu=PCPU"); 774 printf(" %s\n", "-P, --pcpu=PCPU");
794 printf (" %s\n", _("Only scan for processes with PCPU higher than indicated.")); 775 printf(" %s\n", _("Only scan for processes with PCPU higher than indicated."));
795 printf (" %s\n", "-u, --user=USER"); 776 printf(" %s\n", "-u, --user=USER");
796 printf (" %s\n", _("Only scan for processes with user name or ID indicated.")); 777 printf(" %s\n", _("Only scan for processes with user name or ID indicated."));
797 printf (" %s\n", "-a, --argument-array=STRING"); 778 printf(" %s\n", "-a, --argument-array=STRING");
798 printf (" %s\n", _("Only scan for processes with args that contain STRING.")); 779 printf(" %s\n", _("Only scan for processes with args that contain STRING."));
799 printf (" %s\n", "--ereg-argument-array=STRING"); 780 printf(" %s\n", "--ereg-argument-array=STRING");
800 printf (" %s\n", _("Only scan for processes with args that contain the regex STRING.")); 781 printf(" %s\n", _("Only scan for processes with args that contain the regex STRING."));
801 printf (" %s\n", "-C, --command=COMMAND"); 782 printf(" %s\n", "-C, --command=COMMAND");
802 printf (" %s\n", _("Only scan for exact matches of COMMAND (without path).")); 783 printf(" %s\n", _("Only scan for exact matches of COMMAND (without path)."));
803 printf (" %s\n", "-X, --exclude-process"); 784 printf(" %s\n", "-X, --exclude-process");
804 printf (" %s\n", _("Exclude processes which match this comma separated list")); 785 printf(" %s\n", _("Exclude processes which match this comma separated list"));
805 printf (" %s\n", "-k, --no-kthreads"); 786 printf(" %s\n", "-k, --no-kthreads");
806 printf (" %s\n", _("Only scan for non kernel threads (works on Linux only).")); 787 printf(" %s\n", _("Only scan for non kernel threads (works on Linux only)."));
807 788
808 printf(_("\n\ 789 printf(_("\n\
809RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\ 790RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
810specified 'max:min', a warning status will be generated if the\n\ 791specified 'max:min', a warning status will be generated if the\n\
811count is inside the specified range\n\n")); 792count is inside the specified range\n\n"));
812 793
813 printf(_("\ 794 printf(_("\
814This plugin checks the number of currently running processes and\n\ 795This plugin checks the number of currently running processes and\n\
815generates WARNING or CRITICAL states if the process count is outside\n\ 796generates WARNING or CRITICAL states if the process count is outside\n\
816the specified threshold ranges. The process count can be filtered by\n\ 797the specified threshold ranges. The process count can be filtered by\n\
817process owner, parent process PID, current state (e.g., 'Z'), or may\n\ 798process owner, parent process PID, current state (e.g., 'Z'), or may\n\
818be the total number of running processes\n\n")); 799be the total number of running processes\n\n"));
819 800
820 printf ("%s\n", _("Examples:")); 801 printf("%s\n", _("Examples:"));
821 printf (" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); 802 printf(" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry");
822 printf (" %s\n", _("Warning if not two processes with command name portsentry.")); 803 printf(" %s\n", _("Warning if not two processes with command name portsentry."));
823 printf (" %s\n\n", _("Critical if < 2 or > 1024 processes")); 804 printf(" %s\n\n", _("Critical if < 2 or > 1024 processes"));
824 printf (" %s\n", "check_procs -c 1: -C sshd"); 805 printf(" %s\n", "check_procs -c 1: -C sshd");
825 printf (" %s\n", _("Critical if not at least 1 process with command sshd")); 806 printf(" %s\n", _("Critical if not at least 1 process with command sshd"));
826 printf (" %s\n", "check_procs -w 1024 -c 1: -C sshd"); 807 printf(" %s\n", "check_procs -w 1024 -c 1: -C sshd");
827 printf (" %s\n", _("Warning if > 1024 processes with command name sshd.")); 808 printf(" %s\n", _("Warning if > 1024 processes with command name sshd."));
828 printf (" %s\n\n", _("Critical if < 1 processes with command name sshd.")); 809 printf(" %s\n\n", _("Critical if < 1 processes with command name sshd."));
829 printf (" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); 810 printf(" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root");
830 printf (" %s\n", _("Warning alert if > 10 processes with command arguments containing")); 811 printf(" %s\n", _("Warning alert if > 10 processes with command arguments containing"));
831 printf (" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); 812 printf(" %s\n\n", _("'/usr/local/bin/perl' and owned by root"));
832 printf (" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); 813 printf(" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ");
833 printf (" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); 814 printf(" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K"));
834 printf (" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); 815 printf(" %s\n", "check_procs -w 10 -c 20 --metric=CPU");
835 printf (" %s\n", _("Alert if CPU of any processes over 10%% or 20%%")); 816 printf(" %s\n", _("Alert if CPU of any processes over 10%% or 20%%"));
836 817
837 printf (UT_SUPPORT); 818 printf(UT_SUPPORT);
838} 819}
839 820
840void 821void print_usage(void) {
841print_usage (void) 822 printf("%s\n", _("Usage:"));
842{ 823 printf("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname);
843 printf ("%s\n", _("Usage:")); 824 printf(" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
844 printf ("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname); 825 printf(" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
845 printf (" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
846 printf (" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
847} 826}
diff --git a/plugins/check_procs.d/config.h b/plugins/check_procs.d/config.h
new file mode 100644
index 00000000..e32ca066
--- /dev/null
+++ b/plugins/check_procs.d/config.h
@@ -0,0 +1,75 @@
1#pragma once
2
3#include "../../config.h"
4#include "regex.h"
5#include "thresholds.h"
6#include <stddef.h>
7#include <string.h>
8#include <sys/types.h>
9
10enum metric {
11 METRIC_PROCS,
12 METRIC_VSZ,
13 METRIC_RSS,
14 METRIC_CPU,
15 METRIC_ELAPSED
16};
17
18typedef struct {
19 int options; /* bitmask of filter criteria to test against */
20 enum metric metric;
21 char *metric_name;
22 char *input_filename;
23 char *prog;
24 char *args;
25 char *fmt;
26 char *fails;
27 char *exclude_progs;
28 char **exclude_progs_arr;
29 char exclude_progs_counter;
30 regex_t re_args;
31
32 bool kthread_filter;
33 bool usepid; /* whether to test for pid or /proc/pid/exe */
34 uid_t uid;
35 pid_t ppid;
36 int vsz;
37 int rss;
38 float pcpu;
39 char *statopts;
40
41 char *warning_range;
42 char *critical_range;
43 thresholds *procs_thresholds;
44} check_procs_config;
45
46check_procs_config check_procs_config_init() {
47 check_procs_config tmp = {
48 .options = 0,
49 .metric = METRIC_PROCS,
50 .metric_name = strdup("PROCS"),
51 .input_filename = NULL,
52 .prog = NULL,
53 .args = NULL,
54 .fmt = NULL,
55 .fails = NULL,
56 .exclude_progs = NULL,
57 .exclude_progs_arr = NULL,
58 .exclude_progs_counter = 0,
59 .re_args = {0},
60
61 .kthread_filter = false,
62 .usepid = false,
63 .uid = 0,
64 .ppid = 0,
65 .vsz = 0,
66 .rss = 0,
67 .pcpu = 0,
68 .statopts = NULL,
69
70 .warning_range = NULL,
71 .critical_range = NULL,
72 .procs_thresholds = NULL,
73 };
74 return tmp;
75}
diff --git a/plugins/check_radius.c b/plugins/check_radius.c
index d9ff8fa7..cc846709 100644
--- a/plugins/check_radius.c
+++ b/plugins/check_radius.c
@@ -1,32 +1,32 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_radius plugin 3 * Monitoring check_radius plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2024 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_radius plugin 10 * This file contains the check_radius plugin
11* 11 *
12* Tests to see if a radius server is accepting connections. 12 * Tests to see if a radius server is accepting connections.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_radius"; 31const char *progname = "check_radius";
32const char *copyright = "2000-2024"; 32const char *copyright = "2000-2024";
@@ -35,64 +35,57 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 35#include "common.h"
36#include "utils.h" 36#include "utils.h"
37#include "netutils.h" 37#include "netutils.h"
38#include "states.h"
39#include "check_radius.d/config.h"
38 40
39#if defined(HAVE_LIBRADCLI) 41#if defined(HAVE_LIBRADCLI)
40#include <radcli/radcli.h> 42# include <radcli/radcli.h>
41#elif defined(HAVE_LIBFREERADIUS_CLIENT) 43#elif defined(HAVE_LIBFREERADIUS_CLIENT)
42#include <freeradius-client.h> 44# include <freeradius-client.h>
43#elif defined(HAVE_LIBRADIUSCLIENT_NG) 45#elif defined(HAVE_LIBRADIUSCLIENT_NG)
44#include <radiusclient-ng.h> 46# include <radiusclient-ng.h>
45#else 47#else
46#include <radiusclient.h> 48# include <radiusclient.h>
47#endif 49#endif
48 50
49static int process_arguments (int /*argc*/, char ** /*argv*/); 51typedef struct {
50static void print_help (void); 52 int errorcode;
51void print_usage (void); 53 check_radius_config config;
54} check_radius_config_wrapper;
55static check_radius_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
56static void print_help(void);
57void print_usage(void);
52 58
53#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI) 59#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
54#define my_rc_conf_str(a) rc_conf_str(rch,a) 60# define my_rc_conf_str(a) rc_conf_str(rch, a)
55#if defined(HAVE_LIBRADCLI) 61# if defined(HAVE_LIBRADCLI)
56#define my_rc_send_server(a,b) rc_send_server(rch,a,b,AUTH) 62# define my_rc_send_server(a, b) rc_send_server(rch, a, b, AUTH)
57#else 63# else
58#define my_rc_send_server(a,b) rc_send_server(rch,a,b) 64# define my_rc_send_server(a, b) rc_send_server(rch, a, b)
59#endif 65# endif
60#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI) 66# if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI)
61#define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(rch,a,b,c,d,(a)->secret,e,f) 67# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, (a)->secret, e, f)
62#else 68# else
63#define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(rch,a,b,c,d,e,f) 69# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, e, f)
64#endif 70# endif
65#define my_rc_avpair_add(a,b,c,d) rc_avpair_add(rch,a,b,c,-1,d) 71# define my_rc_avpair_add(a, b, c, d) rc_avpair_add(rch, a, b, c, -1, d)
66#define my_rc_read_dictionary(a) rc_read_dictionary(rch, a) 72# define my_rc_read_dictionary(a) rc_read_dictionary(rch, a)
67#else 73#else
68#define my_rc_conf_str(a) rc_conf_str(a) 74# define my_rc_conf_str(a) rc_conf_str(a)
69#define my_rc_send_server(a,b) rc_send_server(a, b) 75# define my_rc_send_server(a, b) rc_send_server(a, b)
70#define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(a,b,c,d,e,f) 76# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(a, b, c, d, e, f)
71#define my_rc_avpair_add(a,b,c,d) rc_avpair_add(a, b, c, d) 77# define my_rc_avpair_add(a, b, c, d) rc_avpair_add(a, b, c, d)
72#define my_rc_read_dictionary(a) rc_read_dictionary(a) 78# define my_rc_read_dictionary(a) rc_read_dictionary(a)
73#endif 79#endif
74 80
75/* REJECT_RC is only defined in some version of radiusclient. It has 81/* REJECT_RC is only defined in some version of radiusclient. It has
76 * been reported from radiusclient-ng 0.5.6 on FreeBSD 7.2-RELEASE */ 82 * been reported from radiusclient-ng 0.5.6 on FreeBSD 7.2-RELEASE */
77#ifndef REJECT_RC 83#ifndef REJECT_RC
78#define REJECT_RC BADRESP_RC 84# define REJECT_RC BADRESP_RC
79#endif 85#endif
80 86
81static int my_rc_read_config(char * /*a*/); 87static int my_rc_read_config(char * /*a*/, rc_handle ** /*rch*/);
82
83#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
84static rc_handle *rch = NULL;
85#endif
86 88
87static char *server = NULL;
88static char *username = NULL;
89static char *password = NULL;
90static char *nasid = NULL;
91static char *nasipaddress = NULL;
92static char *expect = NULL;
93static char *config_file = NULL;
94static unsigned short port = PW_AUTH_UDP_PORT;
95static int retries = 1;
96static bool verbose = false; 89static bool verbose = false;
97 90
98/****************************************************************************** 91/******************************************************************************
@@ -148,149 +141,167 @@ Please note that all tags must be lowercase to use the DocBook XML DTD.
148-@@ 141-@@
149******************************************************************************/ 142******************************************************************************/
150 143
144int main(int argc, char **argv) {
145 setlocale(LC_ALL, "");
146 bindtextdomain(PACKAGE, LOCALEDIR);
147 textdomain(PACKAGE);
151 148
149 /* Parse extra opts if any */
150 argv = np_extra_opts(&argc, argv, progname);
151
152 check_radius_config_wrapper tmp_config = process_arguments(argc, argv);
153
154 if (tmp_config.errorcode == ERROR) {
155 usage4(_("Could not parse arguments"));
156 }
157
158 check_radius_config config = tmp_config.config;
159
160#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
161 rc_handle *rch = NULL;
162#endif
163
164 char *str = strdup("dictionary");
165 if ((config.config_file && my_rc_read_config(config.config_file, &rch)) || my_rc_read_dictionary(my_rc_conf_str(str))) {
166 die(STATE_UNKNOWN, _("Config file error\n"));
167 }
168
169 uint32_t service = PW_AUTHENTICATE_ONLY;
170
171 SEND_DATA data;
172 memset(&data, 0, sizeof(data));
173 if (!(my_rc_avpair_add(&data.send_pairs, PW_SERVICE_TYPE, &service, 0) &&
174 my_rc_avpair_add(&data.send_pairs, PW_USER_NAME, config.username, 0) &&
175 my_rc_avpair_add(&data.send_pairs, PW_USER_PASSWORD, config.password, 0))) {
176 die(STATE_UNKNOWN, _("Out of Memory?\n"));
177 }
178
179 if (config.nas_id != NULL) {
180 if (!(my_rc_avpair_add(&data.send_pairs, PW_NAS_IDENTIFIER, config.nas_id, 0))) {
181 die(STATE_UNKNOWN, _("Invalid NAS-Identifier\n"));
182 }
183 }
152 184
153int
154main (int argc, char **argv)
155{
156 struct sockaddr_storage ss;
157 char name[HOST_NAME_MAX]; 185 char name[HOST_NAME_MAX];
186 if (config.nas_ip_address == NULL) {
187 if (gethostname(name, sizeof(name)) != 0) {
188 die(STATE_UNKNOWN, _("gethostname() failed!\n"));
189 }
190 config.nas_ip_address = name;
191 }
192
193 struct sockaddr_storage radius_server_socket;
194 if (!dns_lookup(config.nas_ip_address, &radius_server_socket, AF_UNSPEC)) {
195 die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
196 }
197
198 uint32_t client_id = ntohl(((struct sockaddr_in *)&radius_server_socket)->sin_addr.s_addr);
199 if (my_rc_avpair_add(&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL) {
200 die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
201 }
202
203 my_rc_buildreq(&data, PW_ACCESS_REQUEST, config.server, config.port, (int)timeout_interval, config.retries);
204
158#ifdef RC_BUFFER_LEN 205#ifdef RC_BUFFER_LEN
159 char msg[RC_BUFFER_LEN]; 206 char msg[RC_BUFFER_LEN];
160#else 207#else
161 char msg[BUFFER_LEN]; 208 char msg[BUFFER_LEN];
162#endif 209#endif
163 SEND_DATA data;
164 int result = STATE_UNKNOWN;
165 uint32_t client_id, service;
166 char *str;
167
168 setlocale (LC_ALL, "");
169 bindtextdomain (PACKAGE, LOCALEDIR);
170 textdomain (PACKAGE);
171 210
172 /* Parse extra opts if any */ 211 int result = my_rc_send_server(&data, msg);
173 argv=np_extra_opts (&argc, argv, progname); 212 rc_avpair_free(data.send_pairs);
213 if (data.receive_pairs) {
214 rc_avpair_free(data.receive_pairs);
215 }
174 216
175 if (process_arguments (argc, argv) == ERROR) 217 if (result == TIMEOUT_RC) {
176 usage4 (_("Could not parse arguments")); 218 printf("Timeout\n");
219 exit(STATE_CRITICAL);
220 }
177 221
178 str = strdup ("dictionary"); 222 if (result == ERROR_RC) {
179 if ((config_file && my_rc_read_config (config_file)) || 223 printf(_("Auth Error\n"));
180 my_rc_read_dictionary (my_rc_conf_str (str))) 224 exit(STATE_CRITICAL);
181 die (STATE_UNKNOWN, _("Config file error\n")); 225 }
182 226
183 service = PW_AUTHENTICATE_ONLY; 227 if (result == REJECT_RC) {
228 printf(_("Auth Failed\n"));
229 exit(STATE_WARNING);
230 }
184 231
185 memset (&data, 0, sizeof(data)); 232 if (result == BADRESP_RC) {
186 if (!(my_rc_avpair_add (&data.send_pairs, PW_SERVICE_TYPE, &service, 0) && 233 printf(_("Bad Response\n"));
187 my_rc_avpair_add (&data.send_pairs, PW_USER_NAME, username, 0) && 234 exit(STATE_WARNING);
188 my_rc_avpair_add (&data.send_pairs, PW_USER_PASSWORD, password, 0) 235 }
189 ))
190 die (STATE_UNKNOWN, _("Out of Memory?\n"));
191 236
192 if (nasid != NULL) { 237 if (config.expect && !strstr(msg, config.expect)) {
193 if (!(my_rc_avpair_add (&data.send_pairs, PW_NAS_IDENTIFIER, nasid, 0))) 238 printf("%s\n", msg);
194 die (STATE_UNKNOWN, _("Invalid NAS-Identifier\n")); 239 exit(STATE_WARNING);
195 } 240 }
196 241
197 if (nasipaddress == NULL) { 242 if (result == OK_RC) {
198 if (gethostname (name, sizeof(name)) != 0) 243 printf(_("Auth OK\n"));
199 die (STATE_UNKNOWN, _("gethostname() failed!\n")); 244 exit(STATE_OK);
200 nasipaddress = name;
201 } 245 }
202 if (!dns_lookup (nasipaddress, &ss, AF_INET)) /* TODO: Support IPv6. */ 246
203 die (STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
204 client_id = ntohl (((struct sockaddr_in *)&ss)->sin_addr.s_addr);
205 if (my_rc_avpair_add (&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL)
206 die (STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
207
208 my_rc_buildreq (&data, PW_ACCESS_REQUEST, server, port, (int)timeout_interval,
209 retries);
210
211 result = my_rc_send_server (&data, msg);
212 rc_avpair_free (data.send_pairs);
213 if (data.receive_pairs)
214 rc_avpair_free (data.receive_pairs);
215
216 if (result == TIMEOUT_RC)
217 die (STATE_CRITICAL, _("Timeout\n"));
218 if (result == ERROR_RC)
219 die (STATE_CRITICAL, _("Auth Error\n"));
220 if (result == REJECT_RC)
221 die (STATE_WARNING, _("Auth Failed\n"));
222 if (result == BADRESP_RC)
223 die (STATE_WARNING, _("Bad Response\n"));
224 if (expect && !strstr (msg, expect))
225 die (STATE_WARNING, "%s\n", msg);
226 if (result == OK_RC)
227 die (STATE_OK, _("Auth OK\n"));
228 (void)snprintf(msg, sizeof(msg), _("Unexpected result code %d"), result); 247 (void)snprintf(msg, sizeof(msg), _("Unexpected result code %d"), result);
229 die (STATE_UNKNOWN, "%s\n", msg); 248 printf("%s\n", msg);
249 exit(STATE_UNKNOWN);
230} 250}
231 251
232
233
234/* process command-line arguments */ 252/* process command-line arguments */
235int 253check_radius_config_wrapper process_arguments(int argc, char **argv) {
236process_arguments (int argc, char **argv) 254 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'P'},
237{ 255 {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'},
238 int c; 256 {"nas-id", required_argument, 0, 'n'}, {"nas-ip-address", required_argument, 0, 'N'},
239 257 {"filename", required_argument, 0, 'F'}, {"expect", required_argument, 0, 'e'},
240 int option = 0; 258 {"retries", required_argument, 0, 'r'}, {"timeout", required_argument, 0, 't'},
241 static struct option longopts[] = { 259 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
242 {"hostname", required_argument, 0, 'H'}, 260 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
243 {"port", required_argument, 0, 'P'}, 261
244 {"username", required_argument, 0, 'u'}, 262 check_radius_config_wrapper result = {
245 {"password", required_argument, 0, 'p'}, 263 .errorcode = OK,
246 {"nas-id", required_argument, 0, 'n'}, 264 .config = check_radius_config_init(),
247 {"nas-ip-address", required_argument, 0, 'N'},
248 {"filename", required_argument, 0, 'F'},
249 {"expect", required_argument, 0, 'e'},
250 {"retries", required_argument, 0, 'r'},
251 {"timeout", required_argument, 0, 't'},
252 {"verbose", no_argument, 0, 'v'},
253 {"version", no_argument, 0, 'V'},
254 {"help", no_argument, 0, 'h'},
255 {0, 0, 0, 0}
256 }; 265 };
257 266
258 while (1) { 267 while (true) {
259 c = getopt_long (argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, 268 int option = 0;
260 &option); 269 int option_index = getopt_long(argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, &option);
261 270
262 if (c == -1 || c == EOF || c == 1) 271 if (option_index == -1 || option_index == EOF || option_index == 1) {
263 break; 272 break;
273 }
264 274
265 switch (c) { 275 switch (option_index) {
266 case '?': /* print short usage statement if args not parsable */ 276 case '?': /* print short usage statement if args not parsable */
267 usage5 (); 277 usage5();
268 case 'h': /* help */ 278 case 'h': /* help */
269 print_help (); 279 print_help();
270 exit (STATE_UNKNOWN); 280 exit(STATE_UNKNOWN);
271 case 'V': /* version */ 281 case 'V': /* version */
272 print_revision (progname, NP_VERSION); 282 print_revision(progname, NP_VERSION);
273 exit (STATE_UNKNOWN); 283 exit(STATE_UNKNOWN);
274 case 'v': /* verbose mode */ 284 case 'v': /* verbose mode */
275 verbose = true; 285 verbose = true;
276 break; 286 break;
277 case 'H': /* hostname */ 287 case 'H': /* hostname */
278 if (!is_host (optarg)) { 288 if (!is_host(optarg)) {
279 usage2 (_("Invalid hostname/address"), optarg); 289 usage2(_("Invalid hostname/address"), optarg);
280 } 290 }
281 server = optarg; 291 result.config.server = optarg;
282 break; 292 break;
283 case 'P': /* port */ 293 case 'P': /* port */
284 if (is_intnonneg (optarg)) 294 if (is_intnonneg(optarg)) {
285 port = (unsigned short)atoi (optarg); 295 result.config.port = (unsigned short)atoi(optarg);
286 else 296 } else {
287 usage4 (_("Port must be a positive integer")); 297 usage4(_("Port must be a positive integer"));
298 }
288 break; 299 break;
289 case 'u': /* username */ 300 case 'u': /* username */
290 username = optarg; 301 result.config.username = optarg;
291 break; 302 break;
292 case 'p': /* password */ 303 case 'p': /* password */
293 password = strdup(optarg); 304 result.config.password = strdup(optarg);
294 305
295 /* Delete the password from process list */ 306 /* Delete the password from process list */
296 while (*optarg != '\0') { 307 while (*optarg != '\0') {
@@ -298,119 +309,115 @@ process_arguments (int argc, char **argv)
298 optarg++; 309 optarg++;
299 } 310 }
300 break; 311 break;
301 case 'n': /* nas id */ 312 case 'n': /* nas id */
302 nasid = optarg; 313 result.config.nas_id = optarg;
303 break; 314 break;
304 case 'N': /* nas ip address */ 315 case 'N': /* nas ip address */
305 nasipaddress = optarg; 316 result.config.nas_ip_address = optarg;
306 break; 317 break;
307 case 'F': /* configuration file */ 318 case 'F': /* configuration file */
308 config_file = optarg; 319 result.config.config_file = optarg;
309 break; 320 break;
310 case 'e': /* expect */ 321 case 'e': /* expect */
311 expect = optarg; 322 result.config.expect = optarg;
312 break; 323 break;
313 case 'r': /* retries */ 324 case 'r': /* retries */
314 if (is_intpos (optarg)) 325 if (is_intpos(optarg)) {
315 retries = atoi (optarg); 326 result.config.retries = atoi(optarg);
316 else 327 } else {
317 usage4 (_("Number of retries must be a positive integer")); 328 usage4(_("Number of retries must be a positive integer"));
329 }
318 break; 330 break;
319 case 't': /* timeout */ 331 case 't': /* timeout */
320 if (is_intpos (optarg)) 332 if (is_intpos(optarg)) {
321 timeout_interval = (unsigned)atoi (optarg); 333 timeout_interval = (unsigned)atoi(optarg);
322 else 334 } else {
323 usage2 (_("Timeout interval must be a positive integer"), optarg); 335 usage2(_("Timeout interval must be a positive integer"), optarg);
336 }
324 break; 337 break;
325 } 338 }
326 } 339 }
327 340
328 if (server == NULL) 341 if (result.config.server == NULL) {
329 usage4 (_("Hostname was not supplied")); 342 usage4(_("Hostname was not supplied"));
330 if (username == NULL) 343 }
331 usage4 (_("User not specified")); 344 if (result.config.username == NULL) {
332 if (password == NULL) 345 usage4(_("User not specified"));
333 usage4 (_("Password not specified")); 346 }
334 if (config_file == NULL) 347 if (result.config.password == NULL) {
335 usage4 (_("Configuration file not specified")); 348 usage4(_("Password not specified"));
349 }
350 if (result.config.config_file == NULL) {
351 usage4(_("Configuration file not specified"));
352 }
336 353
337 return OK; 354 return result;
338} 355}
339 356
340 357void print_help(void) {
341
342void
343print_help (void)
344{
345 char *myport; 358 char *myport;
346 xasprintf (&myport, "%d", PW_AUTH_UDP_PORT); 359 xasprintf(&myport, "%d", PW_AUTH_UDP_PORT);
347 360
348 print_revision (progname, NP_VERSION); 361 print_revision(progname, NP_VERSION);
349 362
350 printf ("Copyright (c) 1999 Robert August Vincent II\n"); 363 printf("Copyright (c) 1999 Robert August Vincent II\n");
351 printf (COPYRIGHT, copyright, email); 364 printf(COPYRIGHT, copyright, email);
352 365
353 printf("%s\n", _("Tests to see if a RADIUS server is accepting connections.")); 366 printf("%s\n", _("Tests to see if a RADIUS server is accepting connections."));
354 367
355 printf ("\n\n"); 368 printf("\n\n");
356 369
357 print_usage (); 370 print_usage();
358 371
359 printf (UT_HELP_VRSN); 372 printf(UT_HELP_VRSN);
360 printf (UT_EXTRA_OPTS); 373 printf(UT_EXTRA_OPTS);
361 374
362 printf (UT_HOST_PORT, 'P', myport); 375 printf(UT_HOST_PORT, 'P', myport);
363 376
364 printf (" %s\n", "-u, --username=STRING"); 377 printf(" %s\n", "-u, --username=STRING");
365 printf (" %s\n", _("The user to authenticate")); 378 printf(" %s\n", _("The user to authenticate"));
366 printf (" %s\n", "-p, --password=STRING"); 379 printf(" %s\n", "-p, --password=STRING");
367 printf (" %s\n", _("Password for authentication (SECURITY RISK)")); 380 printf(" %s\n", _("Password for authentication (SECURITY RISK)"));
368 printf (" %s\n", "-n, --nas-id=STRING"); 381 printf(" %s\n", "-n, --nas-id=STRING");
369 printf (" %s\n", _("NAS identifier")); 382 printf(" %s\n", _("NAS identifier"));
370 printf (" %s\n", "-N, --nas-ip-address=STRING"); 383 printf(" %s\n", "-N, --nas-ip-address=STRING");
371 printf (" %s\n", _("NAS IP Address")); 384 printf(" %s\n", _("NAS IP Address"));
372 printf (" %s\n", "-F, --filename=STRING"); 385 printf(" %s\n", "-F, --filename=STRING");
373 printf (" %s\n", _("Configuration file")); 386 printf(" %s\n", _("Configuration file"));
374 printf (" %s\n", "-e, --expect=STRING"); 387 printf(" %s\n", "-e, --expect=STRING");
375 printf (" %s\n", _("Response string to expect from the server")); 388 printf(" %s\n", _("Response string to expect from the server"));
376 printf (" %s\n", "-r, --retries=INTEGER"); 389 printf(" %s\n", "-r, --retries=INTEGER");
377 printf (" %s\n", _("Number of times to retry a failed connection")); 390 printf(" %s\n", _("Number of times to retry a failed connection"));
378 391
379 printf (UT_CONN_TIMEOUT, timeout_interval); 392 printf(UT_CONN_TIMEOUT, timeout_interval);
380 393
381 printf ("\n"); 394 printf("\n");
382 printf ("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections.")); 395 printf("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections."));
383 printf ("%s\n", _("The server to test must be specified in the invocation, as well as a user")); 396 printf("%s\n", _("The server to test must be specified in the invocation, as well as a user"));
384 printf ("%s\n", _("name and password. A configuration file must be present. The format of")); 397 printf("%s\n", _("name and password. A configuration file must be present. The format of"));
385 printf ("%s\n", _("the configuration file is described in the radiusclient library sources.")); 398 printf("%s\n", _("the configuration file is described in the radiusclient library sources."));
386 printf ("%s\n", _("The password option presents a substantial security issue because the")); 399 printf("%s\n", _("The password option presents a substantial security issue because the"));
387 printf ("%s\n", _("password can possibly be determined by careful watching of the command line")); 400 printf("%s\n", _("password can possibly be determined by careful watching of the command line"));
388 printf ("%s\n", _("in a process listing. This risk is exacerbated because the plugin will")); 401 printf("%s\n", _("in a process listing. This risk is exacerbated because the plugin will"));
389 printf ("%s\n", _("typically be executed at regular predictable intervals. Please be sure that")); 402 printf("%s\n", _("typically be executed at regular predictable intervals. Please be sure that"));
390 printf ("%s\n", _("the password used does not allow access to sensitive system resources.")); 403 printf("%s\n", _("the password used does not allow access to sensitive system resources."));
391 404
392 printf (UT_SUPPORT); 405 printf(UT_SUPPORT);
393} 406}
394 407
395 408void print_usage(void) {
396 409 printf("%s\n", _("Usage:"));
397void 410 printf("%s -H host -F config_file -u username -p password\n\
398print_usage (void)
399{
400 printf ("%s\n", _("Usage:"));
401 printf ("%s -H host -F config_file -u username -p password\n\
402 [-P port] [-t timeout] [-r retries] [-e expect]\n\ 411 [-P port] [-t timeout] [-r retries] [-e expect]\n\
403 [-n nas-id] [-N nas-ip-addr]\n", progname); 412 [-n nas-id] [-N nas-ip-addr]\n",
413 progname);
404} 414}
405 415
406 416int my_rc_read_config(char *config_file_name, rc_handle **rch) {
407
408int my_rc_read_config(char * a)
409{
410#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI) 417#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
411 rch = rc_read_config(a); 418 *rch = rc_read_config(config_file_name);
412 return (rch == NULL) ? 1 : 0; 419 return (rch == NULL) ? 1 : 0;
413#else 420#else
414 return rc_read_config(a); 421 return rc_read_config(config_file_name);
415#endif 422#endif
416} 423}
diff --git a/plugins/check_radius.d/config.h b/plugins/check_radius.d/config.h
new file mode 100644
index 00000000..b27d31e7
--- /dev/null
+++ b/plugins/check_radius.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#if defined(HAVE_LIBRADCLI)
6# include <radcli/radcli.h>
7#elif defined(HAVE_LIBFREERADIUS_CLIENT)
8# include <freeradius-client.h>
9#elif defined(HAVE_LIBRADIUSCLIENT_NG)
10# include <radiusclient-ng.h>
11#else
12# include <radiusclient.h>
13#endif
14
15typedef struct {
16 char *server;
17 char *username;
18 char *password;
19 char *config_file;
20 char *nas_id;
21 char *nas_ip_address;
22 int retries;
23 unsigned short port;
24
25 char *expect;
26} check_radius_config;
27
28check_radius_config check_radius_config_init() {
29 check_radius_config tmp = {
30 .server = NULL,
31 .username = NULL,
32 .password = NULL,
33 .config_file = NULL,
34 .nas_id = NULL,
35 .nas_ip_address = NULL,
36 .retries = 1,
37 .port = PW_AUTH_UDP_PORT,
38
39 .expect = NULL,
40 };
41 return tmp;
42}
diff --git a/plugins/check_swap.c b/plugins/check_swap.c
index cb95949a..435a104e 100644
--- a/plugins/check_swap.c
+++ b/plugins/check_swap.c
@@ -90,6 +90,14 @@ int main(int argc, char **argv) {
90 exit(STATE_UNKNOWN); 90 exit(STATE_UNKNOWN);
91 } 91 }
92 92
93 if (verbose) {
94 printf("Swap retrieval result:\n"
95 "\tFree: %llu\n"
96 "\tUsed: %llu\n"
97 "\tTotal: %llu\n",
98 data.metrics.free, data.metrics.used, data.metrics.total);
99 }
100
93 double percent_used; 101 double percent_used;
94 mp_check overall = mp_check_init(); 102 mp_check overall = mp_check_init();
95 if (config.output_format_is_set) { 103 if (config.output_format_is_set) {
diff --git a/plugins/check_swap.d/check_swap.h b/plugins/check_swap.d/check_swap.h
index 1000fc9e..da08d65a 100644
--- a/plugins/check_swap.d/check_swap.h
+++ b/plugins/check_swap.d/check_swap.h
@@ -1,7 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include "../common.h" 3#include "../common.h"
4#include "output.h" 4#include "../../lib/output.h"
5#include "../../lib/states.h"
5 6
6#ifndef SWAP_CONVERSION 7#ifndef SWAP_CONVERSION
7# define SWAP_CONVERSION 1 8# define SWAP_CONVERSION 1
@@ -26,7 +27,7 @@ typedef struct {
26 27
27typedef struct { 28typedef struct {
28 bool allswaps; 29 bool allswaps;
29 int no_swap_state; 30 mp_state_enum no_swap_state;
30 bool warn_is_set; 31 bool warn_is_set;
31 check_swap_threshold warn; 32 check_swap_threshold warn;
32 bool crit_is_set; 33 bool crit_is_set;
diff --git a/plugins/check_swap.d/swap.c b/plugins/check_swap.d/swap.c
index 180d5037..634f80d9 100644
--- a/plugins/check_swap.d/swap.c
+++ b/plugins/check_swap.d/swap.c
@@ -68,7 +68,7 @@ swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
68 FILE *meminfo_file_ptr; 68 FILE *meminfo_file_ptr;
69 meminfo_file_ptr = fopen(proc_meminfo, "r"); 69 meminfo_file_ptr = fopen(proc_meminfo, "r");
70 70
71 swap_result result = {0}; 71 swap_result result = {};
72 result.errorcode = STATE_UNKNOWN; 72 result.errorcode = STATE_UNKNOWN;
73 73
74 if (meminfo_file_ptr == NULL) { 74 if (meminfo_file_ptr == NULL) {
@@ -78,83 +78,71 @@ swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
78 return result; 78 return result;
79 } 79 }
80 80
81 uint64_t swap_total = 0; 81 unsigned long swap_total = 0;
82 uint64_t swap_used = 0; 82 unsigned long swap_used = 0;
83 uint64_t swap_free = 0; 83 unsigned long swap_free = 0;
84 84
85 bool found_total = false; 85 bool found_total = false;
86 bool found_used = false;
87 bool found_free = false; 86 bool found_free = false;
88 87
89 char input_buffer[MAX_INPUT_BUFFER]; 88 char input_buffer[MAX_INPUT_BUFFER];
90 char str[32]; 89 char str[32];
91 90
92 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) { 91 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) {
93 uint64_t tmp_KB = 0;
94 92
95 /* 93 /*
96 * The following sscanf call looks for a line looking like: "Swap: 123 94 * The following sscanf call looks for a line looking like: "Swap: 123
97 * 123 123" On which kind of system this format exists, I can not say, 95 * 123 123" which exists on NetBSD (at least),
98 * but I wanted to document this for people who are not adapt with 96 * The unit should be Bytes
99 * sscanf anymore, like me
100 * Also the units used here are unclear and probably wrong
101 */ 97 */
102 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, &swap_free) == 3) { 98 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, &swap_free) == 3) {
103
104 result.metrics.total += swap_total;
105 result.metrics.used += swap_used;
106 result.metrics.free += swap_free;
107
108 found_total = true; 99 found_total = true;
109 found_free = true; 100 found_free = true;
110 found_used = true;
111
112 // Set error 101 // Set error
113 result.errorcode = STATE_OK; 102 result.errorcode = STATE_OK;
103 // Break out of fgets here, since both scanf expressions might match (NetBSD for example)
104 break;
105 }
106
107 /*
108 * The following sscanf call looks for lines looking like:
109 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least
110 * on Debian Linux with a 5.* kernel
111 */
112 unsigned long tmp_KB = 0;
113 int sscanf_result = sscanf(input_buffer,
114 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu "
115 "%*[k]%*[B]",
116 str, &tmp_KB);
117
118 if (sscanf_result == 2) {
119
120 if (verbose >= 3) {
121 printf("Got %s with %lu\n", str, tmp_KB);
122 }
114 123
115 /* 124 /* I think this part is always in Kb, so convert to bytes */
116 * The following sscanf call looks for lines looking like: 125 if (strcmp("Total", str) == 0) {
117 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least 126 swap_total = tmp_KB * 1000;
118 * on Debian Linux with a 5.* kernel 127 found_total = true;
119 */ 128 } else if (strcmp("Free", str) == 0) {
120 } else { 129 swap_free += tmp_KB * 1000;
121 int sscanf_result = sscanf(input_buffer, 130 found_free = true;
122 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu " 131 } else if (strcmp("Cached", str) == 0) {
123 "%*[k]%*[B]", 132 swap_free += tmp_KB * 1000;
124 str, &tmp_KB);
125
126 if (sscanf_result == 2) {
127
128 if (verbose >= 3) {
129 printf("Got %s with %lu\n", str, tmp_KB);
130 }
131
132 /* I think this part is always in Kb, so convert to bytes */
133 if (strcmp("Total", str) == 0) {
134 swap_total = tmp_KB * 1000;
135 found_total = true;
136 } else if (strcmp("Free", str) == 0) {
137 swap_free = swap_free + tmp_KB * 1000;
138 found_free = true;
139 found_used = true; // No explicit used metric available
140 } else if (strcmp("Cached", str) == 0) {
141 swap_free = swap_free + tmp_KB * 1000;
142 found_free = true;
143 found_used = true; // No explicit used metric available
144 }
145
146 result.errorcode = STATE_OK;
147 } 133 }
134
135 result.errorcode = STATE_OK;
148 } 136 }
149 } 137 }
150 138
151 fclose(meminfo_file_ptr); 139 fclose(meminfo_file_ptr);
152 140
153 result.metrics.total = swap_total; 141 result.metrics.total = swap_total;
154 result.metrics.used = swap_total - swap_free;
155 result.metrics.free = swap_free; 142 result.metrics.free = swap_free;
143 result.metrics.used = swap_total - swap_free;
156 144
157 if (!found_free || !found_total || !found_used) { 145 if (!found_free || !found_total) {
158 result.errorcode = STATE_UNKNOWN; 146 result.errorcode = STATE_UNKNOWN;
159 } 147 }
160 148
@@ -297,8 +285,14 @@ struct swapent {
297}; 285};
298 286
299#else 287#else
288
289// Includes for NetBSD
290# include <unistd.h>
291# include <sys/swap.h>
292
300# define bsd_swapctl swapctl 293# define bsd_swapctl swapctl
301#endif 294
295#endif // CHECK_SWAP_SWAPCTL_BSD
302 296
303swap_result getSwapFromSwapctl_BSD(swap_config config) { 297swap_result getSwapFromSwapctl_BSD(swap_config config) {
304 /* get the number of active swap devices */ 298 /* get the number of active swap devices */
@@ -322,8 +316,8 @@ swap_result getSwapFromSwapctl_BSD(swap_config config) {
322 unsigned long long used_swap_mb = 0; 316 unsigned long long used_swap_mb = 0;
323 317
324 for (int i = 0; i < nswaps; i++) { 318 for (int i = 0; i < nswaps; i++) {
325 dsktotal_mb = (float)ent[i].se_nblks / (float)config.conversion_factor; 319 dsktotal_mb = (double)ent[i].se_nblks / (double)config.conversion_factor;
326 dskused_mb = (float)ent[i].se_inuse / (float)config.conversion_factor; 320 dskused_mb = (double)ent[i].se_inuse / (double)config.conversion_factor;
327 dskfree_mb = (dsktotal_mb - dskused_mb); 321 dskfree_mb = (dsktotal_mb - dskused_mb);
328 322
329 if (config.allswaps && dsktotal_mb > 0) { 323 if (config.allswaps && dsktotal_mb > 0) {
diff --git a/plugins/check_tcp.c b/plugins/check_tcp.c
index 49ad096c..22dcc74e 100644
--- a/plugins/check_tcp.c
+++ b/plugins/check_tcp.c
@@ -3,7 +3,7 @@
3 * Monitoring check_tcp plugin 3 * Monitoring check_tcp plugin
4 * 4 *
5 * License: GPL 5 * License: GPL
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2025 Monitoring Plugins Development Team
7 * 7 *
8 * Description: 8 * Description:
9 * 9 *
@@ -29,74 +29,63 @@
29 29
30/* progname "check_tcp" changes depending on symlink called */ 30/* progname "check_tcp" changes depending on symlink called */
31char *progname; 31char *progname;
32const char *copyright = "1999-2024"; 32const char *copyright = "1999-2025";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "./common.h"
36#include "netutils.h" 36#include "./netutils.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_tcp.h" 38#include "./check_tcp.d/config.h"
39#include "output.h"
40#include "states.h"
39 41
42#include <sys/types.h>
40#include <ctype.h> 43#include <ctype.h>
41#include <sys/select.h> 44#include <sys/select.h>
42 45
46ssize_t my_recv(int socket_descriptor, char *buf, size_t len, bool use_tls) {
43#ifdef HAVE_SSL 47#ifdef HAVE_SSL
44static bool check_cert = false; 48 if (use_tls) {
45static int days_till_exp_warn, days_till_exp_crit; 49 return np_net_ssl_read(buf, (int)len);
46# define my_recv(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) 50 }
47# define my_send(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
48#else
49# define my_recv(buf, len) read(sd, buf, len)
50# define my_send(buf, len) send(sd, buf, len, 0)
51#endif 51#endif
52 return read(socket_descriptor, buf, len);
53}
52 54
53/* int my_recv(char *, size_t); */ 55ssize_t my_send(int socket_descriptor, char *buf, size_t len, bool use_tls) {
54static int process_arguments(int /*argc*/, char ** /*argv*/); 56#ifdef HAVE_SSL
55static void print_help(void); 57 if (use_tls) {
58 return np_net_ssl_write(buf, (int)len);
59 }
60#endif
61 return write(socket_descriptor, buf, len);
62}
63
64typedef struct {
65 int errorcode;
66 check_tcp_config config;
67} check_tcp_config_wrapper;
68static check_tcp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/, check_tcp_config /*config*/);
69void print_help(const char *service);
56void print_usage(void); 70void print_usage(void);
57 71
58#define EXPECT server_expect[0] 72int verbosity = 0;
59static char *SERVICE = "TCP";
60static char *SEND = NULL;
61static char *QUIT = NULL;
62static int PROTOCOL = IPPROTO_TCP; /* most common is default */
63static int PORT = 0;
64static int READ_TIMEOUT = 2;
65
66static int server_port = 0;
67static char *server_address = NULL;
68static bool host_specified = false;
69static char *server_send = NULL;
70static char *server_quit = NULL;
71static char **server_expect;
72static size_t server_expect_count = 0;
73static ssize_t maxbytes = 0;
74static char **warn_codes = NULL;
75static size_t warn_codes_count = 0;
76static char **crit_codes = NULL;
77static size_t crit_codes_count = 0;
78static unsigned int delay = 0;
79static double warning_time = 0;
80static double critical_time = 0;
81static double elapsed_time = 0;
82static long microsec;
83static int sd = 0;
84#define MAXBUF 1024
85static char buffer[MAXBUF];
86static int expect_mismatch_state = STATE_WARNING;
87static int match_flags = NP_MATCH_EXACT;
88 73
89#ifdef HAVE_SSL 74static const int READ_TIMEOUT = 2;
90static char *sni = NULL;
91static bool sni_specified = false;
92#endif
93 75
94#define FLAG_SSL 0x01 76const int MAXBUF = 1024;
95#define FLAG_VERBOSE 0x02 77
96#define FLAG_TIME_WARN 0x04 78const int DEFAULT_FTP_PORT = 21;
97#define FLAG_TIME_CRIT 0x08 79const int DEFAULT_POP_PORT = 110;
98#define FLAG_HIDE_OUTPUT 0x10 80const int DEFAULT_SPOP_PORT = 995;
99static size_t flags; 81const int DEFAULT_SMTP_PORT = 25;
82const int DEFAULT_SSMTP_PORT = 465;
83const int DEFAULT_IMAP_PORT = 143;
84const int DEFAULT_SIMAP_PORT = 993;
85const int DEFAULT_XMPP_C2S_PORT = 5222;
86const int DEFAULT_NNTP_PORT = 119;
87const int DEFAULT_NNTPS_PORT = 563;
88const int DEFAULT_CLAMD_PORT = 3310;
100 89
101int main(int argc, char **argv) { 90int main(int argc, char **argv) {
102 setlocale(LC_ALL, ""); 91 setlocale(LC_ALL, "");
@@ -105,277 +94,377 @@ int main(int argc, char **argv) {
105 94
106 /* determine program- and service-name quickly */ 95 /* determine program- and service-name quickly */
107 progname = strrchr(argv[0], '/'); 96 progname = strrchr(argv[0], '/');
108 if (progname != NULL) 97 if (progname != NULL) {
109 progname++; 98 progname++;
110 else 99 } else {
111 progname = argv[0]; 100 progname = argv[0];
101 }
102
103 // Initialize config here with values from above,
104 // might be changed by on disk config or cli commands
105 check_tcp_config config = check_tcp_config_init();
112 106
113 size_t prog_name_len = strlen(progname); 107 size_t prog_name_len = strlen(progname);
114 if (prog_name_len > 6 && !memcmp(progname, "check_", 6)) { 108 const size_t prefix_length = strlen("check_");
115 SERVICE = strdup(progname + 6); 109
116 for (size_t i = 0; i < prog_name_len - 6; i++) 110 if (prog_name_len <= prefix_length) {
117 SERVICE[i] = toupper(SERVICE[i]); 111 die(STATE_UNKNOWN, _("Weird progname"));
112 }
113
114 if (!memcmp(progname, "check_", prefix_length)) {
115 config.service = strdup(progname + prefix_length);
116 if (config.service == NULL) {
117 die(STATE_UNKNOWN, _("Allocation failed"));
118 }
119
120 for (size_t i = 0; i < prog_name_len - prefix_length; i++) {
121 config.service[i] = toupper(config.service[i]);
122 }
118 } 123 }
119 124
120 /* set up a reasonable buffer at first (will be realloc()'ed if 125 /* set up a reasonable buffer at first (will be realloc()'ed if
121 * user specifies other options) */ 126 * user specifies other options) */
122 server_expect = calloc(2, sizeof(char *)); 127 config.server_expect = calloc(2, sizeof(char *));
128
129 if (config.server_expect == NULL) {
130 die(STATE_UNKNOWN, _("Allocation failed"));
131 }
123 132
124 /* determine defaults for this service's protocol */ 133 /* determine defaults for this service's protocol */
125 if (!strncmp(SERVICE, "UDP", 3)) { 134 if (!strncmp(config.service, "UDP", strlen("UDP"))) {
126 PROTOCOL = IPPROTO_UDP; 135 config.protocol = IPPROTO_UDP;
127 } else if (!strncmp(SERVICE, "FTP", 3)) { 136 } else if (!strncmp(config.service, "FTP", strlen("FTP"))) {
128 EXPECT = "220"; 137 config.server_expect[0] = "220";
129 QUIT = "QUIT\r\n"; 138 config.quit = "QUIT\r\n";
130 PORT = 21; 139 config.server_port = DEFAULT_FTP_PORT;
131 } else if (!strncmp(SERVICE, "POP", 3) || !strncmp(SERVICE, "POP3", 4)) { 140 } else if (!strncmp(config.service, "POP", strlen("POP")) || !strncmp(config.service, "POP3", strlen("POP3"))) {
132 EXPECT = "+OK"; 141 config.server_expect[0] = "+OK";
133 QUIT = "QUIT\r\n"; 142 config.quit = "QUIT\r\n";
134 PORT = 110; 143 config.server_port = DEFAULT_POP_PORT;
135 } else if (!strncmp(SERVICE, "SMTP", 4)) { 144 } else if (!strncmp(config.service, "SMTP", strlen("SMTP"))) {
136 EXPECT = "220"; 145 config.server_expect[0] = "220";
137 QUIT = "QUIT\r\n"; 146 config.quit = "QUIT\r\n";
138 PORT = 25; 147 config.server_port = DEFAULT_SMTP_PORT;
139 } else if (!strncmp(SERVICE, "IMAP", 4)) { 148 } else if (!strncmp(config.service, "IMAP", strlen("IMAP"))) {
140 EXPECT = "* OK"; 149 config.server_expect[0] = "* OK";
141 QUIT = "a1 LOGOUT\r\n"; 150 config.quit = "a1 LOGOUT\r\n";
142 PORT = 143; 151 config.server_port = DEFAULT_IMAP_PORT;
143 } 152 }
144#ifdef HAVE_SSL 153#ifdef HAVE_SSL
145 else if (!strncmp(SERVICE, "SIMAP", 5)) { 154 else if (!strncmp(config.service, "SIMAP", strlen("SIMAP"))) {
146 EXPECT = "* OK"; 155 config.server_expect[0] = "* OK";
147 QUIT = "a1 LOGOUT\r\n"; 156 config.quit = "a1 LOGOUT\r\n";
148 flags |= FLAG_SSL; 157 config.use_tls = true;
149 PORT = 993; 158 config.server_port = DEFAULT_SIMAP_PORT;
150 } else if (!strncmp(SERVICE, "SPOP", 4)) { 159 } else if (!strncmp(config.service, "SPOP", strlen("SPOP"))) {
151 EXPECT = "+OK"; 160 config.server_expect[0] = "+OK";
152 QUIT = "QUIT\r\n"; 161 config.quit = "QUIT\r\n";
153 flags |= FLAG_SSL; 162 config.use_tls = true;
154 PORT = 995; 163 config.server_port = DEFAULT_SPOP_PORT;
155 } else if (!strncmp(SERVICE, "SSMTP", 5)) { 164 } else if (!strncmp(config.service, "SSMTP", strlen("SSMTP"))) {
156 EXPECT = "220"; 165 config.server_expect[0] = "220";
157 QUIT = "QUIT\r\n"; 166 config.quit = "QUIT\r\n";
158 flags |= FLAG_SSL; 167 config.use_tls = true;
159 PORT = 465; 168 config.server_port = DEFAULT_SSMTP_PORT;
160 } else if (!strncmp(SERVICE, "JABBER", 6)) { 169 } else if (!strncmp(config.service, "JABBER", strlen("JABBER"))) {
161 SEND = "<stream:stream to=\'host\' xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'>\n"; 170 config.send = "<stream:stream to=\'host\' xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'>\n";
162 EXPECT = "<?xml version=\'1.0\'"; 171 config.server_expect[0] = "<?xml version=\'1.0\'";
163 QUIT = "</stream:stream>\n"; 172 config.quit = "</stream:stream>\n";
164 flags |= FLAG_HIDE_OUTPUT; 173 config.hide_output = true;
165 PORT = 5222; 174 config.server_port = DEFAULT_XMPP_C2S_PORT;
166 } else if (!strncmp(SERVICE, "NNTPS", 5)) { 175 } else if (!strncmp(config.service, "NNTPS", strlen("NNTPS"))) {
167 server_expect_count = 2; 176 config.server_expect_count = 2;
168 server_expect[0] = "200"; 177 config.server_expect[0] = "200";
169 server_expect[1] = "201"; 178 config.server_expect[1] = "201";
170 QUIT = "QUIT\r\n"; 179 config.quit = "QUIT\r\n";
171 flags |= FLAG_SSL; 180 config.use_tls = true;
172 PORT = 563; 181 config.server_port = DEFAULT_NNTPS_PORT;
173 } 182 }
174#endif 183#endif
175 else if (!strncmp(SERVICE, "NNTP", 4)) { 184 else if (!strncmp(config.service, "NNTP", strlen("NNTP"))) {
176 server_expect_count = 2; 185 config.server_expect_count = 2;
177 server_expect = malloc(sizeof(char *) * server_expect_count); 186 char **tmp = realloc(config.server_expect, config.server_expect_count * sizeof(char *));
178 server_expect[0] = strdup("200"); 187 if (tmp == NULL) {
179 server_expect[1] = strdup("201"); 188 free(config.server_expect);
180 QUIT = "QUIT\r\n"; 189 die(STATE_UNKNOWN, _("Allocation failed"));
181 PORT = 119; 190 }
182 } else if (!strncmp(SERVICE, "CLAMD", 5)) { 191 config.server_expect = tmp;
183 SEND = "PING"; 192
184 EXPECT = "PONG"; 193 config.server_expect[0] = strdup("200");
185 QUIT = NULL; 194 config.server_expect[1] = strdup("201");
186 PORT = 3310; 195 config.quit = "QUIT\r\n";
196 config.server_port = DEFAULT_NNTP_PORT;
197 } else if (!strncmp(config.service, "CLAMD", strlen("CLAMD"))) {
198 config.send = "PING";
199 config.server_expect[0] = "PONG";
200 config.quit = NULL;
201 config.server_port = DEFAULT_CLAMD_PORT;
187 } 202 }
188 /* fallthrough check, so it's supposed to use reverse matching */ 203 /* fallthrough check, so it's supposed to use reverse matching */
189 else if (strcmp(SERVICE, "TCP")) 204 else if (strcmp(config.service, "TCP")) {
190 usage(_("CRITICAL - Generic check_tcp called with unknown service\n")); 205 usage(_("CRITICAL - Generic check_tcp called with unknown service\n"));
191 206 }
192 server_address = "127.0.0.1";
193 server_port = PORT;
194 server_send = SEND;
195 server_quit = QUIT;
196 char *status = NULL;
197 207
198 /* Parse extra opts if any */ 208 /* Parse extra opts if any */
199 argv = np_extra_opts(&argc, argv, progname); 209 argv = np_extra_opts(&argc, argv, progname);
200 210
201 if (process_arguments(argc, argv) == ERROR) 211 check_tcp_config_wrapper paw = process_arguments(argc, argv, config);
212 if (paw.errorcode == ERROR) {
202 usage4(_("Could not parse arguments")); 213 usage4(_("Could not parse arguments"));
214 }
203 215
204 if (flags & FLAG_VERBOSE) { 216 config = paw.config;
205 printf("Using service %s\n", SERVICE); 217
206 printf("Port: %d\n", server_port); 218 if (verbosity > 0) {
207 printf("flags: 0x%x\n", (int)flags); 219 printf("Using service %s\n", config.service);
220 printf("Port: %d\n", config.server_port);
208 } 221 }
209 222
210 if (EXPECT && !server_expect_count) 223 if ((config.server_expect_count == 0) && config.server_expect[0]) {
211 server_expect_count++; 224 config.server_expect_count++;
225 }
212 226
213 if (PROTOCOL == IPPROTO_UDP && !(server_expect_count && server_send)) { 227 if (config.protocol == IPPROTO_UDP && !(config.server_expect_count && config.send)) {
214 usage(_("With UDP checks, a send/expect string must be specified.")); 228 usage(_("With UDP checks, a send/expect string must be specified."));
215 } 229 }
216 230
231 // Initialize check stuff before setting timers
232 mp_check overall = mp_check_init();
233 if (config.output_format_set) {
234 mp_set_format(config.output_format);
235 }
236
217 /* set up the timer */ 237 /* set up the timer */
218 signal(SIGALRM, socket_timeout_alarm_handler); 238 signal(SIGALRM, socket_timeout_alarm_handler);
219 alarm(socket_timeout); 239 alarm(socket_timeout);
220 240
221 /* try to connect to the host at the given port number */ 241 /* try to connect to the host at the given port number */
222 struct timeval tv; 242 struct timeval start_time;
223 gettimeofday(&tv, NULL); 243 gettimeofday(&start_time, NULL);
224 244
225 int result = STATE_UNKNOWN; 245 int socket_descriptor = 0;
226 result = np_net_connect(server_address, server_port, &sd, PROTOCOL); 246 mp_subcheck inital_connect_result = mp_subcheck_init();
227 if (result == STATE_CRITICAL) 247
228 return econn_refuse_state; 248 // Try initial connection
249 if (np_net_connect(config.server_address, config.server_port, &socket_descriptor, config.protocol) == STATE_CRITICAL) {
250 // Early exit here, we got connection refused
251 inital_connect_result = mp_set_subcheck_state(inital_connect_result, config.econn_refuse_state);
252 xasprintf(&inital_connect_result.output, "Connection to %s on port %i was REFUSED", config.server_address, config.server_port);
253 mp_add_subcheck_to_check(&overall, inital_connect_result);
254 mp_exit(overall);
255 } else {
256 inital_connect_result = mp_set_subcheck_state(inital_connect_result, STATE_OK);
257 xasprintf(&inital_connect_result.output, "Connection to %s on port %i was a SUCCESS", config.server_address, config.server_port);
258 mp_add_subcheck_to_check(&overall, inital_connect_result);
259 }
229 260
230#ifdef HAVE_SSL 261#ifdef HAVE_SSL
231 if (flags & FLAG_SSL) { 262 if (config.use_tls) {
232 result = np_net_ssl_init_with_hostname(sd, (sni_specified ? sni : NULL)); 263 mp_subcheck tls_connection_result = mp_subcheck_init();
233 if (result == STATE_OK && check_cert) { 264 mp_state_enum result = np_net_ssl_init_with_hostname(socket_descriptor, (config.sni_specified ? config.sni : NULL));
234 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 265 tls_connection_result = mp_set_subcheck_default_state(tls_connection_result, result);
266
267 if (result == STATE_OK) {
268 xasprintf(&tls_connection_result.output, "TLS connection succeeded");
269
270 if (config.check_cert) {
271 result = np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit);
272
273 mp_subcheck tls_certificate_lifetime_result = mp_subcheck_init();
274 tls_certificate_lifetime_result = mp_set_subcheck_state(tls_certificate_lifetime_result, result);
275
276 if (result == STATE_OK) {
277 xasprintf(&tls_certificate_lifetime_result.output, "Certificate lifetime is within thresholds");
278 } else if (result == STATE_WARNING) {
279 xasprintf(&tls_certificate_lifetime_result.output, "Certificate lifetime is violating warning threshold (%i)",
280 config.days_till_exp_warn);
281 } else if (result == STATE_CRITICAL) {
282 xasprintf(&tls_certificate_lifetime_result.output, "Certificate lifetime is violating critical threshold (%i)",
283 config.days_till_exp_crit);
284 } else {
285 xasprintf(&tls_certificate_lifetime_result.output, "Certificate lifetime is somehow unknown");
286 }
287
288 mp_add_subcheck_to_subcheck(&tls_connection_result, tls_certificate_lifetime_result);
289 }
290
291 mp_add_subcheck_to_check(&overall, tls_connection_result);
292 } else {
293 xasprintf(&tls_connection_result.output, "TLS connection failed");
294 mp_add_subcheck_to_check(&overall, tls_connection_result);
295
296 if (socket_descriptor) {
297 close(socket_descriptor);
298 }
299 np_net_ssl_cleanup();
300
301 mp_exit(overall);
235 } 302 }
236 } 303 }
237 if (result != STATE_OK) {
238 if (sd)
239 close(sd);
240 np_net_ssl_cleanup();
241 return result;
242 }
243#endif /* HAVE_SSL */ 304#endif /* HAVE_SSL */
244 305
245 if (server_send != NULL) { /* Something to send? */ 306 if (config.send != NULL) { /* Something to send? */
246 my_send(server_send, strlen(server_send)); 307 my_send(socket_descriptor, config.send, strlen(config.send), config.use_tls);
247 } 308 }
248 309
249 if (delay > 0) { 310 if (config.delay > 0) {
250 tv.tv_sec += delay; 311 start_time.tv_sec += config.delay;
251 sleep(delay); 312 sleep(config.delay);
252 } 313 }
253 314
254 if (flags & FLAG_VERBOSE) { 315 if (verbosity > 0) {
255 if (server_send) { 316 if (config.send) {
256 printf("Send string: %s\n", server_send); 317 printf("Send string: %s\n", config.send);
318 }
319 if (config.quit) {
320 printf("Quit string: %s\n", config.quit);
257 } 321 }
258 if (server_quit) { 322 printf("server_expect_count: %d\n", (int)config.server_expect_count);
259 printf("Quit string: %s\n", server_quit); 323 for (size_t i = 0; i < config.server_expect_count; i++) {
324 printf("\t%zd: %s\n", i, config.server_expect[i]);
260 } 325 }
261 printf("server_expect_count: %d\n", (int)server_expect_count);
262 for (size_t i = 0; i < server_expect_count; i++)
263 printf("\t%zd: %s\n", i, server_expect[i]);
264 } 326 }
265 327
266 /* if(len) later on, we know we have a non-NULL response */ 328 /* if(len) later on, we know we have a non-NULL response */
267 ssize_t len = 0; 329 ssize_t len = 0;
330 char *received_buffer = NULL;
331 enum np_match_result match = NP_MATCH_NONE;
332 mp_subcheck expected_data_result = mp_subcheck_init();
268 333
269 int match = -1; 334 if (config.server_expect_count) {
270 struct timeval timeout;
271 fd_set rfds;
272 FD_ZERO(&rfds);
273 if (server_expect_count) {
274 ssize_t received = 0; 335 ssize_t received = 0;
336 char buffer[MAXBUF];
275 337
276 /* watch for the expect string */ 338 /* watch for the expect string */
277 while ((received = my_recv(buffer, sizeof(buffer))) > 0) { 339 while ((received = my_recv(socket_descriptor, buffer, sizeof(buffer), config.use_tls)) > 0) {
278 status = realloc(status, len + received + 1); 340 received_buffer = realloc(received_buffer, len + received + 1);
279 memcpy(&status[len], buffer, received); 341
342 if (received_buffer == NULL) {
343 die(STATE_UNKNOWN, _("Allocation failed"));
344 }
345
346 memcpy(&received_buffer[len], buffer, received);
280 len += received; 347 len += received;
281 status[len] = '\0'; 348 received_buffer[len] = '\0';
282 349
283 /* stop reading if user-forced */ 350 /* stop reading if user-forced */
284 if (maxbytes && len >= maxbytes) 351 if (config.maxbytes && len >= config.maxbytes) {
285 break; 352 break;
353 }
286 354
287 if ((match = np_expect_match(status, server_expect, server_expect_count, match_flags)) != NP_MATCH_RETRY) 355 if ((match = np_expect_match(received_buffer, config.server_expect, config.server_expect_count, config.match_flags)) !=
356 NP_MATCH_RETRY) {
288 break; 357 break;
358 }
359
360 fd_set rfds;
361 FD_ZERO(&rfds);
362 FD_SET(socket_descriptor, &rfds);
289 363
290 /* some protocols wait for further input, so make sure we don't wait forever */ 364 /* some protocols wait for further input, so make sure we don't wait forever */
291 FD_SET(sd, &rfds); 365 struct timeval timeout;
292 timeout.tv_sec = READ_TIMEOUT; 366 timeout.tv_sec = READ_TIMEOUT;
293 timeout.tv_usec = 0; 367 timeout.tv_usec = 0;
294 if (select(sd + 1, &rfds, NULL, NULL, &timeout) <= 0) 368
369 if (select(socket_descriptor + 1, &rfds, NULL, NULL, &timeout) <= 0) {
295 break; 370 break;
371 }
296 } 372 }
297 373
298 if (match == NP_MATCH_RETRY) 374 if (match == NP_MATCH_RETRY) {
299 match = NP_MATCH_FAILURE; 375 match = NP_MATCH_FAILURE;
376 }
300 377
301 /* no data when expected, so return critical */ 378 /* no data when expected, so return critical */
302 if (len == 0) 379 if (len == 0) {
303 die(STATE_CRITICAL, _("No data received from host\n")); 380 xasprintf(&expected_data_result.output, "Received no data when some was expected");
381 expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_CRITICAL);
382 mp_add_subcheck_to_check(&overall, expected_data_result);
383 mp_exit(overall);
384 }
304 385
305 /* print raw output if we're debugging */ 386 /* print raw output if we're debugging */
306 if (flags & FLAG_VERBOSE) 387 if (verbosity > 0) {
307 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n", (int)len + 1, status); 388 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n", (int)len + 1, received_buffer);
389 }
308 /* strip whitespace from end of output */ 390 /* strip whitespace from end of output */
309 while (--len > 0 && isspace(status[len])) 391 while (--len > 0 && isspace(received_buffer[len])) {
310 status[len] = '\0'; 392 received_buffer[len] = '\0';
393 }
311 } 394 }
312 395
313 if (server_quit != NULL) { 396 if (config.quit != NULL) {
314 my_send(server_quit, strlen(server_quit)); 397 my_send(socket_descriptor, config.quit, strlen(config.quit), config.use_tls);
398 }
399
400 if (socket_descriptor) {
401 close(socket_descriptor);
315 } 402 }
316 if (sd)
317 close(sd);
318#ifdef HAVE_SSL 403#ifdef HAVE_SSL
319 np_net_ssl_cleanup(); 404 np_net_ssl_cleanup();
320#endif 405#endif
321 406
322 microsec = deltime(tv); 407 long microsec = deltime(start_time);
323 elapsed_time = (double)microsec / 1.0e6; 408 double elapsed_time = (double)microsec / 1.0e6;
324 409
325 if (flags & FLAG_TIME_CRIT && elapsed_time > critical_time) 410 mp_subcheck elapsed_time_result = mp_subcheck_init();
326 result = STATE_CRITICAL;
327 else if (flags & FLAG_TIME_WARN && elapsed_time > warning_time)
328 result = STATE_WARNING;
329 411
330 /* did we get the response we hoped? */ 412 mp_perfdata time_pd = perfdata_init();
331 if (match == NP_MATCH_FAILURE && result != STATE_CRITICAL) 413 time_pd = mp_set_pd_value(time_pd, elapsed_time);
332 result = expect_mismatch_state; 414 time_pd.label = "time";
415 time_pd.uom = "s";
333 416
334 /* reset the alarm */ 417 if (config.critical_time_set && elapsed_time > config.critical_time) {
335 alarm(0); 418 xasprintf(&elapsed_time_result.output, "Connection time %fs exceeded critical threshold (%f)", elapsed_time, config.critical_time);
419
420 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_CRITICAL);
421 time_pd.crit_present = true;
422 mp_range crit_val = mp_range_init();
423
424 crit_val.end = mp_create_pd_value(config.critical_time);
425 crit_val.end_infinity = false;
336 426
337 /* this is a bit stupid, because we don't want to print the 427 time_pd.crit = crit_val;
338 * response time (which can look ok to the user) if we didn't get 428 } else if (config.warning_time_set && elapsed_time > config.warning_time) {
339 * the response we were looking for. if-else */ 429 xasprintf(&elapsed_time_result.output, "Connection time %fs exceeded warning threshold (%f)", elapsed_time, config.critical_time);
340 printf("%s %s - ", SERVICE, state_text(result)); 430
341 431 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_WARNING);
342 if (match == NP_MATCH_FAILURE && len && !(flags & FLAG_HIDE_OUTPUT)) 432 time_pd.warn_present = true;
343 printf("Unexpected response from host/socket: %s", status); 433 mp_range warn_val = mp_range_init();
344 else { 434 warn_val.end = mp_create_pd_value(config.critical_time);
345 if (match == NP_MATCH_FAILURE) 435 warn_val.end_infinity = false;
346 printf("Unexpected response from host/socket on "); 436
347 else 437 time_pd.warn = warn_val;
348 printf("%.3f second response time on ", elapsed_time); 438 } else {
349 if (server_address[0] != '/') { 439 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_OK);
350 if (host_specified) 440 xasprintf(&elapsed_time_result.output, "Connection time %fs is within thresholds", elapsed_time);
351 printf("%s port %d", server_address, server_port);
352 else
353 printf("port %d", server_port);
354 } else
355 printf("socket %s", server_address);
356 } 441 }
357 442
358 if (match != NP_MATCH_FAILURE && !(flags & FLAG_HIDE_OUTPUT) && len) 443 mp_add_perfdata_to_subcheck(&elapsed_time_result, time_pd);
359 printf(" [%s]", status); 444 mp_add_subcheck_to_check(&overall, elapsed_time_result);
360 445
361 /* perf-data doesn't apply when server doesn't talk properly, 446 /* did we get the response we hoped? */
362 * so print all zeroes on warn and crit. Use fperfdata since 447 if (match == NP_MATCH_FAILURE) {
363 * localisation settings can make different outputs */ 448 expected_data_result = mp_set_subcheck_state(expected_data_result, config.expect_mismatch_state);
364 if (match == NP_MATCH_FAILURE) 449 xasprintf(&expected_data_result.output, "Answer failed to match expectation");
365 printf("|%s", fperfdata("time", elapsed_time, "s", (flags & FLAG_TIME_WARN ? true : false), 0, 450 mp_add_subcheck_to_check(&overall, expected_data_result);
366 (flags & FLAG_TIME_CRIT ? true : false), 0, true, 0, true, socket_timeout)); 451 } else if (match == NP_MATCH_SUCCESS) {
367 else 452 expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_OK);
368 printf("|%s", fperfdata("time", elapsed_time, "s", (flags & FLAG_TIME_WARN ? true : false), warning_time, 453 xasprintf(&expected_data_result.output, "The answer of the server matched the expectation");
369 (flags & FLAG_TIME_CRIT ? true : false), critical_time, true, 0, true, socket_timeout)); 454 mp_add_subcheck_to_check(&overall, expected_data_result);
455 }
370 456
371 putchar('\n'); 457 /* reset the alarm */
372 return result; 458 alarm(0);
459
460 mp_exit(overall);
373} 461}
374 462
375/* process command-line arguments */ 463/* process command-line arguments */
376static int process_arguments(int argc, char **argv) { 464static check_tcp_config_wrapper process_arguments(int argc, char **argv, check_tcp_config config) {
377 enum { 465 enum {
378 SNI_OPTION = CHAR_MAX + 1 466 SNI_OPTION = CHAR_MAX + 1,
467 output_format_index,
379 }; 468 };
380 469
381 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 470 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
@@ -404,54 +493,58 @@ static int process_arguments(int argc, char **argv) {
404 {"ssl", no_argument, 0, 'S'}, 493 {"ssl", no_argument, 0, 'S'},
405 {"sni", required_argument, 0, SNI_OPTION}, 494 {"sni", required_argument, 0, SNI_OPTION},
406 {"certificate", required_argument, 0, 'D'}, 495 {"certificate", required_argument, 0, 'D'},
496 {"output-format", required_argument, 0, output_format_index},
407 {0, 0, 0, 0}}; 497 {0, 0, 0, 0}};
408 498
409 if (argc < 2) 499 if (argc < 2) {
410 usage4(_("No arguments found")); 500 usage4(_("No arguments found"));
501 }
411 502
412 /* backwards compatibility */ 503 /* backwards compatibility */
413 for (int i = 1; i < argc; i++) { 504 for (int i = 1; i < argc; i++) {
414 if (strcmp("-to", argv[i]) == 0) 505 if (strcmp("-to", argv[i]) == 0) {
415 strcpy(argv[i], "-t"); 506 strcpy(argv[i], "-t");
416 else if (strcmp("-wt", argv[i]) == 0) 507 } else if (strcmp("-wt", argv[i]) == 0) {
417 strcpy(argv[i], "-w"); 508 strcpy(argv[i], "-w");
418 else if (strcmp("-ct", argv[i]) == 0) 509 } else if (strcmp("-ct", argv[i]) == 0) {
419 strcpy(argv[i], "-c"); 510 strcpy(argv[i], "-c");
511 }
420 } 512 }
421 513
422 if (!is_option(argv[1])) { 514 if (!is_option(argv[1])) {
423 server_address = argv[1]; 515 config.server_address = argv[1];
424 argv[1] = argv[0]; 516 argv[1] = argv[0];
425 argv = &argv[1]; 517 argv = &argv[1];
426 argc--; 518 argc--;
427 } 519 }
428 520
429 int option_char;
430 bool escape = false; 521 bool escape = false;
522
431 while (true) { 523 while (true) {
432 int option = 0; 524 int option = 0;
433 option_char = getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option); 525 int option_index = getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option);
434 526
435 if (option_char == -1 || option_char == EOF || option_char == 1) 527 if (option_index == -1 || option_index == EOF || option_index == 1) {
436 break; 528 break;
529 }
437 530
438 switch (option_char) { 531 switch (option_index) {
439 case '?': /* print short usage statement if args not parsable */ 532 case '?': /* print short usage statement if args not parsable */
440 usage5(); 533 usage5();
441 case 'h': /* help */ 534 case 'h': /* help */
442 print_help(); 535 print_help(config.service);
443 exit(STATE_UNKNOWN); 536 exit(STATE_UNKNOWN);
444 case 'V': /* version */ 537 case 'V': /* version */
445 print_revision(progname, NP_VERSION); 538 print_revision(progname, NP_VERSION);
446 exit(STATE_UNKNOWN); 539 exit(STATE_UNKNOWN);
447 case 'v': /* verbose mode */ 540 case 'v': /* verbose mode */
448 flags |= FLAG_VERBOSE; 541 verbosity++;
449 match_flags |= NP_MATCH_VERBOSE; 542 config.match_flags |= NP_MATCH_VERBOSE;
450 break; 543 break;
451 case '4': 544 case '4': // Apparently unused TODO
452 address_family = AF_INET; 545 address_family = AF_INET;
453 break; 546 break;
454 case '6': 547 case '6': // Apparently unused TODO
455#ifdef USE_IPV6 548#ifdef USE_IPV6
456 address_family = AF_INET6; 549 address_family = AF_INET6;
457#else 550#else
@@ -459,163 +552,190 @@ static int process_arguments(int argc, char **argv) {
459#endif 552#endif
460 break; 553 break;
461 case 'H': /* hostname */ 554 case 'H': /* hostname */
462 host_specified = true; 555 config.host_specified = true;
463 server_address = optarg; 556 config.server_address = optarg;
464 break; 557 break;
465 case 'c': /* critical */ 558 case 'c': /* critical */
466 critical_time = strtod(optarg, NULL); 559 config.critical_time = strtod(optarg, NULL);
467 flags |= FLAG_TIME_CRIT; 560 config.critical_time_set = true;
468 break; 561 break;
469 case 'j': /* hide output */ 562 case 'j': /* hide output */
470 flags |= FLAG_HIDE_OUTPUT; 563 config.hide_output = true;
471 break; 564 break;
472 case 'w': /* warning */ 565 case 'w': /* warning */
473 warning_time = strtod(optarg, NULL); 566 config.warning_time = strtod(optarg, NULL);
474 flags |= FLAG_TIME_WARN; 567 config.warning_time_set = true;
475 break;
476 case 'C':
477 crit_codes = realloc(crit_codes, ++crit_codes_count);
478 crit_codes[crit_codes_count - 1] = optarg;
479 break;
480 case 'W':
481 warn_codes = realloc(warn_codes, ++warn_codes_count);
482 warn_codes[warn_codes_count - 1] = optarg;
483 break; 568 break;
484 case 't': /* timeout */ 569 case 't': /* timeout */
485 if (!is_intpos(optarg)) 570 if (!is_intpos(optarg)) {
486 usage4(_("Timeout interval must be a positive integer")); 571 usage4(_("Timeout interval must be a positive integer"));
487 else 572 } else {
488 socket_timeout = atoi(optarg); 573 socket_timeout = atoi(optarg);
574 }
489 break; 575 break;
490 case 'p': /* port */ 576 case 'p': /* port */
491 if (!is_intpos(optarg)) 577 if (!is_intpos(optarg)) {
492 usage4(_("Port must be a positive integer")); 578 usage4(_("Port must be a positive integer"));
493 else 579 } else {
494 server_port = atoi(optarg); 580 config.server_port = atoi(optarg);
581 }
495 break; 582 break;
496 case 'E': 583 case 'E':
497 escape = true; 584 escape = true;
498 break; 585 break;
499 case 's': 586 case 's':
500 if (escape) 587 if (escape) {
501 server_send = np_escaped_string(optarg); 588 config.send = np_escaped_string(optarg);
502 else 589 } else {
503 xasprintf(&server_send, "%s", optarg); 590 xasprintf(&config.send, "%s", optarg);
591 }
504 break; 592 break;
505 case 'e': /* expect string (may be repeated) */ 593 case 'e': /* expect string (may be repeated) */
506 match_flags &= ~NP_MATCH_EXACT; 594 config.match_flags &= ~NP_MATCH_EXACT;
507 if (server_expect_count == 0) 595 if (config.server_expect_count == 0) {
508 server_expect = malloc(sizeof(char *) * (++server_expect_count)); 596 config.server_expect = malloc(sizeof(char *) * (++config.server_expect_count));
509 else 597 } else {
510 server_expect = realloc(server_expect, sizeof(char *) * (++server_expect_count)); 598 config.server_expect = realloc(config.server_expect, sizeof(char *) * (++config.server_expect_count));
511 server_expect[server_expect_count - 1] = optarg; 599 }
600
601 if (config.server_expect == NULL) {
602 die(STATE_UNKNOWN, _("Allocation failed"));
603 }
604 config.server_expect[config.server_expect_count - 1] = optarg;
512 break; 605 break;
513 case 'm': 606 case 'm':
514 if (!is_intpos(optarg)) 607 if (!is_intpos(optarg)) {
515 usage4(_("Maxbytes must be a positive integer")); 608 usage4(_("Maxbytes must be a positive integer"));
516 else 609 } else {
517 maxbytes = strtol(optarg, NULL, 0); 610 config.maxbytes = strtol(optarg, NULL, 0);
611 }
518 break; 612 break;
519 case 'q': 613 case 'q':
520 if (escape) 614 if (escape) {
521 server_quit = np_escaped_string(optarg); 615 config.quit = np_escaped_string(optarg);
522 else 616 } else {
523 xasprintf(&server_quit, "%s\r\n", optarg); 617 xasprintf(&config.quit, "%s\r\n", optarg);
618 }
524 break; 619 break;
525 case 'r': 620 case 'r':
526 if (!strncmp(optarg, "ok", 2)) 621 if (!strncmp(optarg, "ok", 2)) {
527 econn_refuse_state = STATE_OK; 622 config.econn_refuse_state = STATE_OK;
528 else if (!strncmp(optarg, "warn", 4)) 623 } else if (!strncmp(optarg, "warn", 4)) {
529 econn_refuse_state = STATE_WARNING; 624 config.econn_refuse_state = STATE_WARNING;
530 else if (!strncmp(optarg, "crit", 4)) 625 } else if (!strncmp(optarg, "crit", 4)) {
531 econn_refuse_state = STATE_CRITICAL; 626 config.econn_refuse_state = STATE_CRITICAL;
532 else 627 } else {
533 usage4(_("Refuse must be one of ok, warn, crit")); 628 usage4(_("Refuse must be one of ok, warn, crit"));
629 }
534 break; 630 break;
535 case 'M': 631 case 'M':
536 if (!strncmp(optarg, "ok", 2)) 632 if (!strncmp(optarg, "ok", 2)) {
537 expect_mismatch_state = STATE_OK; 633 config.expect_mismatch_state = STATE_OK;
538 else if (!strncmp(optarg, "warn", 4)) 634 } else if (!strncmp(optarg, "warn", 4)) {
539 expect_mismatch_state = STATE_WARNING; 635 config.expect_mismatch_state = STATE_WARNING;
540 else if (!strncmp(optarg, "crit", 4)) 636 } else if (!strncmp(optarg, "crit", 4)) {
541 expect_mismatch_state = STATE_CRITICAL; 637 config.expect_mismatch_state = STATE_CRITICAL;
542 else 638 } else {
543 usage4(_("Mismatch must be one of ok, warn, crit")); 639 usage4(_("Mismatch must be one of ok, warn, crit"));
640 }
544 break; 641 break;
545 case 'd': 642 case 'd':
546 if (is_intpos(optarg)) 643 if (is_intpos(optarg)) {
547 delay = atoi(optarg); 644 config.delay = atoi(optarg);
548 else 645 } else {
549 usage4(_("Delay must be a positive integer")); 646 usage4(_("Delay must be a positive integer"));
647 }
550 break; 648 break;
551 case 'D': { /* Check SSL cert validity - days 'til certificate expiration */ 649 case 'D': /* Check SSL cert validity - days 'til certificate expiration */
552#ifdef HAVE_SSL 650#ifdef HAVE_SSL
553# ifdef USE_OPENSSL /* XXX */ 651# ifdef USE_OPENSSL /* XXX */
652 {
554 char *temp; 653 char *temp;
555 if ((temp = strchr(optarg, ',')) != NULL) { 654 if ((temp = strchr(optarg, ',')) != NULL) {
556 *temp = '\0'; 655 *temp = '\0';
557 if (!is_intnonneg(optarg)) 656 if (!is_intnonneg(optarg)) {
558 usage2(_("Invalid certificate expiration period"), optarg); 657 usage2(_("Invalid certificate expiration period"), optarg);
559 days_till_exp_warn = atoi(optarg); 658 }
659 config.days_till_exp_warn = atoi(optarg);
560 *temp = ','; 660 *temp = ',';
561 temp++; 661 temp++;
562 if (!is_intnonneg(temp)) 662 if (!is_intnonneg(temp)) {
563 usage2(_("Invalid certificate expiration period"), temp); 663 usage2(_("Invalid certificate expiration period"), temp);
564 days_till_exp_crit = atoi(temp); 664 }
665 config.days_till_exp_crit = atoi(temp);
565 } else { 666 } else {
566 days_till_exp_crit = 0; 667 config.days_till_exp_crit = 0;
567 if (!is_intnonneg(optarg)) 668 if (!is_intnonneg(optarg)) {
568 usage2(_("Invalid certificate expiration period"), optarg); 669 usage2(_("Invalid certificate expiration period"), optarg);
569 days_till_exp_warn = atoi(optarg); 670 }
671 config.days_till_exp_warn = atoi(optarg);
570 } 672 }
571 check_cert = true; 673 config.check_cert = true;
572 flags |= FLAG_SSL; 674 config.use_tls = true;
573 } break; 675 } break;
574# endif /* USE_OPENSSL */ 676# endif /* USE_OPENSSL */
575#endif 677#endif
576 /* fallthrough if we don't have ssl */ 678 /* fallthrough if we don't have ssl */
577 case 'S': 679 case 'S':
578#ifdef HAVE_SSL 680#ifdef HAVE_SSL
579 flags |= FLAG_SSL; 681 config.use_tls = true;
580#else 682#else
581 die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); 683 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
582#endif 684#endif
583 break; 685 break;
584 case SNI_OPTION: 686 case SNI_OPTION:
585#ifdef HAVE_SSL 687#ifdef HAVE_SSL
586 flags |= FLAG_SSL; 688 config.use_tls = true;
587 sni_specified = true; 689 config.sni_specified = true;
588 sni = optarg; 690 config.sni = optarg;
589#else 691#else
590 die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); 692 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
591#endif 693#endif
592 break; 694 break;
593 case 'A': 695 case 'A':
594 match_flags |= NP_MATCH_ALL; 696 config.match_flags |= NP_MATCH_ALL;
697 break;
698 case output_format_index: {
699 parsed_output_format parser = mp_parse_output_format(optarg);
700 if (!parser.parsing_success) {
701 // TODO List all available formats here, maybe add anothoer usage function
702 printf("Invalid output format: %s\n", optarg);
703 exit(STATE_UNKNOWN);
704 }
705
706 config.output_format_set = true;
707 config.output_format = parser.output_format;
595 break; 708 break;
596 } 709 }
710 }
597 } 711 }
598 712
599 option_char = optind; 713 int index = optind;
600 if (!host_specified && option_char < argc) 714 if (!config.host_specified && index < argc) {
601 server_address = strdup(argv[option_char++]); 715 config.server_address = strdup(argv[index++]);
716 }
602 717
603 if (server_address == NULL) 718 if (config.server_address == NULL) {
604 usage4(_("You must provide a server address")); 719 usage4(_("You must provide a server address"));
605 else if (server_address[0] != '/' && !is_host(server_address)) 720 } else if (config.server_address[0] != '/' && !is_host(config.server_address)) {
606 die(STATE_CRITICAL, "%s %s - %s: %s\n", SERVICE, state_text(STATE_CRITICAL), _("Invalid hostname, address or socket"), 721 die(STATE_CRITICAL, "%s %s - %s: %s\n", config.service, state_text(STATE_CRITICAL), _("Invalid hostname, address or socket"),
607 server_address); 722 config.server_address);
723 }
608 724
609 return OK; 725 check_tcp_config_wrapper result = {
726 .config = config,
727 .errorcode = OK,
728 };
729 return result;
610} 730}
611 731
612void print_help(void) { 732void print_help(const char *service) {
613 print_revision(progname, NP_VERSION); 733 print_revision(progname, NP_VERSION);
614 734
615 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 735 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
616 printf(COPYRIGHT, copyright, email); 736 printf(COPYRIGHT, copyright, email);
617 737
618 printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"), SERVICE); 738 printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"), service);
619 739
620 print_usage(); 740 print_usage();
621 741
@@ -662,6 +782,7 @@ void print_help(void) {
662 782
663 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 783 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
664 784
785 printf(UT_OUTPUT_FORMAT);
665 printf(UT_VERBOSE); 786 printf(UT_VERBOSE);
666 787
667 printf(UT_SUPPORT); 788 printf(UT_SUPPORT);
diff --git a/plugins/check_tcp.d/config.h b/plugins/check_tcp.d/config.h
new file mode 100644
index 00000000..dc25d79e
--- /dev/null
+++ b/plugins/check_tcp.d/config.h
@@ -0,0 +1,84 @@
1#pragma once
2
3#include "../../lib/utils_tcp.h"
4#include "output.h"
5#include "states.h"
6#include <netinet/in.h>
7
8typedef struct {
9 char *server_address;
10 bool host_specified;
11 int server_port; // TODO can this be a uint16?
12
13 int protocol; /* most common is default */
14 char *service;
15 char *send;
16 char *quit;
17 char **server_expect;
18 size_t server_expect_count;
19 bool use_tls;
20#ifdef HAVE_SSL
21 char *sni;
22 bool sni_specified;
23 bool check_cert;
24 int days_till_exp_warn;
25 int days_till_exp_crit;
26#endif // HAVE_SSL
27 int match_flags;
28 mp_state_enum expect_mismatch_state;
29 unsigned int delay;
30
31 bool warning_time_set;
32 double warning_time;
33 bool critical_time_set;
34 double critical_time;
35
36 mp_state_enum econn_refuse_state;
37
38 ssize_t maxbytes;
39
40 bool hide_output;
41
42 bool output_format_set;
43 mp_output_format output_format;
44} check_tcp_config;
45
46check_tcp_config check_tcp_config_init() {
47 check_tcp_config result = {
48 .server_address = "127.0.0.1",
49 .host_specified = false,
50 .server_port = 0,
51
52 .protocol = IPPROTO_TCP,
53 .service = "TCP",
54 .send = NULL,
55 .quit = NULL,
56 .server_expect = NULL,
57 .server_expect_count = 0,
58 .use_tls = false,
59#ifdef HAVE_SSL
60 .sni = NULL,
61 .sni_specified = false,
62 .check_cert = false,
63 .days_till_exp_warn = 0,
64 .days_till_exp_crit = 0,
65#endif // HAVE_SSL
66 .match_flags = NP_MATCH_EXACT,
67 .expect_mismatch_state = STATE_WARNING,
68 .delay = 0,
69
70 .warning_time_set = false,
71 .warning_time = 0,
72 .critical_time_set = false,
73 .critical_time = 0,
74
75 .econn_refuse_state = STATE_CRITICAL,
76
77 .maxbytes = 0,
78
79 .hide_output = false,
80
81 .output_format_set = false,
82 };
83 return result;
84}
diff --git a/plugins/check_time.c b/plugins/check_time.c
index d1f50683..debf59f3 100644
--- a/plugins/check_time.c
+++ b/plugins/check_time.c
@@ -28,6 +28,7 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
31const char *progname = "check_time"; 32const char *progname = "check_time";
32const char *copyright = "1999-2024"; 33const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
@@ -35,28 +36,15 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 36#include "common.h"
36#include "netutils.h" 37#include "netutils.h"
37#include "utils.h" 38#include "utils.h"
38 39#include "check_time.d/config.h"
39enum {
40 TIME_PORT = 37
41};
42 40
43#define UNIX_EPOCH 2208988800UL 41#define UNIX_EPOCH 2208988800UL
44 42
45static uint32_t raw_server_time; 43typedef struct {
46static unsigned long server_time, diff_time; 44 int errorcode;
47static int warning_time = 0; 45 check_time_config config;
48static bool check_warning_time = false; 46} check_time_config_wrapper;
49static int critical_time = 0; 47static check_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50static bool check_critical_time = false;
51static unsigned long warning_diff = 0;
52static bool check_warning_diff = false;
53static unsigned long critical_diff = 0;
54static bool check_critical_diff = false;
55static int server_port = TIME_PORT;
56static char *server_address = NULL;
57static bool use_udp = false;
58
59static int process_arguments(int, char **);
60static void print_help(void); 48static void print_help(void);
61void print_usage(void); 49void print_usage(void);
62 50
@@ -68,8 +56,12 @@ int main(int argc, char **argv) {
68 /* Parse extra opts if any */ 56 /* Parse extra opts if any */
69 argv = np_extra_opts(&argc, argv, progname); 57 argv = np_extra_opts(&argc, argv, progname);
70 58
71 if (process_arguments(argc, argv) == ERROR) 59 check_time_config_wrapper tmp_config = process_arguments(argc, argv);
60 if (tmp_config.errorcode == ERROR) {
72 usage4(_("Could not parse arguments")); 61 usage4(_("Could not parse arguments"));
62 }
63
64 const check_time_config config = tmp_config.config;
73 65
74 /* initialize alarm signal handling */ 66 /* initialize alarm signal handling */
75 signal(SIGALRM, socket_timeout_alarm_handler); 67 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -79,37 +71,40 @@ int main(int argc, char **argv) {
79 time(&start_time); 71 time(&start_time);
80 72
81 int socket; 73 int socket;
82 int result = STATE_UNKNOWN; 74 mp_state_enum result = STATE_UNKNOWN;
83 /* try to connect to the host at the given port number */ 75 /* try to connect to the host at the given port number */
84 if (use_udp) { 76 if (config.use_udp) {
85 result = my_udp_connect(server_address, server_port, &socket); 77 result = my_udp_connect(config.server_address, config.server_port, &socket);
86 } else { 78 } else {
87 result = my_tcp_connect(server_address, server_port, &socket); 79 result = my_tcp_connect(config.server_address, config.server_port, &socket);
88 } 80 }
89 81
90 if (result != STATE_OK) { 82 if (result != STATE_OK) {
91 if (check_critical_time) 83 if (config.check_critical_time) {
92 result = STATE_CRITICAL; 84 result = STATE_CRITICAL;
93 else if (check_warning_time) 85 } else if (config.check_warning_time) {
94 result = STATE_WARNING; 86 result = STATE_WARNING;
95 else 87 } else {
96 result = STATE_UNKNOWN; 88 result = STATE_UNKNOWN;
97 die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"), server_address, server_port); 89 }
90 die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"), config.server_address, config.server_port);
98 } 91 }
99 92
100 if (use_udp) { 93 if (config.use_udp) {
101 if (send(socket, "", 0, 0) < 0) { 94 if (send(socket, "", 0, 0) < 0) {
102 if (check_critical_time) 95 if (config.check_critical_time) {
103 result = STATE_CRITICAL; 96 result = STATE_CRITICAL;
104 else if (check_warning_time) 97 } else if (config.check_warning_time) {
105 result = STATE_WARNING; 98 result = STATE_WARNING;
106 else 99 } else {
107 result = STATE_UNKNOWN; 100 result = STATE_UNKNOWN;
108 die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"), server_address, server_port); 101 }
102 die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"), config.server_address, config.server_port);
109 } 103 }
110 } 104 }
111 105
112 /* watch for the connection string */ 106 /* watch for the connection string */
107 uint32_t raw_server_time;
113 result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0); 108 result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0);
114 109
115 /* close the connection */ 110 /* close the connection */
@@ -121,48 +116,56 @@ int main(int argc, char **argv) {
121 116
122 /* return a WARNING status if we couldn't read any data */ 117 /* return a WARNING status if we couldn't read any data */
123 if (result <= 0) { 118 if (result <= 0) {
124 if (check_critical_time) 119 if (config.check_critical_time) {
125 result = STATE_CRITICAL; 120 result = STATE_CRITICAL;
126 else if (check_warning_time) 121 } else if (config.check_warning_time) {
127 result = STATE_WARNING; 122 result = STATE_WARNING;
128 else 123 } else {
129 result = STATE_UNKNOWN; 124 result = STATE_UNKNOWN;
130 die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"), server_address, server_port); 125 }
126 die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"), config.server_address, config.server_port);
131 } 127 }
132 128
133 result = STATE_OK; 129 result = STATE_OK;
134 130
135 time_t conntime = (end_time - start_time); 131 time_t conntime = (end_time - start_time);
136 if (check_critical_time && conntime > critical_time) 132 if (config.check_critical_time && conntime > config.critical_time) {
137 result = STATE_CRITICAL; 133 result = STATE_CRITICAL;
138 else if (check_warning_time && conntime > warning_time) 134 } else if (config.check_warning_time && conntime > config.warning_time) {
139 result = STATE_WARNING; 135 result = STATE_WARNING;
136 }
140 137
141 if (result != STATE_OK) 138 if (result != STATE_OK) {
142 die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime, 139 die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime,
143 perfdata("time", (long)conntime, "s", check_warning_time, (long)warning_time, check_critical_time, (long)critical_time, true, 0, 140 perfdata("time", (long)conntime, "s", config.check_warning_time, (long)config.warning_time, config.check_critical_time,
144 false, 0)); 141 (long)config.critical_time, true, 0, false, 0));
142 }
145 143
144 unsigned long server_time;
145 unsigned long diff_time;
146 server_time = ntohl(raw_server_time) - UNIX_EPOCH; 146 server_time = ntohl(raw_server_time) - UNIX_EPOCH;
147 if (server_time > (unsigned long)end_time) 147 if (server_time > (unsigned long)end_time) {
148 diff_time = server_time - (unsigned long)end_time; 148 diff_time = server_time - (unsigned long)end_time;
149 else 149 } else {
150 diff_time = (unsigned long)end_time - server_time; 150 diff_time = (unsigned long)end_time - server_time;
151 }
151 152
152 if (check_critical_diff && diff_time > critical_diff) 153 if (config.check_critical_diff && diff_time > config.critical_diff) {
153 result = STATE_CRITICAL; 154 result = STATE_CRITICAL;
154 else if (check_warning_diff && diff_time > warning_diff) 155 } else if (config.check_warning_diff && diff_time > config.warning_diff) {
155 result = STATE_WARNING; 156 result = STATE_WARNING;
157 }
156 158
157 printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time, 159 printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time,
158 perfdata("time", (long)conntime, "s", check_warning_time, (long)warning_time, check_critical_time, (long)critical_time, true, 0, 160 perfdata("time", (long)conntime, "s", config.check_warning_time, (long)config.warning_time, config.check_critical_time,
159 false, 0), 161 (long)config.critical_time, true, 0, false, 0),
160 perfdata("offset", diff_time, "s", check_warning_diff, warning_diff, check_critical_diff, critical_diff, true, 0, false, 0)); 162 perfdata("offset", diff_time, "s", config.check_warning_diff, config.warning_diff, config.check_critical_diff,
163 config.critical_diff, true, 0, false, 0));
161 return result; 164 return result;
162} 165}
163 166
164/* process command-line arguments */ 167/* process command-line arguments */
165int process_arguments(int argc, char **argv) { 168check_time_config_wrapper process_arguments(int argc, char **argv) {
166 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 169 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
167 {"warning-variance", required_argument, 0, 'w'}, 170 {"warning-variance", required_argument, 0, 'w'},
168 {"critical-variance", required_argument, 0, 'c'}, 171 {"critical-variance", required_argument, 0, 'c'},
@@ -175,29 +178,37 @@ int process_arguments(int argc, char **argv) {
175 {"help", no_argument, 0, 'h'}, 178 {"help", no_argument, 0, 'h'},
176 {0, 0, 0, 0}}; 179 {0, 0, 0, 0}};
177 180
178 if (argc < 2) 181 if (argc < 2) {
179 usage("\n"); 182 usage("\n");
183 }
180 184
181 for (int i = 1; i < argc; i++) { 185 for (int i = 1; i < argc; i++) {
182 if (strcmp("-to", argv[i]) == 0) 186 if (strcmp("-to", argv[i]) == 0) {
183 strcpy(argv[i], "-t"); 187 strcpy(argv[i], "-t");
184 else if (strcmp("-wd", argv[i]) == 0) 188 } else if (strcmp("-wd", argv[i]) == 0) {
185 strcpy(argv[i], "-w"); 189 strcpy(argv[i], "-w");
186 else if (strcmp("-cd", argv[i]) == 0) 190 } else if (strcmp("-cd", argv[i]) == 0) {
187 strcpy(argv[i], "-c"); 191 strcpy(argv[i], "-c");
188 else if (strcmp("-wt", argv[i]) == 0) 192 } else if (strcmp("-wt", argv[i]) == 0) {
189 strcpy(argv[i], "-W"); 193 strcpy(argv[i], "-W");
190 else if (strcmp("-ct", argv[i]) == 0) 194 } else if (strcmp("-ct", argv[i]) == 0) {
191 strcpy(argv[i], "-C"); 195 strcpy(argv[i], "-C");
196 }
192 } 197 }
193 198
199 check_time_config_wrapper result = {
200 .errorcode = OK,
201 .config = check_time_config_init(),
202 };
203
194 int option_char; 204 int option_char;
195 while (true) { 205 while (true) {
196 int option = 0; 206 int option = 0;
197 option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option); 207 option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option);
198 208
199 if (option_char == -1 || option_char == EOF) 209 if (option_char == -1 || option_char == EOF) {
200 break; 210 break;
211 }
201 212
202 switch (option_char) { 213 switch (option_char) {
203 case '?': /* print short usage statement if args not parsable */ 214 case '?': /* print short usage statement if args not parsable */
@@ -209,18 +220,19 @@ int process_arguments(int argc, char **argv) {
209 print_revision(progname, NP_VERSION); 220 print_revision(progname, NP_VERSION);
210 exit(STATE_UNKNOWN); 221 exit(STATE_UNKNOWN);
211 case 'H': /* hostname */ 222 case 'H': /* hostname */
212 if (!is_host(optarg)) 223 if (!is_host(optarg)) {
213 usage2(_("Invalid hostname/address"), optarg); 224 usage2(_("Invalid hostname/address"), optarg);
214 server_address = optarg; 225 }
226 result.config.server_address = optarg;
215 break; 227 break;
216 case 'w': /* warning-variance */ 228 case 'w': /* warning-variance */
217 if (is_intnonneg(optarg)) { 229 if (is_intnonneg(optarg)) {
218 warning_diff = strtoul(optarg, NULL, 10); 230 result.config.warning_diff = strtoul(optarg, NULL, 10);
219 check_warning_diff = true; 231 result.config.check_warning_diff = true;
220 } else if (strspn(optarg, "0123456789:,") > 0) { 232 } else if (strspn(optarg, "0123456789:,") > 0) {
221 if (sscanf(optarg, "%lu%*[:,]%d", &warning_diff, &warning_time) == 2) { 233 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.warning_diff, &result.config.warning_time) == 2) {
222 check_warning_diff = true; 234 result.config.check_warning_diff = true;
223 check_warning_time = true; 235 result.config.check_warning_time = true;
224 } else { 236 } else {
225 usage4(_("Warning thresholds must be a positive integer")); 237 usage4(_("Warning thresholds must be a positive integer"));
226 } 238 }
@@ -230,12 +242,12 @@ int process_arguments(int argc, char **argv) {
230 break; 242 break;
231 case 'c': /* critical-variance */ 243 case 'c': /* critical-variance */
232 if (is_intnonneg(optarg)) { 244 if (is_intnonneg(optarg)) {
233 critical_diff = strtoul(optarg, NULL, 10); 245 result.config.critical_diff = strtoul(optarg, NULL, 10);
234 check_critical_diff = true; 246 result.config.check_critical_diff = true;
235 } else if (strspn(optarg, "0123456789:,") > 0) { 247 } else if (strspn(optarg, "0123456789:,") > 0) {
236 if (sscanf(optarg, "%lu%*[:,]%d", &critical_diff, &critical_time) == 2) { 248 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.critical_diff, &result.config.critical_time) == 2) {
237 check_critical_diff = true; 249 result.config.check_critical_diff = true;
238 check_critical_time = true; 250 result.config.check_critical_time = true;
239 } else { 251 } else {
240 usage4(_("Critical thresholds must be a positive integer")); 252 usage4(_("Critical thresholds must be a positive integer"));
241 } 253 }
@@ -244,48 +256,53 @@ int process_arguments(int argc, char **argv) {
244 } 256 }
245 break; 257 break;
246 case 'W': /* warning-connect */ 258 case 'W': /* warning-connect */
247 if (!is_intnonneg(optarg)) 259 if (!is_intnonneg(optarg)) {
248 usage4(_("Warning threshold must be a positive integer")); 260 usage4(_("Warning threshold must be a positive integer"));
249 else 261 } else {
250 warning_time = atoi(optarg); 262 result.config.warning_time = atoi(optarg);
251 check_warning_time = true; 263 }
264 result.config.check_warning_time = true;
252 break; 265 break;
253 case 'C': /* critical-connect */ 266 case 'C': /* critical-connect */
254 if (!is_intnonneg(optarg)) 267 if (!is_intnonneg(optarg)) {
255 usage4(_("Critical threshold must be a positive integer")); 268 usage4(_("Critical threshold must be a positive integer"));
256 else 269 } else {
257 critical_time = atoi(optarg); 270 result.config.critical_time = atoi(optarg);
258 check_critical_time = true; 271 }
272 result.config.check_critical_time = true;
259 break; 273 break;
260 case 'p': /* port */ 274 case 'p': /* port */
261 if (!is_intnonneg(optarg)) 275 if (!is_intnonneg(optarg)) {
262 usage4(_("Port must be a positive integer")); 276 usage4(_("Port must be a positive integer"));
263 else 277 } else {
264 server_port = atoi(optarg); 278 result.config.server_port = atoi(optarg);
279 }
265 break; 280 break;
266 case 't': /* timeout */ 281 case 't': /* timeout */
267 if (!is_intnonneg(optarg)) 282 if (!is_intnonneg(optarg)) {
268 usage2(_("Timeout interval must be a positive integer"), optarg); 283 usage2(_("Timeout interval must be a positive integer"), optarg);
269 else 284 } else {
270 socket_timeout = atoi(optarg); 285 socket_timeout = atoi(optarg);
286 }
271 break; 287 break;
272 case 'u': /* udp */ 288 case 'u': /* udp */
273 use_udp = true; 289 result.config.use_udp = true;
274 } 290 }
275 } 291 }
276 292
277 option_char = optind; 293 option_char = optind;
278 if (server_address == NULL) { 294 if (result.config.server_address == NULL) {
279 if (argc > option_char) { 295 if (argc > option_char) {
280 if (!is_host(argv[option_char])) 296 if (!is_host(argv[option_char])) {
281 usage2(_("Invalid hostname/address"), optarg); 297 usage2(_("Invalid hostname/address"), optarg);
282 server_address = argv[option_char]; 298 }
299 result.config.server_address = argv[option_char];
283 } else { 300 } else {
284 usage4(_("Hostname was not supplied")); 301 usage4(_("Hostname was not supplied"));
285 } 302 }
286 } 303 }
287 304
288 return OK; 305 return result;
289} 306}
290 307
291void print_help(void) { 308void print_help(void) {
diff --git a/plugins/check_time.d/config.h b/plugins/check_time.d/config.h
new file mode 100644
index 00000000..09bd7c45
--- /dev/null
+++ b/plugins/check_time.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 TIME_PORT = 37
8};
9
10typedef struct {
11 char *server_address;
12 int server_port;
13 bool use_udp;
14
15 int warning_time;
16 bool check_warning_time;
17 int critical_time;
18 bool check_critical_time;
19 unsigned long warning_diff;
20 bool check_warning_diff;
21 unsigned long critical_diff;
22 bool check_critical_diff;
23} check_time_config;
24
25check_time_config check_time_config_init() {
26 check_time_config tmp = {
27 .server_address = NULL,
28 .server_port = TIME_PORT,
29 .use_udp = false,
30
31 .warning_time = 0,
32 .check_warning_time = false,
33 .critical_time = 0,
34 .check_critical_time = false,
35
36 .warning_diff = 0,
37 .check_warning_diff = false,
38 .critical_diff = 0,
39 .check_critical_diff = false,
40 };
41 return tmp;
42}
diff --git a/plugins/check_ups.c b/plugins/check_ups.c
index 526a29df..ecc0760f 100644
--- a/plugins/check_ups.c
+++ b/plugins/check_ups.c
@@ -39,69 +39,29 @@ const char *email = "devel@monitoring-plugins.org";
39#include "common.h" 39#include "common.h"
40#include "netutils.h" 40#include "netutils.h"
41#include "utils.h" 41#include "utils.h"
42 42#include "check_ups.d/config.h"
43enum { 43#include "states.h"
44 PORT = 3493
45};
46
47#define UPS_NONE 0 /* no supported options */
48#define UPS_UTILITY 1 /* supports utility line */
49#define UPS_BATTPCT 2 /* supports percent battery remaining */
50#define UPS_STATUS 4 /* supports UPS status */
51#define UPS_TEMP 8 /* supports UPS temperature */
52#define UPS_LOADPCT 16 /* supports load percent */
53#define UPS_REALPOWER 32 /* supports real power */
54
55#define UPSSTATUS_NONE 0
56#define UPSSTATUS_OFF 1
57#define UPSSTATUS_OL 2
58#define UPSSTATUS_OB 4
59#define UPSSTATUS_LB 8
60#define UPSSTATUS_CAL 16
61#define UPSSTATUS_RB 32 /*Replace Battery */
62#define UPSSTATUS_BYPASS 64
63#define UPSSTATUS_OVER 128
64#define UPSSTATUS_TRIM 256
65#define UPSSTATUS_BOOST 512
66#define UPSSTATUS_CHRG 1024
67#define UPSSTATUS_DISCHRG 2048
68#define UPSSTATUS_UNKNOWN 4096
69#define UPSSTATUS_ALARM 8192
70 44
71enum { 45enum {
72 NOSUCHVAR = ERROR - 1 46 NOSUCHVAR = ERROR - 1
73}; 47};
74 48
75typedef struct ups_config {
76 unsigned int server_port;
77 char *server_address;
78 char *ups_name;
79 double warning_value;
80 double critical_value;
81 bool check_warn;
82 bool check_crit;
83 int check_variable;
84 int status;
85 bool temp_output_c;
86} ups_config;
87
88ups_config ups_config_init(void) {
89 ups_config tmp = {0};
90 tmp.server_port = PORT;
91 tmp.server_address = NULL;
92 tmp.ups_name = NULL;
93 tmp.check_variable = UPS_NONE;
94 tmp.status = UPSSTATUS_NONE;
95
96 return tmp;
97}
98
99// Forward declarations 49// Forward declarations
100static int determine_status(ups_config * /*config*/, int *supported_options); 50typedef struct {
101static int get_ups_variable(const char * /*varname*/, char * /*buf*/, ups_config config); 51 int errorcode;
52 int ups_status;
53 int supported_options;
54} determine_status_result;
55static determine_status_result determine_status(check_ups_config /*config*/);
56static int get_ups_variable(const char * /*varname*/, char * /*buf*/, check_ups_config config);
57
58typedef struct {
59 int errorcode;
60 check_ups_config config;
61} check_ups_config_wrapper;
62static check_ups_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
63static check_ups_config_wrapper validate_arguments(check_ups_config_wrapper /*config_wrapper*/);
102 64
103static int process_arguments(int /*argc*/, char ** /*argv*/, ups_config * /*config*/);
104static int validate_arguments(ups_config /*config*/);
105static void print_help(void); 65static void print_help(void);
106void print_usage(void); 66void print_usage(void);
107 67
@@ -109,28 +69,16 @@ int main(int argc, char **argv) {
109 setlocale(LC_ALL, ""); 69 setlocale(LC_ALL, "");
110 bindtextdomain(PACKAGE, LOCALEDIR); 70 bindtextdomain(PACKAGE, LOCALEDIR);
111 textdomain(PACKAGE); 71 textdomain(PACKAGE);
112
113 char *ups_status;
114 ups_status = strdup("N/A");
115
116 char *data;
117 data = strdup("");
118
119 char *message;
120 message = strdup("");
121
122 // Exit result
123 int result = STATE_UNKNOWN;
124
125 /* Parse extra opts if any */ 72 /* Parse extra opts if any */
126 argv = np_extra_opts(&argc, argv, progname); 73 argv = np_extra_opts(&argc, argv, progname);
127 74
128 // Config from commandline 75 check_ups_config_wrapper tmp_config = process_arguments(argc, argv);
129 ups_config config = ups_config_init();
130 76
131 if (process_arguments(argc, argv, &config) == ERROR) { 77 if (tmp_config.errorcode == ERROR) {
132 usage4(_("Could not parse arguments")); 78 usage4(_("Could not parse arguments"));
133 } 79 }
80 // Config from commandline
81 check_ups_config config = tmp_config.config;
134 82
135 /* initialize alarm signal handling */ 83 /* initialize alarm signal handling */
136 signal(SIGALRM, socket_timeout_alarm_handler); 84 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -138,71 +86,75 @@ int main(int argc, char **argv) {
138 /* set socket timeout */ 86 /* set socket timeout */
139 alarm(socket_timeout); 87 alarm(socket_timeout);
140 88
141 int supported_options = UPS_NONE;
142
143 /* get the ups status if possible */ 89 /* get the ups status if possible */
144 if (determine_status(&config, &supported_options) != OK) { 90 determine_status_result query_result = determine_status(config);
91 if (query_result.errorcode != OK) {
145 return STATE_CRITICAL; 92 return STATE_CRITICAL;
146 } 93 }
147 94
148 if (supported_options & UPS_STATUS) { 95 int ups_status_flags = query_result.ups_status;
96 int supported_options = query_result.supported_options;
149 97
150 ups_status = strdup(""); 98 // Exit result
99 mp_state_enum result = STATE_UNKNOWN;
100 char *message = NULL;
151 101
102 if (supported_options & UPS_STATUS) {
103 char *ups_status = strdup("");
152 result = STATE_OK; 104 result = STATE_OK;
153 105
154 if (config.status & UPSSTATUS_OFF) { 106 if (ups_status_flags & UPSSTATUS_OFF) {
155 xasprintf(&ups_status, "Off"); 107 xasprintf(&ups_status, "Off");
156 result = STATE_CRITICAL; 108 result = STATE_CRITICAL;
157 } else if ((config.status & (UPSSTATUS_OB | UPSSTATUS_LB)) == (UPSSTATUS_OB | UPSSTATUS_LB)) { 109 } else if ((ups_status_flags & (UPSSTATUS_OB | UPSSTATUS_LB)) == (UPSSTATUS_OB | UPSSTATUS_LB)) {
158 xasprintf(&ups_status, _("On Battery, Low Battery")); 110 xasprintf(&ups_status, _("On Battery, Low Battery"));
159 result = STATE_CRITICAL; 111 result = STATE_CRITICAL;
160 } else { 112 } else {
161 if (config.status & UPSSTATUS_OL) { 113 if (ups_status_flags & UPSSTATUS_OL) {
162 xasprintf(&ups_status, "%s%s", ups_status, _("Online")); 114 xasprintf(&ups_status, "%s%s", ups_status, _("Online"));
163 } 115 }
164 if (config.status & UPSSTATUS_OB) { 116 if (ups_status_flags & UPSSTATUS_OB) {
165 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery")); 117 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery"));
166 result = max_state(result, STATE_WARNING); 118 result = max_state(result, STATE_WARNING);
167 } 119 }
168 if (config.status & UPSSTATUS_LB) { 120 if (ups_status_flags & UPSSTATUS_LB) {
169 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery")); 121 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery"));
170 result = max_state(result, STATE_WARNING); 122 result = max_state(result, STATE_WARNING);
171 } 123 }
172 if (config.status & UPSSTATUS_CAL) { 124 if (ups_status_flags & UPSSTATUS_CAL) {
173 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating")); 125 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating"));
174 } 126 }
175 if (config.status & UPSSTATUS_RB) { 127 if (ups_status_flags & UPSSTATUS_RB) {
176 xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery")); 128 xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery"));
177 result = max_state(result, STATE_WARNING); 129 result = max_state(result, STATE_WARNING);
178 } 130 }
179 if (config.status & UPSSTATUS_BYPASS) { 131 if (ups_status_flags & UPSSTATUS_BYPASS) {
180 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass")); 132 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass"));
181 // Bypassing the battery is likely a bad thing 133 // Bypassing the battery is likely a bad thing
182 result = STATE_CRITICAL; 134 result = STATE_CRITICAL;
183 } 135 }
184 if (config.status & UPSSTATUS_OVER) { 136 if (ups_status_flags & UPSSTATUS_OVER) {
185 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload")); 137 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload"));
186 result = max_state(result, STATE_WARNING); 138 result = max_state(result, STATE_WARNING);
187 } 139 }
188 if (config.status & UPSSTATUS_TRIM) { 140 if (ups_status_flags & UPSSTATUS_TRIM) {
189 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming")); 141 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming"));
190 } 142 }
191 if (config.status & UPSSTATUS_BOOST) { 143 if (ups_status_flags & UPSSTATUS_BOOST) {
192 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting")); 144 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting"));
193 } 145 }
194 if (config.status & UPSSTATUS_CHRG) { 146 if (ups_status_flags & UPSSTATUS_CHRG) {
195 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging")); 147 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging"));
196 } 148 }
197 if (config.status & UPSSTATUS_DISCHRG) { 149 if (ups_status_flags & UPSSTATUS_DISCHRG) {
198 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging")); 150 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging"));
199 result = max_state(result, STATE_WARNING); 151 result = max_state(result, STATE_WARNING);
200 } 152 }
201 if (config.status & UPSSTATUS_ALARM) { 153 if (ups_status_flags & UPSSTATUS_ALARM) {
202 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM")); 154 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM"));
203 result = STATE_CRITICAL; 155 result = STATE_CRITICAL;
204 } 156 }
205 if (config.status & UPSSTATUS_UNKNOWN) { 157 if (ups_status_flags & UPSSTATUS_UNKNOWN) {
206 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown")); 158 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown"));
207 } 159 }
208 } 160 }
@@ -211,7 +163,7 @@ int main(int argc, char **argv) {
211 163
212 int res; 164 int res;
213 char temp_buffer[MAX_INPUT_BUFFER]; 165 char temp_buffer[MAX_INPUT_BUFFER];
214 166 char *performance_data = strdup("");
215 /* get the ups utility voltage if possible */ 167 /* get the ups utility voltage if possible */
216 res = get_ups_variable("input.voltage", temp_buffer, config); 168 res = get_ups_variable("input.voltage", temp_buffer, config);
217 if (res == NOSUCHVAR) { 169 if (res == NOSUCHVAR) {
@@ -239,11 +191,12 @@ int main(int argc, char **argv) {
239 } else if (config.check_warn && ups_utility_deviation >= config.warning_value) { 191 } else if (config.check_warn && ups_utility_deviation >= config.warning_value) {
240 result = max_state(result, STATE_WARNING); 192 result = max_state(result, STATE_WARNING);
241 } 193 }
242 xasprintf(&data, "%s", 194 xasprintf(&performance_data, "%s",
243 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", config.check_warn, (long)(1000 * config.warning_value), 195 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", config.check_warn, (long)(1000 * config.warning_value),
244 config.check_crit, (long)(1000 * config.critical_value), true, 0, false, 0)); 196 config.check_crit, (long)(1000 * config.critical_value), true, 0, false, 0));
245 } else { 197 } else {
246 xasprintf(&data, "%s", perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false, 0, true, 0, false, 0)); 198 xasprintf(&performance_data, "%s",
199 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false, 0, true, 0, false, 0));
247 } 200 }
248 } 201 }
249 202
@@ -266,11 +219,12 @@ int main(int argc, char **argv) {
266 } else if (config.check_warn && ups_battery_percent <= config.warning_value) { 219 } else if (config.check_warn && ups_battery_percent <= config.warning_value) {
267 result = max_state(result, STATE_WARNING); 220 result = max_state(result, STATE_WARNING);
268 } 221 }
269 xasprintf(&data, "%s %s", data, 222 xasprintf(&performance_data, "%s %s", performance_data,
270 perfdata("battery", (long)ups_battery_percent, "%", config.check_warn, (long)(config.warning_value), 223 perfdata("battery", (long)ups_battery_percent, "%", config.check_warn, (long)(config.warning_value),
271 config.check_crit, (long)(config.critical_value), true, 0, true, 100)); 224 config.check_crit, (long)(config.critical_value), true, 0, true, 100));
272 } else { 225 } else {
273 xasprintf(&data, "%s %s", data, perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true, 0, true, 100)); 226 xasprintf(&performance_data, "%s %s", performance_data,
227 perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true, 0, true, 100));
274 } 228 }
275 } 229 }
276 230
@@ -293,11 +247,12 @@ int main(int argc, char **argv) {
293 } else if (config.check_warn && ups_load_percent >= config.warning_value) { 247 } else if (config.check_warn && ups_load_percent >= config.warning_value) {
294 result = max_state(result, STATE_WARNING); 248 result = max_state(result, STATE_WARNING);
295 } 249 }
296 xasprintf(&data, "%s %s", data, 250 xasprintf(&performance_data, "%s %s", performance_data,
297 perfdata("load", (long)ups_load_percent, "%", config.check_warn, (long)(config.warning_value), config.check_crit, 251 perfdata("load", (long)ups_load_percent, "%", config.check_warn, (long)(config.warning_value), config.check_crit,
298 (long)(config.critical_value), true, 0, true, 100)); 252 (long)(config.critical_value), true, 0, true, 100));
299 } else { 253 } else {
300 xasprintf(&data, "%s %s", data, perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0, true, 100)); 254 xasprintf(&performance_data, "%s %s", performance_data,
255 perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0, true, 100));
301 } 256 }
302 } 257 }
303 258
@@ -329,11 +284,12 @@ int main(int argc, char **argv) {
329 } else if (config.check_warn && ups_temperature >= config.warning_value) { 284 } else if (config.check_warn && ups_temperature >= config.warning_value) {
330 result = max_state(result, STATE_WARNING); 285 result = max_state(result, STATE_WARNING);
331 } 286 }
332 xasprintf(&data, "%s %s", data, 287 xasprintf(&performance_data, "%s %s", performance_data,
333 perfdata("temp", (long)ups_temperature, tunits, config.check_warn, (long)(config.warning_value), config.check_crit, 288 perfdata("temp", (long)ups_temperature, tunits, config.check_warn, (long)(config.warning_value), config.check_crit,
334 (long)(config.critical_value), true, 0, false, 0)); 289 (long)(config.critical_value), true, 0, false, 0));
335 } else { 290 } else {
336 xasprintf(&data, "%s %s", data, perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0, false, 0)); 291 xasprintf(&performance_data, "%s %s", performance_data,
292 perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0, false, 0));
337 } 293 }
338 } 294 }
339 295
@@ -355,11 +311,12 @@ int main(int argc, char **argv) {
355 } else if (config.check_warn && ups_realpower >= config.warning_value) { 311 } else if (config.check_warn && ups_realpower >= config.warning_value) {
356 result = max_state(result, STATE_WARNING); 312 result = max_state(result, STATE_WARNING);
357 } 313 }
358 xasprintf(&data, "%s %s", data, 314 xasprintf(&performance_data, "%s %s", performance_data,
359 perfdata("realpower", (long)ups_realpower, "W", config.check_warn, (long)(config.warning_value), config.check_crit, 315 perfdata("realpower", (long)ups_realpower, "W", config.check_warn, (long)(config.warning_value), config.check_crit,
360 (long)(config.critical_value), true, 0, false, 0)); 316 (long)(config.critical_value), true, 0, false, 0));
361 } else { 317 } else {
362 xasprintf(&data, "%s %s", data, perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0, false, 0)); 318 xasprintf(&performance_data, "%s %s", performance_data,
319 perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0, false, 0));
363 } 320 }
364 } 321 }
365 322
@@ -373,66 +330,73 @@ int main(int argc, char **argv) {
373 /* reset timeout */ 330 /* reset timeout */
374 alarm(0); 331 alarm(0);
375 332
376 printf("UPS %s - %s|%s\n", state_text(result), message, data); 333 printf("UPS %s - %s|%s\n", state_text(result), message, performance_data);
377 return result; 334 exit(result);
378} 335}
379 336
380/* determines what options are supported by the UPS */ 337/* determines what options are supported by the UPS */
381int determine_status(ups_config *config, int *supported_options) { 338determine_status_result determine_status(const check_ups_config config) {
382 char recv_buffer[MAX_INPUT_BUFFER];
383 339
384 int res = get_ups_variable("ups.status", recv_buffer, *config); 340 determine_status_result result = {
341 .errorcode = OK,
342 .ups_status = UPSSTATUS_NONE,
343 .supported_options = 0,
344 };
345
346 char recv_buffer[MAX_INPUT_BUFFER];
347 int res = get_ups_variable("ups.status", recv_buffer, config);
385 if (res == NOSUCHVAR) { 348 if (res == NOSUCHVAR) {
386 return OK; 349 return result;
387 } 350 }
388 351
389 if (res != STATE_OK) { 352 if (res != STATE_OK) {
390 printf("%s\n", _("Invalid response received from host")); 353 printf("%s\n", _("Invalid response received from host"));
391 return ERROR; 354 result.errorcode = ERROR;
355 return result;
392 } 356 }
393 357
394 *supported_options |= UPS_STATUS; 358 result.supported_options |= UPS_STATUS;
395 359
396 char temp_buffer[MAX_INPUT_BUFFER]; 360 char temp_buffer[MAX_INPUT_BUFFER];
397 361
398 strcpy(temp_buffer, recv_buffer); 362 strcpy(temp_buffer, recv_buffer);
399 for (char *ptr = (char *)strtok(temp_buffer, " "); ptr != NULL; ptr = (char *)strtok(NULL, " ")) { 363 for (char *ptr = strtok(temp_buffer, " "); ptr != NULL; ptr = strtok(NULL, " ")) {
400 if (!strcmp(ptr, "OFF")) { 364 if (!strcmp(ptr, "OFF")) {
401 config->status |= UPSSTATUS_OFF; 365 result.ups_status |= UPSSTATUS_OFF;
402 } else if (!strcmp(ptr, "OL")) { 366 } else if (!strcmp(ptr, "OL")) {
403 config->status |= UPSSTATUS_OL; 367 result.ups_status |= UPSSTATUS_OL;
404 } else if (!strcmp(ptr, "OB")) { 368 } else if (!strcmp(ptr, "OB")) {
405 config->status |= UPSSTATUS_OB; 369 result.ups_status |= UPSSTATUS_OB;
406 } else if (!strcmp(ptr, "LB")) { 370 } else if (!strcmp(ptr, "LB")) {
407 config->status |= UPSSTATUS_LB; 371 result.ups_status |= UPSSTATUS_LB;
408 } else if (!strcmp(ptr, "CAL")) { 372 } else if (!strcmp(ptr, "CAL")) {
409 config->status |= UPSSTATUS_CAL; 373 result.ups_status |= UPSSTATUS_CAL;
410 } else if (!strcmp(ptr, "RB")) { 374 } else if (!strcmp(ptr, "RB")) {
411 config->status |= UPSSTATUS_RB; 375 result.ups_status |= UPSSTATUS_RB;
412 } else if (!strcmp(ptr, "BYPASS")) { 376 } else if (!strcmp(ptr, "BYPASS")) {
413 config->status |= UPSSTATUS_BYPASS; 377 result.ups_status |= UPSSTATUS_BYPASS;
414 } else if (!strcmp(ptr, "OVER")) { 378 } else if (!strcmp(ptr, "OVER")) {
415 config->status |= UPSSTATUS_OVER; 379 result.ups_status |= UPSSTATUS_OVER;
416 } else if (!strcmp(ptr, "TRIM")) { 380 } else if (!strcmp(ptr, "TRIM")) {
417 config->status |= UPSSTATUS_TRIM; 381 result.ups_status |= UPSSTATUS_TRIM;
418 } else if (!strcmp(ptr, "BOOST")) { 382 } else if (!strcmp(ptr, "BOOST")) {
419 config->status |= UPSSTATUS_BOOST; 383 result.ups_status |= UPSSTATUS_BOOST;
420 } else if (!strcmp(ptr, "CHRG")) { 384 } else if (!strcmp(ptr, "CHRG")) {
421 config->status |= UPSSTATUS_CHRG; 385 result.ups_status |= UPSSTATUS_CHRG;
422 } else if (!strcmp(ptr, "DISCHRG")) { 386 } else if (!strcmp(ptr, "DISCHRG")) {
423 config->status |= UPSSTATUS_DISCHRG; 387 result.ups_status |= UPSSTATUS_DISCHRG;
424 } else if (!strcmp(ptr, "ALARM")) { 388 } else if (!strcmp(ptr, "ALARM")) {
425 config->status |= UPSSTATUS_ALARM; 389 result.ups_status |= UPSSTATUS_ALARM;
426 } else { 390 } else {
427 config->status |= UPSSTATUS_UNKNOWN; 391 result.ups_status |= UPSSTATUS_UNKNOWN;
428 } 392 }
429 } 393 }
430 394
431 return OK; 395 return result;
432} 396}
433 397
434/* gets a variable value for a specific UPS */ 398/* gets a variable value for a specific UPS */
435int get_ups_variable(const char *varname, char *buf, const ups_config config) { 399int get_ups_variable(const char *varname, char *buf, const check_ups_config config) {
436 char send_buffer[MAX_INPUT_BUFFER]; 400 char send_buffer[MAX_INPUT_BUFFER];
437 401
438 /* create the command string to send to the UPS daemon */ 402 /* create the command string to send to the UPS daemon */
@@ -500,7 +464,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
500 [-wv warn_value] [-cv crit_value] [-to to_sec] */ 464 [-wv warn_value] [-cv crit_value] [-to to_sec] */
501 465
502/* process command-line arguments */ 466/* process command-line arguments */
503int process_arguments(int argc, char **argv, ups_config *config) { 467check_ups_config_wrapper process_arguments(int argc, char **argv) {
504 468
505 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 469 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
506 {"ups", required_argument, 0, 'u'}, 470 {"ups", required_argument, 0, 'u'},
@@ -514,8 +478,14 @@ int process_arguments(int argc, char **argv, ups_config *config) {
514 {"help", no_argument, 0, 'h'}, 478 {"help", no_argument, 0, 'h'},
515 {0, 0, 0, 0}}; 479 {0, 0, 0, 0}};
516 480
481 check_ups_config_wrapper result = {
482 .errorcode = OK,
483 .config = check_ups_config_init(),
484 };
485
517 if (argc < 2) { 486 if (argc < 2) {
518 return ERROR; 487 result.errorcode = ERROR;
488 return result;
519 } 489 }
520 490
521 int c; 491 int c;
@@ -542,52 +512,52 @@ int process_arguments(int argc, char **argv, ups_config *config) {
542 usage5(); 512 usage5();
543 case 'H': /* hostname */ 513 case 'H': /* hostname */
544 if (is_host(optarg)) { 514 if (is_host(optarg)) {
545 config->server_address = optarg; 515 result.config.server_address = optarg;
546 } else { 516 } else {
547 usage2(_("Invalid hostname/address"), optarg); 517 usage2(_("Invalid hostname/address"), optarg);
548 } 518 }
549 break; 519 break;
550 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for 520 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for
551 Fahrenheit) */ 521 Fahrenheit) */
552 config->temp_output_c = true; 522 result.config.temp_output_c = true;
553 break; 523 break;
554 case 'u': /* ups name */ 524 case 'u': /* ups name */
555 config->ups_name = optarg; 525 result.config.ups_name = optarg;
556 break; 526 break;
557 case 'p': /* port */ 527 case 'p': /* port */
558 if (is_intpos(optarg)) { 528 if (is_intpos(optarg)) {
559 config->server_port = atoi(optarg); 529 result.config.server_port = atoi(optarg);
560 } else { 530 } else {
561 usage2(_("Port must be a positive integer"), optarg); 531 usage2(_("Port must be a positive integer"), optarg);
562 } 532 }
563 break; 533 break;
564 case 'c': /* critical time threshold */ 534 case 'c': /* critical time threshold */
565 if (is_intnonneg(optarg)) { 535 if (is_intnonneg(optarg)) {
566 config->critical_value = atoi(optarg); 536 result.config.critical_value = atoi(optarg);
567 config->check_crit = true; 537 result.config.check_crit = true;
568 } else { 538 } else {
569 usage2(_("Critical time must be a positive integer"), optarg); 539 usage2(_("Critical time must be a positive integer"), optarg);
570 } 540 }
571 break; 541 break;
572 case 'w': /* warning time threshold */ 542 case 'w': /* warning time threshold */
573 if (is_intnonneg(optarg)) { 543 if (is_intnonneg(optarg)) {
574 config->warning_value = atoi(optarg); 544 result.config.warning_value = atoi(optarg);
575 config->check_warn = true; 545 result.config.check_warn = true;
576 } else { 546 } else {
577 usage2(_("Warning time must be a positive integer"), optarg); 547 usage2(_("Warning time must be a positive integer"), optarg);
578 } 548 }
579 break; 549 break;
580 case 'v': /* variable */ 550 case 'v': /* variable */
581 if (!strcmp(optarg, "LINE")) { 551 if (!strcmp(optarg, "LINE")) {
582 config->check_variable = UPS_UTILITY; 552 result.config.check_variable = UPS_UTILITY;
583 } else if (!strcmp(optarg, "TEMP")) { 553 } else if (!strcmp(optarg, "TEMP")) {
584 config->check_variable = UPS_TEMP; 554 result.config.check_variable = UPS_TEMP;
585 } else if (!strcmp(optarg, "BATTPCT")) { 555 } else if (!strcmp(optarg, "BATTPCT")) {
586 config->check_variable = UPS_BATTPCT; 556 result.config.check_variable = UPS_BATTPCT;
587 } else if (!strcmp(optarg, "LOADPCT")) { 557 } else if (!strcmp(optarg, "LOADPCT")) {
588 config->check_variable = UPS_LOADPCT; 558 result.config.check_variable = UPS_LOADPCT;
589 } else if (!strcmp(optarg, "REALPOWER")) { 559 } else if (!strcmp(optarg, "REALPOWER")) {
590 config->check_variable = UPS_REALPOWER; 560 result.config.check_variable = UPS_REALPOWER;
591 } else { 561 } else {
592 usage2(_("Unrecognized UPS variable"), optarg); 562 usage2(_("Unrecognized UPS variable"), optarg);
593 } 563 }
@@ -608,27 +578,27 @@ int process_arguments(int argc, char **argv, ups_config *config) {
608 } 578 }
609 } 579 }
610 580
611 if (config->server_address == NULL && argc > optind) { 581 if (result.config.server_address == NULL && argc > optind) {
612 if (is_host(argv[optind])) { 582 if (is_host(argv[optind])) {
613 config->server_address = argv[optind++]; 583 result.config.server_address = argv[optind++];
614 } else { 584 } else {
615 usage2(_("Invalid hostname/address"), optarg); 585 usage2(_("Invalid hostname/address"), optarg);
616 } 586 }
617 } 587 }
618 588
619 if (config->server_address == NULL) { 589 if (result.config.server_address == NULL) {
620 config->server_address = strdup("127.0.0.1"); 590 result.config.server_address = strdup("127.0.0.1");
621 } 591 }
622 592
623 return validate_arguments(*config); 593 return validate_arguments(result);
624} 594}
625 595
626int validate_arguments(ups_config config) { 596check_ups_config_wrapper validate_arguments(check_ups_config_wrapper config_wrapper) {
627 if (!config.ups_name) { 597 if (config_wrapper.config.ups_name) {
628 printf("%s\n", _("Error : no UPS indicated")); 598 printf("%s\n", _("Error : no UPS indicated"));
629 return ERROR; 599 config_wrapper.errorcode = ERROR;
630 } 600 }
631 return OK; 601 return config_wrapper;
632} 602}
633 603
634void print_help(void) { 604void print_help(void) {
diff --git a/plugins/check_ups.d/config.h b/plugins/check_ups.d/config.h
new file mode 100644
index 00000000..353104f2
--- /dev/null
+++ b/plugins/check_ups.d/config.h
@@ -0,0 +1,55 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6#define UPS_NONE 0 /* no supported options */
7#define UPS_UTILITY 1 /* supports utility line */
8#define UPS_BATTPCT 2 /* supports percent battery remaining */
9#define UPS_STATUS 4 /* supports UPS status */
10#define UPS_TEMP 8 /* supports UPS temperature */
11#define UPS_LOADPCT 16 /* supports load percent */
12#define UPS_REALPOWER 32 /* supports real power */
13
14#define UPSSTATUS_NONE 0
15#define UPSSTATUS_OFF 1
16#define UPSSTATUS_OL 2
17#define UPSSTATUS_OB 4
18#define UPSSTATUS_LB 8
19#define UPSSTATUS_CAL 16
20#define UPSSTATUS_RB 32 /*Replace Battery */
21#define UPSSTATUS_BYPASS 64
22#define UPSSTATUS_OVER 128
23#define UPSSTATUS_TRIM 256
24#define UPSSTATUS_BOOST 512
25#define UPSSTATUS_CHRG 1024
26#define UPSSTATUS_DISCHRG 2048
27#define UPSSTATUS_UNKNOWN 4096
28#define UPSSTATUS_ALARM 8192
29
30enum {
31 PORT = 3493
32};
33
34typedef struct ups_config {
35 unsigned int server_port;
36 char *server_address;
37 char *ups_name;
38 double warning_value;
39 double critical_value;
40 bool check_warn;
41 bool check_crit;
42 int check_variable;
43 bool temp_output_c;
44} check_ups_config;
45
46check_ups_config check_ups_config_init(void) {
47 check_ups_config tmp = {0};
48 tmp.server_port = PORT;
49 tmp.server_address = NULL;
50 tmp.ups_name = NULL;
51 tmp.check_variable = UPS_NONE;
52
53 return tmp;
54}
55
diff --git a/plugins/common.h b/plugins/common.h
index 603bae55..35d1e549 100644
--- a/plugins/common.h
+++ b/plugins/common.h
@@ -31,7 +31,7 @@
31#ifndef _COMMON_H_ 31#ifndef _COMMON_H_
32#define _COMMON_H_ 32#define _COMMON_H_
33 33
34#include "config.h" 34#include "../config.h"
35#include "../lib/monitoringplug.h" 35#include "../lib/monitoringplug.h"
36 36
37#ifdef HAVE_FEATURES_H 37#ifdef HAVE_FEATURES_H
@@ -110,7 +110,7 @@
110 110
111/* GNU Libraries */ 111/* GNU Libraries */
112#include <getopt.h> 112#include <getopt.h>
113#include "dirname.h" 113#include "../gl/dirname.h"
114 114
115#include <locale.h> 115#include <locale.h>
116 116
@@ -190,7 +190,7 @@ enum {
190 * Internationalization 190 * Internationalization
191 * 191 *
192 */ 192 */
193#include "gettext.h" 193#include "../gl/gettext.h"
194#define _(String) gettext (String) 194#define _(String) gettext (String)
195#if ! ENABLE_NLS 195#if ! ENABLE_NLS
196# undef textdomain 196# undef textdomain
diff --git a/plugins/negate.c b/plugins/negate.c
index 750c0bfb..0520d298 100644
--- a/plugins/negate.c
+++ b/plugins/negate.c
@@ -38,21 +38,18 @@ const char *email = "devel@monitoring-plugins.org";
38#include "common.h" 38#include "common.h"
39#include "utils.h" 39#include "utils.h"
40#include "utils_cmd.h" 40#include "utils_cmd.h"
41#include "negate.d/config.h"
42#include "../lib/states.h"
41 43
42#include <ctype.h> 44typedef struct {
45 int errorcode;
46 negate_config config;
47} negate_config_wrapper;
48static negate_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
49static negate_config_wrapper validate_arguments(negate_config_wrapper /*config_wrapper*/);
43 50
44static const char **process_arguments(int /*argc*/, char ** /*argv*/);
45static void validate_arguments(char ** /*command_line*/);
46static void print_help(void); 51static void print_help(void);
47void print_usage(void); 52void print_usage(void);
48static bool subst_text = false;
49
50static int state[4] = {
51 STATE_OK,
52 STATE_WARNING,
53 STATE_CRITICAL,
54 STATE_UNKNOWN,
55};
56 53
57int main(int argc, char **argv) { 54int main(int argc, char **argv) {
58 setlocale(LC_ALL, ""); 55 setlocale(LC_ALL, "");
@@ -61,15 +58,24 @@ int main(int argc, char **argv) {
61 58
62 timeout_interval = DEFAULT_TIMEOUT; 59 timeout_interval = DEFAULT_TIMEOUT;
63 60
64 char **command_line = (char **)process_arguments(argc, argv); 61 negate_config_wrapper tmp_config = process_arguments(argc, argv);
62
63 if (tmp_config.errorcode == ERROR) {
64 die(STATE_UNKNOWN, _("negate: Failed to parse input"));
65 }
66
67 negate_config config = tmp_config.config;
68
69 char **command_line = config.command_line;
65 70
66 /* Set signal handling and alarm */ 71 /* Set signal handling and alarm */
67 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) 72 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
68 die(STATE_UNKNOWN, _("Cannot catch SIGALRM")); 73 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
74 }
69 75
70 (void)alarm((unsigned)timeout_interval); 76 (void)alarm(timeout_interval);
71 77
72 int result = STATE_UNKNOWN; 78 mp_state_enum result = STATE_UNKNOWN;
73 output chld_out; 79 output chld_out;
74 output chld_err; 80 output chld_err;
75 81
@@ -86,46 +92,52 @@ int main(int argc, char **argv) {
86 } 92 }
87 93
88 /* Return UNKNOWN or worse if no output is returned */ 94 /* Return UNKNOWN or worse if no output is returned */
89 if (chld_out.lines == 0) 95 if (chld_out.lines == 0) {
90 die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n")); 96 die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n"));
97 }
91 98
92 char *sub; 99 char *sub;
93 for (size_t i = 0; i < chld_out.lines; i++) { 100 for (size_t i = 0; i < chld_out.lines; i++) {
94 if (subst_text && result >= 0 && result <= 4 && result != state[result]) { 101 if (config.subst_text && result >= 0 && result <= 4 && result != config.state[result]) {
95 /* Loop over each match found */ 102 /* Loop over each match found */
96 while ((sub = strstr(chld_out.line[i], state_text(result)))) { 103 while ((sub = strstr(chld_out.line[i], state_text(result)))) {
97 /* Terminate the first part and skip over the string we'll substitute */ 104 /* Terminate the first part and skip over the string we'll substitute */
98 *sub = '\0'; 105 *sub = '\0';
99 sub += strlen(state_text(result)); 106 sub += strlen(state_text(result));
100 /* then put everything back together */ 107 /* then put everything back together */
101 xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i], state_text(state[result]), sub); 108 xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i], state_text(config.state[result]), sub);
102 } 109 }
103 } 110 }
104 printf("%s\n", chld_out.line[i]); 111 printf("%s\n", chld_out.line[i]);
105 } 112 }
106 113
107 if (result >= 0 && result <= 4) { 114 if (result >= 0 && result <= 4) {
108 exit(state[result]); 115 exit(config.state[result]);
109 } else { 116 } else {
110 exit(result); 117 exit(result);
111 } 118 }
112} 119}
113 120
114/* process command-line arguments */ 121/* process command-line arguments */
115static const char **process_arguments(int argc, char **argv) { 122static negate_config_wrapper process_arguments(int argc, char **argv) {
116 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, 123 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'},
117 {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'}, 124 {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'},
118 {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'}, 125 {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'},
119 {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'}, 126 {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'},
120 {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}}; 127 {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}};
121 128
129 negate_config_wrapper result = {
130 .errorcode = OK,
131 .config = negate_config_init(),
132 };
122 bool permute = true; 133 bool permute = true;
123 while (true) { 134 while (true) {
124 int option = 0; 135 int option = 0;
125 int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option); 136 int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option);
126 137
127 if (option_char == -1 || option_char == EOF) 138 if (option_char == -1 || option_char == EOF) {
128 break; 139 break;
140 }
129 141
130 switch (option_char) { 142 switch (option_char) {
131 case '?': /* help */ 143 case '?': /* help */
@@ -139,58 +151,68 @@ static const char **process_arguments(int argc, char **argv) {
139 print_revision(progname, NP_VERSION); 151 print_revision(progname, NP_VERSION);
140 exit(STATE_UNKNOWN); 152 exit(STATE_UNKNOWN);
141 case 't': /* timeout period */ 153 case 't': /* timeout period */
142 if (!is_integer(optarg)) 154 if (!is_integer(optarg)) {
143 usage2(_("Timeout interval must be a positive integer"), optarg); 155 usage2(_("Timeout interval must be a positive integer"), optarg);
144 else 156 } else {
145 timeout_interval = atoi(optarg); 157 timeout_interval = atoi(optarg);
158 }
146 break; 159 break;
147 case 'T': /* Result to return on timeouts */ 160 case 'T': /* Result to return on timeouts */
148 if ((timeout_state = mp_translate_state(optarg)) == ERROR) 161 if ((timeout_state = mp_translate_state(optarg)) == ERROR) {
149 usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 162 usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
163 }
150 break; 164 break;
151 case 'o': /* replacement for OK */ 165 case 'o': /* replacement for OK */
152 if ((state[STATE_OK] = mp_translate_state(optarg)) == ERROR) 166 if ((result.config.state[STATE_OK] = mp_translate_state(optarg)) == ERROR) {
153 usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 167 usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
168 }
154 permute = false; 169 permute = false;
155 break; 170 break;
156 171
157 case 'w': /* replacement for WARNING */ 172 case 'w': /* replacement for WARNING */
158 if ((state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) 173 if ((result.config.state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) {
159 usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 174 usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
175 }
160 permute = false; 176 permute = false;
161 break; 177 break;
162 case 'c': /* replacement for CRITICAL */ 178 case 'c': /* replacement for CRITICAL */
163 if ((state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) 179 if ((result.config.state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) {
164 usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 180 usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
181 }
165 permute = false; 182 permute = false;
166 break; 183 break;
167 case 'u': /* replacement for UNKNOWN */ 184 case 'u': /* replacement for UNKNOWN */
168 if ((state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) 185 if ((result.config.state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) {
169 usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 186 usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
187 }
170 permute = false; 188 permute = false;
171 break; 189 break;
172 case 's': /* Substitute status text */ 190 case 's': /* Substitute status text */
173 subst_text = true; 191 result.config.subst_text = true;
174 break; 192 break;
175 } 193 }
176 } 194 }
177 195
178 validate_arguments(&argv[optind]);
179
180 if (permute) { /* No [owcu] switch specified, default to this */ 196 if (permute) { /* No [owcu] switch specified, default to this */
181 state[STATE_OK] = STATE_CRITICAL; 197 result.config.state[STATE_OK] = STATE_CRITICAL;
182 state[STATE_CRITICAL] = STATE_OK; 198 result.config.state[STATE_CRITICAL] = STATE_OK;
183 } 199 }
184 200
185 return (const char **)&argv[optind]; 201 result.config.command_line = &argv[optind];
202
203 return validate_arguments(result);
186} 204}
187 205
188void validate_arguments(char **command_line) { 206negate_config_wrapper validate_arguments(negate_config_wrapper config_wrapper) {
189 if (command_line[0] == NULL) 207 if (config_wrapper.config.command_line[0] == NULL) {
190 usage4(_("Could not parse arguments")); 208 usage4(_("Could not parse arguments"));
209 }
191 210
192 if (strncmp(command_line[0], "/", 1) != 0 && strncmp(command_line[0], "./", 2) != 0) 211 if (strncmp(config_wrapper.config.command_line[0], "/", 1) != 0 && strncmp(config_wrapper.config.command_line[0], "./", 2) != 0) {
193 usage4(_("Require path to command")); 212 usage4(_("Require path to command"));
213 }
214
215 return config_wrapper;
194} 216}
195 217
196void print_help(void) { 218void print_help(void) {
diff --git a/plugins/negate.d/config.h b/plugins/negate.d/config.h
new file mode 100644
index 00000000..0cf30cd4
--- /dev/null
+++ b/plugins/negate.d/config.h
@@ -0,0 +1,24 @@
1#pragma once
2
3#include "states.h"
4
5typedef struct {
6 mp_state_enum state[4];
7 bool subst_text;
8 char **command_line;
9} negate_config;
10
11negate_config negate_config_init() {
12 negate_config tmp = {
13 .state =
14 {
15 STATE_OK,
16 STATE_WARNING,
17 STATE_CRITICAL,
18 STATE_UNKNOWN,
19 },
20 .subst_text = false,
21 .command_line = NULL,
22 };
23 return tmp;
24}
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index 719de575..96740b3a 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -201,7 +201,7 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int
201 time_t tm_t; 201 time_t tm_t;
202 202
203 if (!certificate) { 203 if (!certificate) {
204 printf("%s\n", _("CRITICAL - Cannot retrieve server certificate.")); 204 printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
205 return STATE_CRITICAL; 205 return STATE_CRITICAL;
206 } 206 }
207 207
diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t
index 9eb77ce4..0f62fb2b 100644
--- a/plugins/t/check_disk.t
+++ b/plugins/t/check_disk.t
@@ -10,6 +10,7 @@ use strict;
10use Test::More; 10use Test::More;
11use NPTest; 11use NPTest;
12use POSIX qw(ceil floor); 12use POSIX qw(ceil floor);
13use Data::Dumper;
13 14
14my $successOutput = '/^DISK OK/'; 15my $successOutput = '/^DISK OK/';
15my $failureOutput = '/^DISK CRITICAL/'; 16my $failureOutput = '/^DISK CRITICAL/';
@@ -20,173 +21,216 @@ my $result;
20my $mountpoint_valid = getTestParameter( "NP_MOUNTPOINT_VALID", "Path to valid mountpoint", "/"); 21my $mountpoint_valid = getTestParameter( "NP_MOUNTPOINT_VALID", "Path to valid mountpoint", "/");
21my $mountpoint2_valid = getTestParameter( "NP_MOUNTPOINT2_VALID", "Path to another valid mountpoint. Must be different from 1st one", "/var"); 22my $mountpoint2_valid = getTestParameter( "NP_MOUNTPOINT2_VALID", "Path to another valid mountpoint. Must be different from 1st one", "/var");
22 23
24my $output_format = "--output-format mp-test-json";
25
23if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") { 26if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") {
24 plan skip_all => "Need 2 mountpoints to test"; 27 plan skip_all => "Need 2 mountpoints to test";
25} else { 28} else {
26 plan tests => 94; 29 plan tests => 97;
27} 30}
28 31
29$result = NPTest->testCmd( 32$result = NPTest->testCmd(
30 "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -p $mountpoint2_valid" 33 "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -P -p $mountpoint2_valid $output_format"
31 ); 34 );
32cmp_ok( $result->return_code, "==", 0, "Checking two mountpoints (must have at least 1% free in space and inodes)"); 35cmp_ok( $result->return_code, "==", 0, "Checking two mountpoints (must have at least 1% free in space and inodes)");
33my $c = 0;
34$_ = $result->output;
35$c++ while /\(/g; # counts number of "(" - should be two
36cmp_ok( $c, '==', 2, "Got two mountpoints in output");
37 36
37like($result->{'mp_test_result'}->{'state'}, "/OK/", "Main result is OK");
38like($result->{'mp_test_result'}->{'checks'}->[0]->{'state'}, "/OK/", "First sub result is OK");
39like($result->{'mp_test_result'}->{'checks'}->[1]->{'state'}, "/OK/", "Second sub result is OK");
40
41my $absolut_space_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'};
42# print("absolute space on mp1: ". $absolut_space_mp1 . "\n");
43
44my $free_percent_on_mp1 = ($result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / ($absolut_space_mp1/100));
45print("free percent on mp1: ". $free_percent_on_mp1 . "\n");
46
47my $absolut_space_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'};
48# print("absolute space on mp2: ". $absolut_space_mp2 . "\n");
49
50my $free_percent_on_mp2 = ($result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ ($absolut_space_mp2/100));
51print("free percent on mp2: ". $free_percent_on_mp2 . "\n");
38 52
39# Get perf data 53my @perfdata;
40# Should use Monitoring::Plugin 54@perfdata[0] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0];
41my @perf_data = sort(split(/ /, $result->perf_output)); 55@perfdata[1] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0];
42 56
57# Decrease precision of numbers since the the fs might be modified between the two runs
58$perfdata[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000);
59$perfdata[1]->{'value'}->{'value'} = int($perfdata[1]->{'value'}->{'value'} / 1000000);
43 60
44# Calculate avg_free free on mountpoint1 and mountpoint2 61# Calculate avg_free free on mountpoint1 and mountpoint2
45# because if you check in the middle, you should get different errors 62# because if you check in the middle, you should get different errors
46$_ = $result->output; 63my $avg_free_percent = ceil(($free_percent_on_mp1+$free_percent_on_mp2)/2);
47my ($free_on_mp1, $free_on_mp2) = (m/\((\d+\.\d+)%.*\((\d+\.\d+)%/); 64# print("avg_free: " . $avg_free_percent . "\n");
48die "Cannot parse output: $_" unless ($free_on_mp1 && $free_on_mp2);
49my $avg_free = ceil(($free_on_mp1+$free_on_mp2)/2);
50my ($more_free, $less_free); 65my ($more_free, $less_free);
51if ($free_on_mp1 > $free_on_mp2) { 66if ($free_percent_on_mp1 > $free_percent_on_mp2) {
52 $more_free = $mountpoint_valid; 67 $more_free = $mountpoint_valid;
53 $less_free = $mountpoint2_valid; 68 $less_free = $mountpoint2_valid;
54} elsif ($free_on_mp1 < $free_on_mp2) { 69} elsif ($free_percent_on_mp1 < $free_percent_on_mp2) {
55 $more_free = $mountpoint2_valid; 70 $more_free = $mountpoint2_valid;
56 $less_free = $mountpoint_valid; 71 $less_free = $mountpoint_valid;
57} else { 72} else {
58 die "Two mountpoints are the same - cannot do rest of test"; 73 die "Two mountpoints are the same - cannot do rest of test";
59} 74}
60if($free_on_mp1 == $avg_free || $free_on_mp2 == $avg_free) { 75
76print("less free: " . $less_free . "\n");
77print("more free: " . $more_free . "\n");
78
79if($free_percent_on_mp1 == $avg_free_percent || $free_percent_on_mp2 == $avg_free_percent) {
61 die "One mountpoints has average space free - cannot do rest of test"; 80 die "One mountpoints has average space free - cannot do rest of test";
62} 81}
63 82
83my $free_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'};
84my $total_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'};
85my $free_inode_percentage_on_mp1 = $free_inodes_on_mp1 / ($total_inodes_on_mp1 / 100);
64 86
65# Do same for inodes 87my $free_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'};
66$_ = $result->output; 88my $total_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'};
67my ($free_inode_on_mp1, $free_inode_on_mp2) = (m/inode=(\d+)%.*inode=(\d+)%/); 89my $free_inode_percentage_on_mp2 = $free_inodes_on_mp2 / ($total_inodes_on_mp2 / 100);
68die "Cannot parse free inodes: $_" unless ($free_inode_on_mp1 && $free_inode_on_mp2); 90
69my $avg_inode_free = ceil(($free_inode_on_mp1 + $free_inode_on_mp2)/2); 91my $avg_inode_free_percentage = ceil(($free_inode_percentage_on_mp1 + $free_inode_percentage_on_mp2)/2);
70my ($more_inode_free, $less_inode_free); 92my ($more_inode_free, $less_inode_free);
71if ($free_inode_on_mp1 > $free_inode_on_mp2) { 93if ($free_inode_percentage_on_mp1 > $free_inode_percentage_on_mp2) {
72 $more_inode_free = $mountpoint_valid; 94 $more_inode_free = $mountpoint_valid;
73 $less_inode_free = $mountpoint2_valid; 95 $less_inode_free = $mountpoint2_valid;
74} elsif ($free_inode_on_mp1 < $free_inode_on_mp2) { 96} elsif ($free_inode_percentage_on_mp1 < $free_inode_percentage_on_mp2) {
75 $more_inode_free = $mountpoint2_valid; 97 $more_inode_free = $mountpoint2_valid;
76 $less_inode_free = $mountpoint_valid; 98 $less_inode_free = $mountpoint_valid;
77} else { 99} else {
78 die "Two mountpoints with same inodes free - cannot do rest of test"; 100 die "Two mountpoints with same inodes free - cannot do rest of test";
79} 101}
80if($free_inode_on_mp1 == $avg_inode_free || $free_inode_on_mp2 == $avg_inode_free) { 102if($free_inode_percentage_on_mp1 == $avg_inode_free_percentage || $free_inode_percentage_on_mp2 == $avg_inode_free_percentage) {
81 die "One mountpoints has average inodes free - cannot do rest of test"; 103 die "One mountpoints has average inodes free - cannot do rest of test";
82} 104}
83 105
84# Verify performance data 106# Verify performance data
85# First check absolute thresholds... 107# First check absolute thresholds...
86$result = NPTest->testCmd( 108$result = NPTest->testCmd(
87 "./check_disk -w 20 -c 10 -p $mountpoint_valid" 109 "./check_disk -w 20 -c 10 -p $mountpoint_valid $output_format"
88 ); 110 );
89$_ = $result->perf_output; 111
90my ($warn_absth_data, $crit_absth_data, $total_absth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); 112cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
91# default unit is MiB, but perfdata is always bytes 113
92is ($warn_absth_data, $total_absth_data - (20 * (2 ** 20)), "Wrong warning in perf data using absolute thresholds"); 114my $warn_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'};
93is ($crit_absth_data, $total_absth_data - (10 * (2 ** 20)), "Wrong critical in perf data using absolute thresholds"); 115my $crit_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'};
116my $total_absth_data= $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'};
117
118# print("warn: " .$warn_absth_data . "\n");
119# print("crit: " .$crit_absth_data . "\n");
120# print("total: " .$total_absth_data . "\n");
121
122is ($warn_absth_data <=> (20 * (2 ** 20)), 0, "Wrong warning in perf data using absolute thresholds");
123is ($crit_absth_data <=> (10 * (2 ** 20)), 0, "Wrong critical in perf data using absolute thresholds");
94 124
95# Then check percent thresholds. 125# Then check percent thresholds.
96$result = NPTest->testCmd( 126$result = NPTest->testCmd(
97 "./check_disk -w 20% -c 10% -p $mountpoint_valid" 127 "./check_disk -w 20% -c 10% -p $mountpoint_valid $output_format"
98 ); 128 );
99$_ = $result->perf_output; 129
100my ($warn_percth_data, $crit_percth_data, $total_percth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); 130cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
101is ($warn_percth_data, int((1-20/100)*$total_percth_data), "Wrong warning in perf data using percent thresholds"); 131
102is ($crit_percth_data, int((1-10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds"); 132my $warn_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'};
133my $crit_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'};
134my $total_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'};
135
136print("warn_percth_data: " . $warn_percth_data . "\n");
137print("crit_percth_data: " . $crit_percth_data . "\n");
138
139is (int($warn_percth_data), int((20/100)*$total_percth_data), "Wrong warning in perf data using percent thresholds. Got " . $warn_percth_data . " with total " . $total_percth_data);
140is (int($crit_percth_data), int((10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds. Got " . $crit_percth_data . " with total " . $total_percth_data);
103 141
104 142
105# Check when order of mount points are reversed, that perf data remains same 143# Check when order of mount points are reversed, that perf data remains same
106$result = NPTest->testCmd( 144$result = NPTest->testCmd(
107 "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid" 145 "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid $output_format"
108 ); 146 );
109@_ = sort(split(/ /, $result->perf_output)); 147cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
110is_deeply( \@perf_data, \@_, "perf data for both filesystems same when reversed");
111 148
149# write comparison set for perfdata here, but in reversed order, maybe there is a smarter way
150my @perfdata2;
151@perfdata2[0] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0];
152@perfdata2[1] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0];
153# Decrease precision of numbers since the the fs might be modified between the two runs
154$perfdata2[0]->{'value'}->{'value'} = int($perfdata2[0]->{'value'}->{'value'} / 1000000);
155$perfdata2[1]->{'value'}->{'value'} = int($perfdata2[1]->{'value'}->{'value'} / 1000000);
156is_deeply(\@perfdata, \@perfdata2, "perf data for both filesystems same when reversed");
112 157
113# Basic filesystem checks for sizes 158# Basic filesystem checks for sizes
114$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free" ); 159$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free $output_format");
115cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free"); 160cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
116like ( $result->output, $successOutput, "OK output" ); 161like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free");
117like ( $result->only_output, qr/free space/, "Have free space text");
118like ( $result->only_output, qr/$more_free/, "Have disk name in text");
119 162
120$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free" ); 163$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free $output_format" );
121cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free and $less_free"); 164cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
165like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free and $less_free");
122 166
123$_ = $result->output; 167my $free_mb_on_mp1 =$result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / (1024 * 1024);
124 168my $free_mb_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ (1024 * 1024);
125my ($free_mb_on_mp1, $free_mb_on_mp2) = (m/(\d+)MiB .* (\d+)MiB /g);
126die "Cannot parse output: $_" unless ($free_mb_on_mp1 && $free_mb_on_mp2); 169die "Cannot parse output: $_" unless ($free_mb_on_mp1 && $free_mb_on_mp2);
127 170
128my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2; 171my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2;
129 172
130 173
174$result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free $output_format" );
175cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
131 176
132$result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free" ); 177$result = NPTest->testCmd( "./check_disk 101 101 $more_free" );
133is( $result->only_output, "DISK OK", "No print out of disks with -e for OKs"); 178like($result->output, "/OK/", "OK in Output");
134 179cmp_ok( $result->return_code, '==', 0, "Old syntax okay, output was: ". $result->output . "\n" );
135$result = NPTest->testCmd( "./check_disk 100 100 $more_free" );
136cmp_ok( $result->return_code, '==', 0, "Old syntax okay" );
137 180
138$result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" ); 181$result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" );
139cmp_ok( $result->return_code, "==", 0, "At least 1% free" ); 182cmp_ok( $result->return_code, "==", 0, "At least 1% free" );
140 183
141$result = NPTest->testCmd( 184$result = NPTest->testCmd(
142 "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free" 185 "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free $output_format"
143 ); 186 );
144cmp_ok( $result->return_code, "==", 2, "Get critical on less_free mountpoint $less_free" ); 187cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
145like( $result->output, $failureOutput, "Right output" ); 188like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Get critical on less_free mountpoint $less_free");
146 189
147 190
148$result = NPTest->testCmd( 191$result = NPTest->testCmd(
149 "./check_disk -w $avg_free% -c 0% -p $less_free" 192 "./check_disk -w $avg_free_percent% -c 0% -p $less_free $output_format"
150 ); 193 );
151cmp_ok( $result->return_code, '==', 1, "Get warning on less_free mountpoint, when checking avg_free"); 194cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
195like($result->{'mp_test_result'}->{'state'}, "/WARNING/", "Get warning on less_free mountpoint, when checking avg_free");
152 196
153$result = NPTest->testCmd( 197$result = NPTest->testCmd(
154 "./check_disk -w $avg_free% -c $avg_free% -p $more_free" 198 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
155 ); 199 );
156cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free"); 200cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free");
157 201
158$result = NPTest->testCmd( 202$result = NPTest->testCmd(
159 "./check_disk -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" 203 "./check_disk -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
160 ); 204 );
161cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning"); 205cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning");
162my $all_disks = $result->output; 206my $all_disks = $result->output;
163 207
164$result = NPTest->testCmd( 208$result = NPTest->testCmd(
165 "./check_disk -e -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" 209 "./check_disk -e -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
166 ); 210 );
167isnt( $result->output, $all_disks, "-e gives different output"); 211isnt( $result->output, $all_disks, "-e gives different output");
168 212
169# Need spaces around filesystem name in case less_free and more_free are nested 213# Need spaces around filesystem name in case less_free and more_free are nested
170like( $result->output, qr/ $less_free /, "Found problem $less_free"); 214like( $result->output, qr/ $less_free /, "Found problem $less_free");
171unlike( $result->only_output, qr/ $more_free /, "Has ignored $more_free as not a problem"); 215unlike( $result->only_output, qr/ $more_free /, "Has ignored $more_free as not a problem");
172like( $result->perf_output, qr/ $more_free=/, "But $more_free is still in perf data"); 216like( $result->perf_output, qr/'$more_free'=/, "But $more_free is still in perf data");
173 217
174$result = NPTest->testCmd( 218$result = NPTest->testCmd(
175 "./check_disk -w $avg_free% -c 0% -p $more_free" 219 "./check_disk -w $avg_free_percent% -c 0% -p $more_free"
176 ); 220 );
177cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free"); 221cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free");
178 222
179$result = NPTest->testCmd( 223$result = NPTest->testCmd(
180 "./check_disk -w $avg_free% -c $avg_free% -p $less_free" 224 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free"
181 ); 225 );
182cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free"); 226cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free");
183$result = NPTest->testCmd( 227$result = NPTest->testCmd(
184 "./check_disk -w $avg_free% -c 0% -p $more_free -w $avg_free% -c $avg_free% -p $less_free" 228 "./check_disk -w $avg_free_percent% -c 0% -p $more_free -w $avg_free_percent% -c $avg_free_percent% -p $less_free"
185 ); 229 );
186cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical"); 230cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical");
187 231
188$result = NPTest->testCmd( 232$result = NPTest->testCmd(
189 "./check_disk -w $avg_free% -c $avg_free% -p $less_free -w $avg_free% -c 0% -p $more_free" 233 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free -w $avg_free_percent% -c 0% -p $more_free"
190 ); 234 );
191cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); 235cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference");
192 236
@@ -203,32 +247,32 @@ is( $result->return_code, 2, "Critical requesting 100% free inodes for both moun
203$result = NPTest->testCmd( "./check_disk --iwarning 1% --icritical 1% -p $more_inode_free -K 100% -W 100% -p $less_inode_free" ); 247$result = NPTest->testCmd( "./check_disk --iwarning 1% --icritical 1% -p $more_inode_free -K 100% -W 100% -p $less_inode_free" );
204is( $result->return_code, 2, "Get critical on less_inode_free mountpoint $less_inode_free"); 248is( $result->return_code, 2, "Get critical on less_inode_free mountpoint $less_inode_free");
205 249
206$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free" ); 250$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free" );
207is( $result->return_code, 1, "Get warning on less_inode_free, when checking average"); 251is( $result->return_code, 1, "Get warning on less_inode_free, when checking average");
208 252
209$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free "); 253$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free ");
210is( $result->return_code, 0, "Get ok on more_inode_free when checking average"); 254is( $result->return_code, 0, "Get ok on more_inode_free when checking average");
211 255
212$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free" ); 256$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free" );
213is ($result->return_code, 1, "Combine above two tests, get warning"); 257is ($result->return_code, 1, "Combine above two tests, get warning");
214$all_disks = $result->output; 258$all_disks = $result->output;
215 259
216$result = NPTest->testCmd( "./check_disk -e -W $avg_inode_free% -K 0% -p $less_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free" ); 260$result = NPTest->testCmd( "./check_disk -e -W $avg_inode_free_percentage% -K 0% -p $less_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free" );
217isnt( $result->output, $all_disks, "-e gives different output"); 261isnt( $result->output, $all_disks, "-e gives different output");
218like( $result->output, qr/$less_inode_free/, "Found problem $less_inode_free"); 262like( $result->output, qr/$less_inode_free/, "Found problem $less_inode_free");
219unlike( $result->only_output, qr/$more_inode_free\s/, "Has ignored $more_inode_free as not a problem"); 263unlike( $result->only_output, qr/$more_inode_free\s/, "Has ignored $more_inode_free as not a problem");
220like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data"); 264like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data");
221 265
222$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free" ); 266$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" );
223is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average"); 267is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average");
224 268
225$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" ); 269$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" );
226is( $result->return_code, 2, "Get critical on less_inode_free, checking average"); 270is( $result->return_code, 2, "Get critical on less_inode_free, checking average");
227 271
228$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" ); 272$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" );
229is( $result->return_code, 2, "Combining above two tests, get critical"); 273is( $result->return_code, 2, "Combining above two tests, get critical");
230 274
231$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free -W $avg_inode_free% -K 0% -p $more_inode_free" ); 275$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" );
232cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); 276cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference");
233 277
234 278
@@ -249,9 +293,9 @@ $result = NPTest->testCmd(
249 ); 293 );
250cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" ); 294cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" );
251 295
252$result = NPTest->testCmd( "./check_disk -w 100% -c 100% ".${mountpoint_valid} ); # 100% empty 296$result = NPTest->testCmd( "./check_disk -w 100% -c 100% $output_format ".${mountpoint_valid} ); # 100% empty
253cmp_ok( $result->return_code, "==", 2, "100% empty" ); 297cmp_ok( $result->return_code, "==", 0, "100% empty" );
254like( $result->output, $failureOutput, "Right output" ); 298like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "100% empty");
255 299
256$result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" ); 300$result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" );
257cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" ); 301cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" );
@@ -263,7 +307,8 @@ cmp_ok( $result->return_code, "==", 2, "100 TB empty" );
263# Checking old syntax of check_disk warn crit [fs], with warn/crit at USED% thresholds 307# Checking old syntax of check_disk warn crit [fs], with warn/crit at USED% thresholds
264$result = NPTest->testCmd( "./check_disk 0 0 ".${mountpoint_valid} ); 308$result = NPTest->testCmd( "./check_disk 0 0 ".${mountpoint_valid} );
265cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used"); 309cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used");
266like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments"); 310# like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments");
311# TODO not sure what the above should test, taking it out
267 312
268$result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" ); 313$result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" );
269cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" ); 314cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" );
@@ -311,8 +356,9 @@ $result = NPTest->testCmd( "./check_disk -w 0% -c 0% -p / -p /" );
311unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice"); 356unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice");
312 357
313# are partitions added if -C is given without path selection -p ? 358# are partitions added if -C is given without path selection -p ?
314$result = NPTest->testCmd( "./check_disk -w 0% -c 0% -C -w 0% -c 0% -p $mountpoint_valid" ); 359$result = NPTest->testCmd( "./check_disk -w 0% -c 0% -C -w 0% -c 0% -p $mountpoint_valid $output_format" );
315like( $result->output, '/;.*;\|/', "-C selects partitions if -p is not given"); 360cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
361cmp_ok(scalar $result->{'mp_test_result'}->{'checks'}, '>', 1, "-C invokes matchall logic again");
316 362
317# grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit 363# grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit
318$result = NPTest->testCmd( "./check_disk -w ". ($free_mb_on_all + 1) ." -c ". ($free_mb_on_all + 1) ." -g group -p $mountpoint_valid -p $mountpoint2_valid" ); 364$result = NPTest->testCmd( "./check_disk -w ". ($free_mb_on_all + 1) ." -c ". ($free_mb_on_all + 1) ." -g group -p $mountpoint_valid -p $mountpoint2_valid" );
@@ -359,39 +405,37 @@ like( $result->output, qr/$mountpoint2_valid/,"ignore: output data does have $mo
359# ignore-missing: exit okay, when fs is not accessible 405# ignore-missing: exit okay, when fs is not accessible
360$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p /bob"); 406$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p /bob");
361cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for not existing filesystem /bob"); 407cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for not existing filesystem /bob");
362like( $result->output, '/^DISK OK - No disks were found for provided parameters - ignored paths: /bob;.*$/', 'Output OK'); 408like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
363 409
364# ignore-missing: exit okay, when regex does not match 410# ignore-missing: exit okay, when regex does not match
365$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r /bob"); 411$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r /bob");
366cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 412cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
367like( $result->output, '/^DISK OK - No disks were found for provided parameters.*$/', 'Output OK'); 413like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
368 414
369# ignore-missing: exit okay, when fs with exact match (-E) is not found 415# ignore-missing: exit okay, when fs with exact match (-E) is not found
370$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -E -p /etc"); 416$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -E -p /etc");
371cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay when exact match does not find fs"); 417cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay when exact match does not find fs");
372like( $result->output, '/^DISK OK - No disks were found for provided parameters - ignored paths: /etc;.*$/', 'Output OK'); 418like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
373 419
374# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (regex) 420# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (regex)
375$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r '/bob' -r '^/\$'"); 421$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r '/bob' -r '^/\$'");
376cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 422cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
377like( $result->output, '/^DISK OK - free space: \/ .*$/', 'Output OK');
378 423
379# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (path) 424# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (path)
380$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p '/bob' -p '/'"); 425$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p '/bob' -p '/'");
381cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 426cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
382like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK'); 427# like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK');
383 428
384# ignore-missing: exit okay, when checking one non-existing fs (path) and one ignored 429# ignore-missing: exit okay, when checking one non-existing fs (path) and one ignored
385$result = NPTest->testCmd( "./check_disk -n -w 0% -c 0% -r /dummy -i /dummy2"); 430$result = NPTest->testCmd( "./check_disk -n -w 0% -c 0% -r /dummy -i /dummy2");
386cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 431cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
387like( $result->output, '/^DISK OK - No disks were found for provided parameters\|$/', 'Output OK'); 432like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
388 433
389# ignore-missing: exit okay, when regex match does not find anything 434# ignore-missing: exit okay, when regex match does not find anything
390$result = NPTest->testCmd( "./check_disk -n -e -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); 435$result = NPTest->testCmd( "./check_disk -n -e -l -w 10% -c 5% -W 10% -K 5% -r /dummy");
391cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 436cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
392like( $result->output, '/^DISK OK\|$/', 'Output OK');
393 437
394# ignore-missing: exit okay, when regex match does not find anything 438# ignore-missing: exit okay, when regex match does not find anything
395$result = NPTest->testCmd( "./check_disk -n -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); 439$result = NPTest->testCmd( "./check_disk -n -l -w 10% -c 5% -W 10% -K 5% -r /dummy");
396cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 440cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
397like( $result->output, '/^DISK OK - No disks were found for provided parameters\|$/', 'Output OK'); 441like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
diff --git a/plugins/t/check_ftp.t b/plugins/t/check_ftp.t
index 93a7d7c3..a2f79dca 100644
--- a/plugins/t/check_ftp.t
+++ b/plugins/t/check_ftp.t
@@ -15,7 +15,7 @@ my $host_tcp_ftp = getTestParameter("NP_HOST_TCP_FTP", "A host providing t
15my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1"); 15my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1");
16my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); 16my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost");
17 17
18my $successOutput = '/FTP OK -\s+[0-9]?\.?[0-9]+ second response time/'; 18my $successOutput = '/Connection time\s+[0-9]?\.?[0-9]+/';
19 19
20my $t; 20my $t;
21 21
diff --git a/plugins/t/check_jabber.t b/plugins/t/check_jabber.t
index 08cadcbd..dc46f4c3 100644
--- a/plugins/t/check_jabber.t
+++ b/plugins/t/check_jabber.t
@@ -15,11 +15,11 @@ my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname
15my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); 15my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost");
16 16
17 17
18my $jabberOK = '/JABBER OK\s-\s\d+\.\d+\ssecond response time on '.$host_tcp_jabber.' port 5222/'; 18my $jabberOK = '/Connection to '.$host_tcp_jabber.' on port 5222/';
19 19
20my $jabberUnresponsive = '/Socket timeout after\s\d+\sseconds/'; 20my $jabberUnresponsive = '/Socket timeout after\s\d+\sseconds/';
21 21
22my $jabberInvalid = '/JABBER CRITICAL - Invalid hostname, address or socket:\s.+/'; 22my $jabberInvalid = '/Invalid hostname, address or socket:\s.+/';
23 23
24my $r; 24my $r;
25 25
diff --git a/plugins/t/check_tcp.t b/plugins/t/check_tcp.t
index cb4de53d..5c8fd0be 100644
--- a/plugins/t/check_tcp.t
+++ b/plugins/t/check_tcp.t
@@ -21,19 +21,19 @@ my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname
21my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); 21my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost");
22my $internet_access = getTestParameter("NP_INTERNET_ACCESS", "Is this system directly connected to the internet?", "yes"); 22my $internet_access = getTestParameter("NP_INTERNET_ACCESS", "Is this system directly connected to the internet?", "yes");
23 23
24my $successOutput = '/^TCP OK\s-\s+[0-9]?\.?[0-9]+ second response time on port [0-9]+/'; 24my $successOutput = '/Connection time\s+[0-9]?\.?[0-9]+s is within thresholds+/';
25 25
26my $failedExpect = '/^TCP WARNING\s-\sUnexpected response from host/socket on port [0-9]+/'; 26my $failedExpect = '/Answer failed to match/';
27 27
28my $t; 28my $t;
29 29
30$tests = $tests - 4 if $internet_access eq "no"; 30$tests = $tests - 4 if $internet_access eq "no";
31plan tests => $tests; 31plan tests => $tests;
32 32
33$t += checkCmd( "./check_tcp $host_tcp_http -p 80 -wt 300 -ct 600", 0, $successOutput ); 33$t += checkCmd( "./check_tcp $host_tcp_http -p 80 -w 300 -c 600", 0, $successOutput );
34$t += checkCmd( "./check_tcp $host_tcp_http -p 81 -wt 0 -ct 0 -to 1", 2 ); # use invalid port for this test 34$t += checkCmd( "./check_tcp $host_tcp_http -p 81 -w 0 -c 0 -t 1", 2 ); # use invalid port for this test
35$t += checkCmd( "./check_tcp $host_nonresponsive -p 80 -wt 0 -ct 0 -to 1", 2 ); 35$t += checkCmd( "./check_tcp $host_nonresponsive -p 80 -w 0 -c 0 -t 1", 2 );
36$t += checkCmd( "./check_tcp $hostname_invalid -p 80 -wt 0 -ct 0 -to 1", 2 ); 36$t += checkCmd( "./check_tcp $hostname_invalid -p 80 -w 0 -c 0 -t 1", 2 );
37if($internet_access ne "no") { 37if($internet_access ne "no") {
38 $t += checkCmd( "./check_tcp -S -D 1 -H $host_tls_http -p 443", 0 ); 38 $t += checkCmd( "./check_tcp -S -D 1 -H $host_tls_http -p 443", 0 );
39 $t += checkCmd( "./check_tcp -S -D 9000,1 -H $host_tls_http -p 443", 1 ); 39 $t += checkCmd( "./check_tcp -S -D 9000,1 -H $host_tls_http -p 443", 1 );
diff --git a/plugins/t/check_udp.t b/plugins/t/check_udp.t
index 6c47d095..5cb9e6dc 100644
--- a/plugins/t/check_udp.t
+++ b/plugins/t/check_udp.t
@@ -28,7 +28,7 @@ like ( $res->output, '/With UDP checks, a send/expect string must be specified.
28 28
29$res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s foo -e bar" ); 29$res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s foo -e bar" );
30cmp_ok( $res->return_code, '==', 2, "Errors correctly because no udp service running" ); 30cmp_ok( $res->return_code, '==', 2, "Errors correctly because no udp service running" );
31like ( $res->output, '/No data received from host/', "Output OK"); 31like ( $res->output, '/Received no data /', "Output OK");
32 32
33my $nc; 33my $nc;
34if(system("which nc.traditional >/dev/null 2>&1") == 0) { 34if(system("which nc.traditional >/dev/null 2>&1") == 0) {
@@ -48,7 +48,7 @@ SKIP: {
48 sleep 1; 48 sleep 1;
49 $res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s '' -e barbar -4" ); 49 $res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s '' -e barbar -4" );
50 cmp_ok( $res->return_code, '==', 0, "Got barbar response back" ); 50 cmp_ok( $res->return_code, '==', 0, "Got barbar response back" );
51 like ( $res->output, '/\[barbar\]/', "Output OK"); 51 like ( $res->output, '/answer of the server matched/', "Output OK");
52 close NC; 52 close NC;
53 53
54 # Start up a udp server listening on port 3333, quit after 3 seconds 54 # Start up a udp server listening on port 3333, quit after 3 seconds
diff --git a/lib/tests/test_disk.c b/plugins/tests/test_check_disk.c
index c18db7a4..35c57bce 100644
--- a/lib/tests/test_disk.c
+++ b/plugins/tests/test_check_disk.c
@@ -17,28 +17,16 @@
17 *****************************************************************************/ 17 *****************************************************************************/
18 18
19#include "common.h" 19#include "common.h"
20#include "utils_disk.h" 20#include "../check_disk.d/utils_disk.h"
21#include "tap.h" 21#include "../../tap/tap.h"
22#include "regex.h" 22#include "regex.h"
23 23
24void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc); 24void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc);
25 25
26int main(int argc, char **argv) { 26int main(int argc, char **argv) {
27 struct name_list *exclude_filesystem = NULL; 27 plan_tests(35);
28 struct name_list *exclude_fstype = NULL;
29 struct name_list *dummy_mountlist = NULL;
30 struct name_list *temp_name;
31 struct parameter_list *paths = NULL;
32 struct parameter_list *p, *prev = NULL, *last = NULL;
33
34 struct mount_entry *dummy_mount_list;
35 struct mount_entry *me;
36 struct mount_entry **mtail = &dummy_mount_list;
37 int cflags = REG_NOSUB | REG_EXTENDED;
38 int found = 0, count = 0;
39
40 plan_tests(33);
41 28
29 struct name_list *exclude_filesystem = NULL;
42 ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list"); 30 ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list");
43 np_add_name(&exclude_filesystem, "/var/log"); 31 np_add_name(&exclude_filesystem, "/var/log");
44 ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now"); 32 ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now");
@@ -47,6 +35,7 @@ int main(int argc, char **argv) {
47 ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now"); 35 ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now");
48 ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list"); 36 ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list");
49 37
38 struct name_list *exclude_fstype = NULL;
50 ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list"); 39 ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list");
51 np_add_name(&exclude_fstype, "iso9660"); 40 np_add_name(&exclude_fstype, "iso9660");
52 ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now"); 41 ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now");
@@ -59,7 +48,9 @@ int main(int argc, char **argv) {
59 } 48 }
60 */ 49 */
61 50
62 me = (struct mount_entry *)malloc(sizeof *me); 51 struct mount_entry *dummy_mount_list;
52 struct mount_entry **mtail = &dummy_mount_list;
53 struct mount_entry *me = (struct mount_entry *)malloc(sizeof *me);
63 me->me_devname = strdup("/dev/c0t0d0s0"); 54 me->me_devname = strdup("/dev/c0t0d0s0");
64 me->me_mountdir = strdup("/"); 55 me->me_mountdir = strdup("/");
65 *mtail = me; 56 *mtail = me;
@@ -77,6 +68,7 @@ int main(int argc, char **argv) {
77 *mtail = me; 68 *mtail = me;
78 mtail = &me->me_next; 69 mtail = &me->me_next;
79 70
71 int cflags = REG_NOSUB | REG_EXTENDED;
80 np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a")); 72 np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a"));
81 np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3, strdup("regex on dev names:")); 73 np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3, strdup("regex on dev names:"));
82 np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0, strdup("regex on non existent dev/path:")); 74 np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0, strdup("regex on non existent dev/path:"));
@@ -89,14 +81,16 @@ int main(int argc, char **argv) {
89 np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, strdup("grouped regex pathname match:")); 81 np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, strdup("grouped regex pathname match:"));
90 np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, strdup("grouped regi pathname match:")); 82 np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, strdup("grouped regi pathname match:"));
91 83
92 np_add_parameter(&paths, "/home/groups"); 84 filesystem_list test_paths = filesystem_list_init();
93 np_add_parameter(&paths, "/var"); 85 mp_int_fs_list_append(&test_paths, "/home/groups");
94 np_add_parameter(&paths, "/tmp"); 86 mp_int_fs_list_append(&test_paths, "/var");
95 np_add_parameter(&paths, "/home/tonvoon"); 87 mp_int_fs_list_append(&test_paths, "/tmp");
96 np_add_parameter(&paths, "/dev/c2t0d0s0"); 88 mp_int_fs_list_append(&test_paths, "/home/tonvoon");
89 mp_int_fs_list_append(&test_paths, "/dev/c2t0d0s0");
90 ok(test_paths.length == 5, "List counter works correctly with appends");
97 91
98 np_set_best_match(paths, dummy_mount_list, false); 92 mp_int_fs_list_set_best_match(test_paths, dummy_mount_list, false);
99 for (p = paths; p; p = p->name_next) { 93 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
100 struct mount_entry *temp_me; 94 struct mount_entry *temp_me;
101 temp_me = p->best_match; 95 temp_me = p->best_match;
102 if (!strcmp(p->name, "/home/groups")) { 96 if (!strcmp(p->name, "/home/groups")) {
@@ -112,15 +106,19 @@ int main(int argc, char **argv) {
112 } 106 }
113 } 107 }
114 108
115 paths = NULL; /* Bad boy - should free, but this is a test suite */ 109 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
116 np_add_parameter(&paths, "/home/groups"); 110 mp_int_fs_list_del(&test_paths, p);
117 np_add_parameter(&paths, "/var"); 111 }
118 np_add_parameter(&paths, "/tmp"); 112 ok(test_paths.length == 0, "List delete sets counter properly");
119 np_add_parameter(&paths, "/home/tonvoon");
120 np_add_parameter(&paths, "/home");
121 113
122 np_set_best_match(paths, dummy_mount_list, true); 114 mp_int_fs_list_append(&test_paths, "/home/groups");
123 for (p = paths; p; p = p->name_next) { 115 mp_int_fs_list_append(&test_paths, "/var");
116 mp_int_fs_list_append(&test_paths, "/tmp");
117 mp_int_fs_list_append(&test_paths, "/home/tonvoon");
118 mp_int_fs_list_append(&test_paths, "/home");
119
120 mp_int_fs_list_set_best_match(test_paths, dummy_mount_list, true);
121 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
124 if (!strcmp(p->name, "/home/groups")) { 122 if (!strcmp(p->name, "/home/groups")) {
125 ok(!p->best_match, "/home/groups correctly not found"); 123 ok(!p->best_match, "/home/groups correctly not found");
126 } else if (!strcmp(p->name, "/var")) { 124 } else if (!strcmp(p->name, "/var")) {
@@ -134,59 +132,66 @@ int main(int argc, char **argv) {
134 } 132 }
135 } 133 }
136 134
135 bool found = false;
137 /* test deleting first element in paths */ 136 /* test deleting first element in paths */
138 paths = np_del_parameter(paths, NULL); 137 mp_int_fs_list_del(&test_paths, NULL);
139 for (p = paths; p; p = p->name_next) { 138 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
140 if (!strcmp(p->name, "/home/groups")) 139 if (!strcmp(p->name, "/home/groups")) {
141 found = 1; 140 found = true;
141 }
142 } 142 }
143 ok(found == 0, "first element successfully deleted"); 143 ok(!found, "first element successfully deleted");
144 found = 0; 144 found = false;
145 145
146 p = paths; 146 parameter_list_elem *prev = NULL;
147 while (p) { 147 parameter_list_elem *p = NULL;
148 if (!strcmp(p->name, "/tmp")) 148 for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) {
149 p = np_del_parameter(p, prev); 149 if (!strcmp(path->name, "/tmp")) {
150 else { 150 mp_int_fs_list_del(&test_paths, path);
151 prev = p;
152 p = p->name_next;
153 } 151 }
152 p = path;
154 } 153 }
155 154
156 for (p = paths; p; p = p->name_next) { 155 parameter_list_elem *last = NULL;
157 if (!strcmp(p->name, "/tmp")) 156 for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) {
158 found = 1; 157 if (!strcmp(path->name, "/tmp")) {
159 if (p->name_next) 158 found = true;
160 prev = p; 159 }
161 else 160 if (path->next) {
162 last = p; 161 prev = path;
162 } else {
163 last = path;
164 }
163 } 165 }
164 ok(found == 0, "/tmp element successfully deleted"); 166 ok(!found, "/tmp element successfully deleted");
165 167
166 p = np_del_parameter(last, prev); 168 int count = 0;
167 for (p = paths; p; p = p->name_next) { 169 mp_int_fs_list_del(&test_paths, p);
168 if (!strcmp(p->name, "/home")) 170 for (p = test_paths.first; p; p = p->next) {
169 found = 1; 171 if (!strcmp(p->name, "/home")) {
172 found = true;
173 }
170 last = p; 174 last = p;
171 count++; 175 count++;
172 } 176 }
173 ok(found == 0, "last (/home) element successfully deleted"); 177 ok(!found, "last (/home) element successfully deleted");
174 ok(count == 2, "two elements remaining"); 178 ok(count == 2, "two elements remaining");
175 179
176 return exit_status(); 180 return exit_status();
177} 181}
178 182
179void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc) { 183void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc) {
180 int matches = 0; 184 regex_t regex;
181 regex_t re; 185 if (regcomp(&regex, regstr, cflags) == 0) {
182 struct mount_entry *me; 186 int matches = 0;
183 if (regcomp(&re, regstr, cflags) == 0) { 187 for (struct mount_entry *me = dummy_mount_list; me; me = me->me_next) {
184 for (me = dummy_mount_list; me; me = me->me_next) { 188 if (np_regex_match_mount_entry(me, &regex)) {
185 if (np_regex_match_mount_entry(me, &re))
186 matches++; 189 matches++;
190 }
187 } 191 }
188 ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect, matches); 192 ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect, matches);
189 193
190 } else 194 } else {
191 ok(false, "regex '%s' not compilable", regstr); 195 ok(false, "regex '%s' not compilable", regstr);
196 }
192} 197}
diff --git a/plugins/tests/test_check_disk.t b/plugins/tests/test_check_disk.t
new file mode 100755
index 00000000..56354650
--- /dev/null
+++ b/plugins/tests/test_check_disk.t
@@ -0,0 +1,6 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_check_disk") {
4 plan skip_all => "./test_check_disk not compiled - please enable libtap library to test";
5}
6exec "./test_check_disk";
diff --git a/plugins/utils.h b/plugins/utils.h
index 92a6c115..1d3c153c 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -76,7 +76,7 @@ char *strnl(char *);
76char *strpcpy(char *, const char *, const char *); 76char *strpcpy(char *, const char *, const char *);
77char *strpcat(char *, const char *, const char *); 77char *strpcat(char *, const char *, const char *);
78int xvasprintf(char **strp, const char *fmt, va_list ap); 78int xvasprintf(char **strp, const char *fmt, va_list ap);
79int xasprintf(char **strp, const char *fmt, ...); 79int xasprintf(char **strp, const char *fmt, ...)__attribute__ ((format (printf, 2, 3)));
80 80
81void usage(const char *) __attribute__((noreturn)); 81void usage(const char *) __attribute__((noreturn));
82void usage2(const char *, const char *) __attribute__((noreturn)); 82void usage2(const char *, const char *) __attribute__((noreturn));