summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am97
-rw-r--r--plugins/check_apt.c824
-rw-r--r--plugins/check_apt.d/config.h46
-rw-r--r--plugins/check_by_ssh.c815
-rw-r--r--plugins/check_by_ssh.d/config.h71
-rw-r--r--plugins/check_cluster.c351
-rw-r--r--plugins/check_cluster.d/config.h33
-rw-r--r--plugins/check_curl.c4369
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c1814
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h151
-rw-r--r--plugins/check_curl.d/config.h123
-rw-r--r--plugins/check_dbi.c1377
-rw-r--r--plugins/check_dbi.d/config.h66
-rw-r--r--plugins/check_dig.c919
-rw-r--r--plugins/check_dig.d/config.h48
-rw-r--r--plugins/check_disk.c2379
-rw-r--r--plugins/check_disk.d/utils_disk.c528
-rw-r--r--plugins/check_disk.d/utils_disk.h160
-rw-r--r--plugins/check_dns.c1205
-rw-r--r--plugins/check_dns.d/config.h34
-rw-r--r--plugins/check_dummy.c198
-rw-r--r--plugins/check_fping.c1050
-rw-r--r--plugins/check_fping.d/config.h81
-rw-r--r--plugins/check_game.c604
-rw-r--r--plugins/check_game.d/config.h30
-rw-r--r--plugins/check_hpjd.c543
-rw-r--r--plugins/check_hpjd.d/config.h25
-rw-r--r--plugins/check_http.c3585
-rw-r--r--plugins/check_ide_smart.c727
-rw-r--r--plugins/check_ide_smart.d/config.h15
-rw-r--r--plugins/check_ldap.c813
-rw-r--r--plugins/check_ldap.d/config.h56
-rw-r--r--plugins/check_load.c662
-rw-r--r--plugins/check_load.d/config.h30
-rw-r--r--plugins/check_mrtg.c632
-rw-r--r--plugins/check_mrtg.d/config.h37
-rw-r--r--plugins/check_mrtgtraf.c632
-rw-r--r--plugins/check_mrtgtraf.d/config.h33
-rw-r--r--plugins/check_mysql.c1002
-rw-r--r--plugins/check_mysql.d/config.h59
-rw-r--r--plugins/check_mysql_query.c547
-rw-r--r--plugins/check_mysql_query.d/config.h42
-rw-r--r--plugins/check_nagios.c422
-rw-r--r--plugins/check_nagios.d/config.h19
-rw-r--r--plugins/check_nt.c801
-rw-r--r--plugins/check_ntp.c901
-rw-r--r--plugins/check_ntp_peer.c1029
-rw-r--r--plugins/check_ntp_peer.d/config.h82
-rw-r--r--plugins/check_ntp_time.c869
-rw-r--r--plugins/check_ntp_time.d/config.h43
-rw-r--r--plugins/check_nwstat.c1740
-rw-r--r--plugins/check_overcr.c469
-rw-r--r--plugins/check_pgsql.c950
-rw-r--r--plugins/check_pgsql.d/config.h70
-rw-r--r--plugins/check_ping.c905
-rw-r--r--plugins/check_ping.d/config.h46
-rw-r--r--plugins/check_procs.c1186
-rw-r--r--plugins/check_procs.d/config.h75
-rw-r--r--plugins/check_radius.c633
-rw-r--r--plugins/check_radius.d/config.h48
-rw-r--r--plugins/check_real.c761
-rw-r--r--plugins/check_real.d/config.h42
-rw-r--r--plugins/check_smtp.c1453
-rw-r--r--plugins/check_smtp.d/config.h96
-rw-r--r--plugins/check_snmp.c2074
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.c938
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.h71
-rw-r--r--plugins/check_snmp.d/config.h81
-rw-r--r--plugins/check_ssh.c535
-rw-r--r--plugins/check_ssh.d/config.h29
-rw-r--r--plugins/check_swap.c844
-rw-r--r--plugins/check_swap.d/check_swap.h49
-rw-r--r--plugins/check_swap.d/swap.c471
-rw-r--r--plugins/check_tcp.c1138
-rw-r--r--plugins/check_tcp.d/config.h84
-rw-r--r--plugins/check_time.c529
-rw-r--r--plugins/check_time.d/config.h42
-rw-r--r--plugins/check_ups.c377
-rw-r--r--plugins/check_ups.d/config.h54
-rw-r--r--plugins/check_users.c433
-rw-r--r--plugins/check_users.d/config.h20
-rw-r--r--plugins/check_users.d/users.c169
-rw-r--r--plugins/check_users.d/users.h18
-rw-r--r--plugins/common.h207
-rw-r--r--plugins/negate.c364
-rw-r--r--plugins/negate.d/config.h24
-rw-r--r--plugins/netutils.c451
-rw-r--r--plugins/netutils.h174
-rw-r--r--plugins/picohttpparser/picohttpparser.c1140
-rw-r--r--plugins/picohttpparser/picohttpparser.h31
-rw-r--r--plugins/popen.c322
-rw-r--r--plugins/popen.h20
-rw-r--r--plugins/runcmd.c283
-rw-r--r--plugins/runcmd.h47
-rw-r--r--plugins/sslutils.c576
-rw-r--r--plugins/t/check_apt.t14
-rw-r--r--plugins/t/check_by_ssh.t50
-rw-r--r--plugins/t/check_curl.t162
-rw-r--r--plugins/t/check_dbi.t10
-rw-r--r--plugins/t/check_disk.t340
-rw-r--r--plugins/t/check_ftp.t2
-rw-r--r--plugins/t/check_http.t2
-rw-r--r--plugins/t/check_jabber.t6
-rw-r--r--plugins/t/check_ldap.t16
-rw-r--r--plugins/t/check_load.t18
-rw-r--r--plugins/t/check_mysql.t33
-rw-r--r--plugins/t/check_mysql_query.t2
-rw-r--r--plugins/t/check_ntp.t4
-rw-r--r--plugins/t/check_smtp.t8
-rw-r--r--plugins/t/check_snmp.t103
-rw-r--r--plugins/t/check_ssh.t114
-rw-r--r--plugins/t/check_swap.t58
-rw-r--r--plugins/t/check_tcp.t12
-rw-r--r--plugins/t/check_udp.t4
-rw-r--r--plugins/t/check_users.t4
-rwxr-xr-xplugins/tests/check_curl.t406
-rwxr-xr-xplugins/tests/check_nt.t80
-rwxr-xr-xplugins/tests/check_snmp.t159
-rw-r--r--plugins/tests/check_snmp_agent.pl78
-rw-r--r--plugins/tests/conf/snmpd.conf2
-rw-r--r--plugins/tests/test_check_disk.c213
-rwxr-xr-xplugins/tests/test_check_disk.t6
-rw-r--r--plugins/tests/test_check_snmp.c175
-rwxr-xr-xplugins/tests/test_check_snmp.t6
-rw-r--r--plugins/tests/test_check_swap.c21
-rwxr-xr-xplugins/tests/test_check_swap.t6
-rw-r--r--plugins/tests/var/proc_meminfo55
-rw-r--r--plugins/urlize.c219
-rw-r--r--plugins/utils.c732
-rw-r--r--plugins/utils.h161
130 files changed, 29402 insertions, 24927 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 49086b7a..2bea8fc0 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -13,8 +13,14 @@ AM_CFLAGS = -DNP_VERSION='"$(NP_VERSION)"'
13 13
14VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t 14VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t
15 15
16AM_CPPFLAGS = -I.. -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl \ 16AM_CPPFLAGS = -I.. \
17 @LDAPINCLUDE@ @PGINCLUDE@ @SSLINCLUDE@ 17 -I$(top_srcdir)/lib \
18 -I$(top_srcdir)/gl \
19 -I$(top_srcdir)/intl \
20 -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
21 @LDAPINCLUDE@ \
22 @PGINCLUDE@ \
23 @SSLINCLUDE@
18 24
19localedir = $(datadir)/locale 25localedir = $(datadir)/locale
20# gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this 26# gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this
@@ -27,30 +33,82 @@ MATHLIBS = @MATHLIBS@
27#AM_CFLAGS = -Wall 33#AM_CFLAGS = -Wall
28 34
29libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ 35libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \
30 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_nwstat check_overcr check_ping \ 36 check_mrtg check_mrtgtraf check_ntp_peer check_ping \
31 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ 37 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \
32 check_ups check_users negate \ 38 check_ups check_users negate \
33 urlize @EXTRAS@ 39 urlize @EXTRAS@ \
40 check_snmp
34 41
35check_tcp_programs = check_ftp check_imap check_nntp check_pop \ 42check_tcp_programs = check_ftp check_imap check_nntp check_pop \
36 check_udp check_clamd @check_tcp_ssl@ 43 check_udp check_clamd @check_tcp_ssl@
37 44
38EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ 45EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \
39 check_swap check_fping check_ldap check_game check_dig \ 46 check_swap check_fping check_ldap check_game check_dig \
40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \ 47 check_nagios check_by_ssh check_dns check_ide_smart \
41 check_procs check_mysql_query check_apt check_dbi check_curl 48 check_procs check_mysql_query check_apt check_dbi check_curl \
49 \
50 tests/test_check_swap \
51 tests/test_check_snmp \
52 tests/test_check_disk
42 53
43SUBDIRS = picohttpparser 54SUBDIRS = picohttpparser
44 55
45EXTRA_DIST = t tests 56np_test_scripts = tests/test_check_swap.t \
57 tests/test_check_snmp.t \
58 tests/test_check_disk.t
59
60EXTRA_DIST = t \
61 tests \
62 $(np_test_scripts) \
63 negate.d \
64 check_swap.d \
65 check_ldap.d \
66 check_hpjd.d \
67 check_game.d \
68 check_radius.d \
69 check_ide_smart.d \
70 check_curl.d \
71 check_disk.d \
72 check_time.d \
73 check_users.d \
74 check_load.d \
75 check_nagios.d \
76 check_dbi.d \
77 check_tcp.d \
78 check_real.d \
79 check_ssh.d \
80 check_dns.d \
81 check_mrtgtraf.d \
82 check_mysql_query.d \
83 check_mrtg.d \
84 check_ntp_peer.d \
85 check_apt.d \
86 check_pgsql.d \
87 check_procs.d \
88 check_ping.d \
89 check_by_ssh.d \
90 check_smtp.d \
91 check_snmp.d \
92 check_mysql.d \
93 check_ntp_time.d \
94 check_dig.d \
95 check_cluster.d \
96 check_curl.d \
97 check_cluster.d \
98 check_ups.d \
99 check_fping.d
46 100
47PLUGINHDRS = common.h 101PLUGINHDRS = common.h
48 102
49noinst_LIBRARIES = libnpcommon.a 103noinst_LIBRARIES = libnpcommon.a
104noinst_PROGRAMS = @EXTRA_PLUGIN_TESTS@
105# These two lines support "make check", but we use "make test"
106check_PROGRAMS = @EXTRA_PLUGIN_TESTS@
50 107
51libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \ 108libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \
52 popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h 109 popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h
53 110
111
54BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a $(LIB_CRYPTO) 112BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a $(LIB_CRYPTO)
55NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS) 113NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS)
56NETLIBS = $(NETOBJS) $(SOCKETLIBS) 114NETLIBS = $(NETOBJS) $(SOCKETLIBS)
@@ -58,7 +116,10 @@ SSLOBJS = $(BASEOBJS) $(NETLIBS) $(SSLLIBS) $(LIB_CRYPTO)
58 116
59TESTS_ENVIRONMENT = perl -I $(top_builddir) -I $(top_srcdir) 117TESTS_ENVIRONMENT = perl -I $(top_builddir) -I $(top_srcdir)
60 118
61TESTS = @PLUGIN_TEST@ 119tap_ldflags = -L$(top_srcdir)/tap
120
121TESTS = @PLUGIN_TEST@ @EXTRA_PLUGIN_TESTS@
122
62 123
63test: 124test:
64 perl -I $(top_builddir) -I $(top_srcdir) ../test.pl 125 perl -I $(top_builddir) -I $(top_srcdir) ../test.pl
@@ -74,9 +135,11 @@ check_cluster_LDADD = $(BASEOBJS)
74check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 135check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
75check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 136check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
76check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a 137check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a
138check_curl_SOURCES = check_curl.c check_curl.d/check_curl_helpers.c
77check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 139check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
78check_dig_LDADD = $(NETLIBS) 140check_dig_LDADD = $(NETLIBS)
79check_disk_LDADD = $(BASEOBJS) 141check_disk_LDADD = $(BASEOBJS)
142check_disk_SOURCES = check_disk.c check_disk.d/utils_disk.c
80check_dns_LDADD = $(NETLIBS) 143check_dns_LDADD = $(NETLIBS)
81check_dummy_LDADD = $(BASEOBJS) 144check_dummy_LDADD = $(BASEOBJS)
82check_fping_LDADD = $(NETLIBS) 145check_fping_LDADD = $(NETLIBS)
@@ -94,24 +157,25 @@ check_mysql_query_CFLAGS = $(AM_CFLAGS) $(MYSQLCFLAGS)
94check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE) 157check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE)
95check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS) 158check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS)
96check_nagios_LDADD = $(BASEOBJS) 159check_nagios_LDADD = $(BASEOBJS)
97check_nt_LDADD = $(NETLIBS)
98check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
99check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 160check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
100check_nwstat_LDADD = $(NETLIBS)
101check_overcr_LDADD = $(NETLIBS)
102check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) 161check_pgsql_LDADD = $(NETLIBS) $(PGLIBS)
103check_ping_LDADD = $(NETLIBS) 162check_ping_LDADD = $(NETLIBS)
104check_procs_LDADD = $(BASEOBJS) 163check_procs_LDADD = $(BASEOBJS)
105check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) 164check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS)
106check_real_LDADD = $(NETLIBS) 165check_real_LDADD = $(NETLIBS)
166check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c
107check_snmp_LDADD = $(BASEOBJS) 167check_snmp_LDADD = $(BASEOBJS)
168check_snmp_LDFLAGS = $(AM_LDFLAGS) -lm `net-snmp-config --libs`
169check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags | sed 's/-Werror=declaration-after-statement//'`
108check_smtp_LDADD = $(SSLOBJS) 170check_smtp_LDADD = $(SSLOBJS)
109check_ssh_LDADD = $(NETLIBS) 171check_ssh_LDADD = $(NETLIBS)
172check_swap_SOURCES = check_swap.c check_swap.d/swap.c
110check_swap_LDADD = $(MATHLIBS) $(BASEOBJS) 173check_swap_LDADD = $(MATHLIBS) $(BASEOBJS)
111check_tcp_LDADD = $(SSLOBJS) 174check_tcp_LDADD = $(SSLOBJS)
112check_time_LDADD = $(NETLIBS) 175check_time_LDADD = $(NETLIBS)
113check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) 176check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS)
114check_ups_LDADD = $(NETLIBS) 177check_ups_LDADD = $(NETLIBS)
178check_users_SOURCES = check_users.c check_users.d/users.c
115check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) 179check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS)
116check_by_ssh_LDADD = $(NETLIBS) 180check_by_ssh_LDADD = $(NETLIBS)
117check_ide_smart_LDADD = $(BASEOBJS) 181check_ide_smart_LDADD = $(BASEOBJS)
@@ -122,6 +186,13 @@ if !HAVE_UTMPX
122check_users_LDADD += popen.o 186check_users_LDADD += popen.o
123endif 187endif
124 188
189tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
190tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c
191tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
192tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c
193tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap
194tests_test_check_disk_SOURCES = tests/test_check_disk.c
195
125############################################################################## 196##############################################################################
126# secondary dependencies 197# secondary dependencies
127 198
diff --git a/plugins/check_apt.c b/plugins/check_apt.c
index 5c0f6e28..9ed5b6cf 100644
--- a/plugins/check_apt.c
+++ b/plugins/check_apt.c
@@ -1,188 +1,250 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_apt plugin 3 * Monitoring check_apt plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
7* 7 *
8* Original author: Sean Finney 8 * Original author: Sean Finney
9* 9 *
10* Description: 10 * Description:
11* 11 *
12* This file contains the check_apt plugin 12 * This file contains the check_apt plugin
13* 13 *
14* Check for available updates in apt package management systems 14 * Check for available updates in apt package management systems
15* 15 *
16* 16 *
17* This program is free software: you can redistribute it and/or modify 17 * This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by 18 * it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or 19 * the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version. 20 * (at your option) any later version.
21* 21 *
22* This program is distributed in the hope that it will be useful, 22 * This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details. 25 * GNU General Public License for more details.
26* 26 *
27* You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>. 28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "perfdata.h"
32const char *progname = "check_apt"; 33const char *progname = "check_apt";
33const char *copyright = "2006-2008"; 34const char *copyright = "2006-2024";
34const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
35 36
37#include "states.h"
38#include "output.h"
36#include "common.h" 39#include "common.h"
37#include "runcmd.h" 40#include "runcmd.h"
38#include "utils.h" 41#include "utils.h"
39#include "regex.h" 42#include "regex.h"
43#include "check_apt.d/config.h"
40 44
41/* some constants */
42typedef enum { UPGRADE, DIST_UPGRADE, NO_UPGRADE } upgrade_type;
43
44/* Character for hidden input file option (for testing). */
45#define INPUT_FILE_OPT CHAR_MAX+1
46/* the default opts can be overridden via the cmdline */ 45/* the default opts can be overridden via the cmdline */
47#define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" 46const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq";
48#define UPDATE_DEFAULT_OPTS "-q" 47const char *UPDATE_DEFAULT_OPTS = "-q";
48
49/* until i commit the configure.in patch which gets this, i'll define 49/* until i commit the configure.in patch which gets this, i'll define
50 * it here as well */ 50 * it here as well */
51#ifndef PATH_TO_APTGET 51#ifndef PATH_TO_APTGET
52# define PATH_TO_APTGET "/usr/bin/apt-get" 52# define PATH_TO_APTGET "/usr/bin/apt-get"
53#endif /* PATH_TO_APTGET */ 53#endif /* PATH_TO_APTGET */
54
54/* String found at the beginning of the apt output lines we're interested in */ 55/* String found at the beginning of the apt output lines we're interested in */
55#define PKGINST_PREFIX "Inst " 56const char *PKGINST_PREFIX = "Inst ";
56/* the RE that catches security updates */ 57/* the RE that catches security updates */
57#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" 58const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)";
58 59
59/* some standard functions */ 60/* some standard functions */
60int process_arguments(int, char **); 61typedef struct {
61void print_help(void); 62 int errorcode;
63 check_apt_config config;
64} check_apt_config_wrapper;
65static check_apt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
66static void print_help(void);
62void print_usage(void); 67void print_usage(void);
63 68
64/* construct the appropriate apt-get cmdline */ 69/* construct the appropriate apt-get cmdline */
65char* construct_cmdline(upgrade_type u, const char *opts); 70static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/);
71
66/* run an apt-get update */ 72/* run an apt-get update */
67int run_update(void); 73typedef struct {
74 mp_subcheck sc;
75 bool stderr_warning;
76 bool exec_warning;
77} run_update_result;
78static run_update_result run_update(char *update_opts);
79
80typedef struct {
81 int errorcode;
82 size_t package_count;
83 size_t security_package_count;
84 char **packages_list;
85 char **secpackages_list;
86 bool exec_warning;
87} run_upgrade_result;
88
68/* run an apt-get upgrade */ 89/* run an apt-get upgrade */
69int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkglist); 90run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude,
91 const char *do_critical, const char *upgrade_opts,
92 const char *input_filename);
93
70/* add another clause to a regexp */ 94/* add another clause to a regexp */
71char* add_to_regexp(char *expr, const char *next); 95static char *add_to_regexp(char * /*expr*/, const char * /*next*/);
72/* extract package name from Inst line */ 96/* extract package name from Inst line */
73char* pkg_name(char *line); 97static char *pkg_name(char * /*line*/);
74/* string comparison function for qsort */ 98/* string comparison function for qsort */
75int cmpstringp(const void *p1, const void *p2); 99static int cmpstringp(const void * /*p1*/, const void * /*p2*/);
76 100
77/* configuration variables */ 101/* configuration variables */
78static int verbose = 0; /* -v */ 102static int verbose = 0; /* -v */
79static bool list = false; /* list packages available for upgrade */
80static bool do_update = false; /* whether to call apt-get update */
81static bool only_critical = false; /* whether to warn about non-critical updates */
82static upgrade_type upgrade = UPGRADE; /* which type of upgrade to do */
83static char *upgrade_opts = NULL; /* options to override defaults for upgrade */
84static char *update_opts = NULL; /* options to override defaults for update */
85static char *do_include = NULL; /* regexp to only include certain packages */
86static char *do_exclude = NULL; /* regexp to only exclude certain packages */
87static char *do_critical = NULL; /* regexp specifying critical packages */
88static char *input_filename = NULL; /* input filename for testing */
89/* number of packages available for upgrade to return WARNING status */
90static int packages_warning = 1;
91 103
92/* other global variables */ 104/* other global variables */
93static int stderr_warning = 0; /* if a cmd issued output on stderr */ 105static bool stderr_warning = false; /* if a cmd issued output on stderr */
94static int exec_warning = 0; /* if a cmd exited non-zero */ 106static bool exec_warning = false; /* if a cmd exited non-zero */
95
96int main (int argc, char **argv) {
97 int result=STATE_UNKNOWN, packages_available=0, sec_count=0;
98 char **packages_list=NULL, **secpackages_list=NULL;
99 107
108int main(int argc, char **argv) {
100 /* Parse extra opts if any */ 109 /* Parse extra opts if any */
101 argv=np_extra_opts(&argc, argv, progname); 110 argv = np_extra_opts(&argc, argv, progname);
102 111
103 if (process_arguments(argc, argv) == ERROR) 112 check_apt_config_wrapper tmp_config = process_arguments(argc, argv);
113
114 if (tmp_config.errorcode == ERROR) {
104 usage_va(_("Could not parse arguments")); 115 usage_va(_("Could not parse arguments"));
116 }
117
118 const check_apt_config config = tmp_config.config;
119
120 if (config.output_format_is_set) {
121 mp_set_format(config.output_format);
122 }
105 123
106 /* Set signal handling and alarm timeout */ 124 /* Set signal handling and alarm timeout */
107 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 125 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
108 usage_va(_("Cannot catch SIGALRM")); 126 usage_va(_("Cannot catch SIGALRM"));
109 } 127 }
110 128
111 /* handle timeouts gracefully... */ 129 /* handle timeouts gracefully... */
112 alarm (timeout_interval); 130 alarm(timeout_interval);
113 131
132 mp_check overall = mp_check_init();
114 /* if they want to run apt-get update first... */ 133 /* if they want to run apt-get update first... */
115 if(do_update) result = run_update(); 134 if (config.do_update) {
135 run_update_result update_result = run_update(config.update_opts);
136
137 mp_add_subcheck_to_check(&overall, update_result.sc);
138 }
116 139
117 /* apt-get upgrade */ 140 /* apt-get upgrade */
118 result = max_state(result, run_upgrade(&packages_available, &sec_count, &packages_list, &secpackages_list)); 141 run_upgrade_result upgrad_res =
119 142 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical,
120 if(sec_count > 0){ 143 config.upgrade_opts, config.input_filename);
121 result = max_state(result, STATE_CRITICAL); 144
122 } else if(packages_available >= packages_warning && only_critical == false){ 145 mp_subcheck sc_run_upgrade = mp_subcheck_init();
123 result = max_state(result, STATE_WARNING); 146 if (upgrad_res.errorcode == OK) {
124 } else if(result > STATE_UNKNOWN){ 147 sc_run_upgrade = mp_set_subcheck_state(sc_run_upgrade, STATE_OK);
125 result = STATE_UNKNOWN; 148 }
149 xasprintf(&sc_run_upgrade.output, "Executed apt upgrade (dry run)");
150
151 mp_add_subcheck_to_check(&overall, sc_run_upgrade);
152
153 size_t packages_available = upgrad_res.package_count;
154 size_t number_of_security_updates = upgrad_res.security_package_count;
155 char **packages_list = upgrad_res.packages_list;
156 char **secpackages_list = upgrad_res.secpackages_list;
157
158 mp_perfdata pd_security_updates = perfdata_init();
159 pd_security_updates.value = mp_create_pd_value(number_of_security_updates);
160 pd_security_updates.label = "critical_updates";
161
162 mp_subcheck sc_security_updates = mp_subcheck_init();
163 xasprintf(&sc_security_updates.output, "Security updates available: %zu",
164 number_of_security_updates);
165 mp_add_perfdata_to_subcheck(&sc_security_updates, pd_security_updates);
166
167 if (number_of_security_updates > 0) {
168 sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_CRITICAL);
169 } else {
170 sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_OK);
171 }
172
173 mp_perfdata pd_other_updates = perfdata_init();
174 pd_other_updates.value = mp_create_pd_value(packages_available);
175 pd_other_updates.label = "available_upgrades";
176
177 mp_subcheck sc_other_updates = mp_subcheck_init();
178
179 xasprintf(&sc_other_updates.output, "Updates available: %zu", packages_available);
180 sc_other_updates = mp_set_subcheck_default_state(sc_other_updates, STATE_OK);
181 mp_add_perfdata_to_subcheck(&sc_other_updates, pd_other_updates);
182
183 if (packages_available >= config.packages_warning && !config.only_critical) {
184 sc_other_updates = mp_set_subcheck_state(sc_other_updates, STATE_WARNING);
126 } 185 }
127 186
128 printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), 187 if (config.list) {
129 state_text(result), 188 qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp);
130 packages_available, 189 qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *),
131 (upgrade==DIST_UPGRADE)?"dist-upgrade":"upgrade", 190 cmpstringp);
132 sec_count, 191
133 (stderr_warning)?" warnings detected":"", 192 for (size_t i = 0; i < number_of_security_updates; i++) {
134 (stderr_warning && exec_warning)?",":"", 193 xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output,
135 (exec_warning)?" errors detected":"", 194 secpackages_list[i]);
136 (stderr_warning||exec_warning)?".":"", 195 }
137 packages_available, 196
138 sec_count 197 if (!config.only_critical) {
139 ); 198 for (size_t i = 0; i < packages_available - number_of_security_updates; i++) {
140 199 xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output,
141 if(list) { 200 packages_list[i]);
142 qsort(secpackages_list, sec_count, sizeof(char*), cmpstringp); 201 }
143 qsort(packages_list, packages_available-sec_count, sizeof(char*), cmpstringp);
144
145 for(int i = 0; i < sec_count; i++)
146 printf("%s (security)\n", secpackages_list[i]);
147
148 if (only_critical == false) {
149 for(int i = 0; i < packages_available - sec_count; i++)
150 printf("%s\n", packages_list[i]);
151 } 202 }
152 } 203 }
204 mp_add_subcheck_to_check(&overall, sc_security_updates);
205 mp_add_subcheck_to_check(&overall, sc_other_updates);
153 206
154 return result; 207 mp_exit(overall);
155} 208}
156 209
157/* process command-line arguments */ 210/* process command-line arguments */
158int process_arguments (int argc, char **argv) { 211check_apt_config_wrapper process_arguments(int argc, char **argv) {
159 int c; 212 enum {
160 213 /* Character for hidden input file option (for testing). */
161 static struct option longopts[] = { 214 INPUT_FILE_OPT = CHAR_MAX + 1,
162 {"version", no_argument, 0, 'V'}, 215 output_format_index,
163 {"help", no_argument, 0, 'h'}, 216 };
164 {"verbose", no_argument, 0, 'v'}, 217 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
165 {"timeout", required_argument, 0, 't'}, 218 {"help", no_argument, 0, 'h'},
166 {"update", optional_argument, 0, 'u'}, 219 {"verbose", no_argument, 0, 'v'},
167 {"upgrade", optional_argument, 0, 'U'}, 220 {"timeout", required_argument, 0, 't'},
168 {"no-upgrade", no_argument, 0, 'n'}, 221 {"update", optional_argument, 0, 'u'},
169 {"dist-upgrade", optional_argument, 0, 'd'}, 222 {"upgrade", optional_argument, 0, 'U'},
170 {"list", no_argument, false, 'l'}, 223 {"no-upgrade", no_argument, 0, 'n'},
171 {"include", required_argument, 0, 'i'}, 224 {"dist-upgrade", optional_argument, 0, 'd'},
172 {"exclude", required_argument, 0, 'e'}, 225 {"list", no_argument, 0, 'l'},
173 {"critical", required_argument, 0, 'c'}, 226 {"include", required_argument, 0, 'i'},
174 {"only-critical", no_argument, 0, 'o'}, 227 {"exclude", required_argument, 0, 'e'},
175 {"input-file", required_argument, 0, INPUT_FILE_OPT}, 228 {"critical", required_argument, 0, 'c'},
176 {"packages-warning", required_argument, 0, 'w'}, 229 {"only-critical", no_argument, 0, 'o'},
177 {0, 0, 0, 0} 230 {"input-file", required_argument, 0, INPUT_FILE_OPT},
231 {"packages-warning", required_argument, 0, 'w'},
232 {"output-format", required_argument, 0, output_format_index},
233 {0, 0, 0, 0}};
234
235 check_apt_config_wrapper result = {
236 .errorcode = OK,
237 .config = check_apt_config_init(),
178 }; 238 };
179 239
180 while(1) { 240 while (true) {
181 c = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL); 241 int option_char = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL);
182 242
183 if(c == -1 || c == EOF || c == 1) break; 243 if (option_char == -1 || option_char == EOF || option_char == 1) {
244 break;
245 }
184 246
185 switch(c) { 247 switch (option_char) {
186 case 'h': 248 case 'h':
187 print_help(); 249 print_help();
188 exit(STATE_UNKNOWN); 250 exit(STATE_UNKNOWN);
@@ -193,124 +255,153 @@ int process_arguments (int argc, char **argv) {
193 verbose++; 255 verbose++;
194 break; 256 break;
195 case 't': 257 case 't':
196 timeout_interval=atoi(optarg); 258 timeout_interval = atoi(optarg);
197 break; 259 break;
198 case 'd': 260 case 'd':
199 upgrade=DIST_UPGRADE; 261 result.config.upgrade = DIST_UPGRADE;
200 if(optarg!=NULL){ 262 if (optarg != NULL) {
201 upgrade_opts=strdup(optarg); 263 result.config.upgrade_opts = strdup(optarg);
202 if(upgrade_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 264 if (result.config.upgrade_opts == NULL) {
265 die(STATE_UNKNOWN, "strdup failed");
266 }
203 } 267 }
204 break; 268 break;
205 case 'U': 269 case 'U':
206 upgrade=UPGRADE; 270 result.config.upgrade = UPGRADE;
207 if(optarg!=NULL){ 271 if (optarg != NULL) {
208 upgrade_opts=strdup(optarg); 272 result.config.upgrade_opts = strdup(optarg);
209 if(upgrade_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 273 if (result.config.upgrade_opts == NULL) {
274 die(STATE_UNKNOWN, "strdup failed");
275 }
210 } 276 }
211 break; 277 break;
212 case 'n': 278 case 'n':
213 upgrade=NO_UPGRADE; 279 result.config.upgrade = NO_UPGRADE;
214 break; 280 break;
215 case 'u': 281 case 'u':
216 do_update=true; 282 result.config.do_update = true;
217 if(optarg!=NULL){ 283 if (optarg != NULL) {
218 update_opts=strdup(optarg); 284 result.config.update_opts = strdup(optarg);
219 if(update_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 285 if (result.config.update_opts == NULL) {
286 die(STATE_UNKNOWN, "strdup failed");
287 }
220 } 288 }
221 break; 289 break;
222 case 'l': 290 case 'l':
223 list=true; 291 result.config.list = true;
224 break; 292 break;
225 case 'i': 293 case 'i':
226 do_include=add_to_regexp(do_include, optarg); 294 result.config.do_include = add_to_regexp(result.config.do_include, optarg);
227 break; 295 break;
228 case 'e': 296 case 'e':
229 do_exclude=add_to_regexp(do_exclude, optarg); 297 result.config.do_exclude = add_to_regexp(result.config.do_exclude, optarg);
230 break; 298 break;
231 case 'c': 299 case 'c':
232 do_critical=add_to_regexp(do_critical, optarg); 300 result.config.do_critical = add_to_regexp(result.config.do_critical, optarg);
233 break; 301 break;
234 case 'o': 302 case 'o':
235 only_critical=true; 303 result.config.only_critical = true;
236 break; 304 break;
237 case INPUT_FILE_OPT: 305 case INPUT_FILE_OPT:
238 input_filename = optarg; 306 result.config.input_filename = optarg;
239 break; 307 break;
240 case 'w': 308 case 'w':
241 packages_warning = atoi(optarg); 309 result.config.packages_warning = atoi(optarg);
310 break;
311 case output_format_index: {
312 parsed_output_format parser = mp_parse_output_format(optarg);
313 if (!parser.parsing_success) {
314 // TODO List all available formats here, maybe add anothoer usage function
315 printf("Invalid output format: %s\n", optarg);
316 exit(STATE_UNKNOWN);
317 }
318
319 result.config.output_format_is_set = true;
320 result.config.output_format = parser.output_format;
242 break; 321 break;
322 }
243 default: 323 default:
244 /* print short usage statement if args not parsable */ 324 /* print short usage statement if args not parsable */
245 usage5(); 325 usage5();
246 } 326 }
247 } 327 }
248 328
249 return OK; 329 return result;
250} 330}
251 331
252
253/* run an apt-get upgrade */ 332/* run an apt-get upgrade */
254int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkglist){ 333run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include,
255 int result=STATE_UNKNOWN, regres=0, pc=0, spc=0; 334 const char *do_exclude, const char *do_critical,
256 struct output chld_out, chld_err; 335 const char *upgrade_opts, const char *input_filename) {
257 regex_t ireg, ereg, sreg; 336 regex_t exclude_regex;
258 char *cmdline=NULL, rerrbuf[64];
259
260 /* initialize ereg as it is possible it is printed while uninitialized */ 337 /* initialize ereg as it is possible it is printed while uninitialized */
261 memset(&ereg, '\0', sizeof(ereg.buffer)); 338 memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer));
262 339
263 if(upgrade==NO_UPGRADE) return STATE_OK; 340 run_upgrade_result result = {
341 .errorcode = OK,
342 };
264 343
344 if (upgrade == NO_UPGRADE) {
345 result.errorcode = OK;
346 return result;
347 }
348
349 int regres = 0;
350 regex_t include_regex;
351 char rerrbuf[64];
265 /* compile the regexps */ 352 /* compile the regexps */
266 if (do_include != NULL) { 353 if (do_include != NULL) {
267 regres=regcomp(&ireg, do_include, REG_EXTENDED); 354 regres = regcomp(&include_regex, do_include, REG_EXTENDED);
268 if (regres!=0) { 355 if (regres != 0) {
269 regerror(regres, &ireg, rerrbuf, 64); 356 regerror(regres, &include_regex, rerrbuf, 64);
270 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 357 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
271 } 358 }
272 } 359 }
273 360
274 if(do_exclude!=NULL){ 361 if (do_exclude != NULL) {
275 regres=regcomp(&ereg, do_exclude, REG_EXTENDED); 362 regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED);
276 if(regres!=0) { 363 if (regres != 0) {
277 regerror(regres, &ereg, rerrbuf, 64); 364 regerror(regres, &exclude_regex, rerrbuf, 64);
278 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), 365 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
279 progname, rerrbuf);
280 } 366 }
281 } 367 }
282 368
369 regex_t sreg;
283 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; 370 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE;
284 regres=regcomp(&sreg, crit_ptr, REG_EXTENDED); 371 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED);
285 if(regres!=0) { 372 if (regres != 0) {
286 regerror(regres, &ereg, rerrbuf, 64); 373 regerror(regres, &exclude_regex, rerrbuf, 64);
287 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), 374 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
288 progname, rerrbuf);
289 } 375 }
290 376
291 cmdline=construct_cmdline(upgrade, upgrade_opts); 377 output chld_out;
378 output chld_err;
379 char *cmdline = NULL;
380 cmdline = construct_cmdline(upgrade, upgrade_opts);
292 if (input_filename != NULL) { 381 if (input_filename != NULL) {
293 /* read input from a file for testing */ 382 /* read input from a file for testing */
294 result = cmd_file_read(input_filename, &chld_out, 0); 383 result.errorcode = cmd_file_read(input_filename, &chld_out, 0);
295 } else { 384 } else {
296 /* run the upgrade */ 385 /* run the upgrade */
297 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 386 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0);
298 } 387 }
299 388
300 /* apt-get upgrade only changes exit status if there is an 389 // apt-get upgrade only changes exit status if there is an
301 * internal error when run in dry-run mode. therefore we will 390 // internal error when run in dry-run mode.
302 * treat such an error as UNKNOWN */ 391 if (result.errorcode != 0) {
303 if(result != 0){ 392 result.exec_warning = true;
304 exec_warning=1; 393 result.errorcode = ERROR;
305 result = STATE_UNKNOWN; 394 // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
306 fprintf(stderr, _("'%s' exited with non-zero status.\n"),
307 cmdline);
308 } 395 }
309 396
310 *pkglist=malloc(sizeof(char *) * chld_out.lines); 397 char **pkglist = malloc(sizeof(char *) * chld_out.lines);
311 if(!pkglist) die(STATE_UNKNOWN, "malloc failed!\n"); 398 if (!pkglist) {
312 *secpkglist=malloc(sizeof(char *) * chld_out.lines); 399 die(STATE_UNKNOWN, "malloc failed!\n");
313 if(!secpkglist) die(STATE_UNKNOWN, "malloc failed!\n"); 400 }
401 char **secpkglist = malloc(sizeof(char *) * chld_out.lines);
402 if (!secpkglist) {
403 die(STATE_UNKNOWN, "malloc failed!\n");
404 }
314 405
315 /* parse the output, which should only consist of lines like 406 /* parse the output, which should only consist of lines like
316 * 407 *
@@ -321,242 +412,293 @@ int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkg
321 * we may need to switch to the --print-uris output format, 412 * we may need to switch to the --print-uris output format,
322 * in which case the logic here will slightly change. 413 * in which case the logic here will slightly change.
323 */ 414 */
324 for(size_t i = 0; i < chld_out.lines; i++) { 415 size_t package_counter = 0;
325 if(verbose){ 416 size_t security_package_counter = 0;
417 for (size_t i = 0; i < chld_out.lines; i++) {
418 if (verbose) {
326 printf("%s\n", chld_out.line[i]); 419 printf("%s\n", chld_out.line[i]);
327 } 420 }
421
328 /* if it is a package we care about */ 422 /* if it is a package we care about */
329 if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 && 423 if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 &&
330 (do_include == NULL || regexec(&ireg, chld_out.line[i], 0, NULL, 0) == 0)) { 424 (do_include == NULL || regexec(&include_regex, chld_out.line[i], 0, NULL, 0) == 0)) {
331 /* if we're not excluding, or it's not in the 425 /* if we're not excluding, or it's not in the
332 * list of stuff to exclude */ 426 * list of stuff to exclude */
333 if(do_exclude==NULL || 427 if (do_exclude == NULL || regexec(&exclude_regex, chld_out.line[i], 0, NULL, 0) != 0) {
334 regexec(&ereg, chld_out.line[i], 0, NULL, 0)!=0){ 428 package_counter++;
335 pc++; 429 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) {
336 if(regexec(&sreg, chld_out.line[i], 0, NULL, 0)==0){ 430 security_package_counter++;
337 spc++; 431
338 if(verbose) printf("*"); 432 if (verbose) {
339 (*secpkglist)[spc-1] = pkg_name(chld_out.line[i]); 433 printf("*");
434 }
435
436 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]);
340 } else { 437 } else {
341 (*pkglist)[pc-spc-1] = pkg_name(chld_out.line[i]); 438 (pkglist)[package_counter - security_package_counter - 1] =
439 pkg_name(chld_out.line[i]);
342 } 440 }
343 if(verbose){ 441 if (verbose) {
344 printf("*%s\n", chld_out.line[i]); 442 printf("*%s\n", chld_out.line[i]);
345 } 443 }
346 } 444 }
347 } 445 }
348 } 446 }
349 *pkgcount=pc; 447
350 *secpkgcount=spc; 448 result.package_count = package_counter;
449 result.security_package_count = security_package_counter;
450 result.packages_list = pkglist;
451 result.secpackages_list = secpkglist;
351 452
352 /* If we get anything on stderr, at least set warning */ 453 /* If we get anything on stderr, at least set warning */
353 if (input_filename == NULL && chld_err.buflen) { 454 if (input_filename == NULL && chld_err.buflen) {
354 stderr_warning=1; 455 stderr_warning = true;
355 result = max_state(result, STATE_WARNING); 456 result.errorcode = ERROR;
356 if(verbose){ 457
357 for(size_t i = 0; i < chld_err.lines; i++) { 458 if (verbose) {
459 for (size_t i = 0; i < chld_err.lines; i++) {
358 fprintf(stderr, "%s\n", chld_err.line[i]); 460 fprintf(stderr, "%s\n", chld_err.line[i]);
359 } 461 }
360 } 462 }
361 } 463 }
362 if (do_include != NULL) regfree(&ireg); 464
465 if (do_include != NULL) {
466 regfree(&include_regex);
467 }
468
363 regfree(&sreg); 469 regfree(&sreg);
364 if(do_exclude!=NULL) regfree(&ereg); 470
471 if (do_exclude != NULL) {
472 regfree(&exclude_regex);
473 }
474
365 free(cmdline); 475 free(cmdline);
476
366 return result; 477 return result;
367} 478}
368 479
369/* run an apt-get update (needs root) */ 480/* run an apt-get update (needs root) */
370int run_update(void){ 481run_update_result run_update(char *update_opts) {
371 int result=STATE_UNKNOWN;
372 struct output chld_out, chld_err;
373 char *cmdline; 482 char *cmdline;
374
375 /* run the update */ 483 /* run the update */
376 cmdline = construct_cmdline(NO_UPGRADE, update_opts); 484 cmdline = construct_cmdline(NO_UPGRADE, update_opts);
377 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 485
486 run_update_result result = {
487 .exec_warning = false,
488 .stderr_warning = false,
489 .sc = mp_subcheck_init(),
490 };
491
492 result.sc = mp_set_subcheck_default_state(result.sc, STATE_OK);
493 xasprintf(&result.sc.output, "executing '%s' first", cmdline);
494
495 output chld_out;
496 output chld_err;
497 int cmd_error = np_runcmd(cmdline, &chld_out, &chld_err, 0);
378 /* apt-get update changes exit status if it can't fetch packages. 498 /* apt-get update changes exit status if it can't fetch packages.
379 * since we were explicitly asked to do so, this is treated as 499 * since we were explicitly asked to do so, this is treated as
380 * a critical error. */ 500 * a critical error. */
381 if(result != 0){ 501 if (cmd_error != 0) {
382 exec_warning=1; 502 exec_warning = true;
383 result = STATE_CRITICAL; 503 result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL);
384 fprintf(stderr, _("'%s' exited with non-zero status.\n"), 504 xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline);
385 cmdline);
386 } 505 }
387 506
388 if(verbose){ 507 if (verbose) {
389 for(size_t i = 0; i < chld_out.lines; i++) { 508 for (size_t i = 0; i < chld_out.lines; i++) {
390 printf("%s\n", chld_out.line[i]); 509 printf("%s\n", chld_out.line[i]);
391 } 510 }
392 } 511 }
393 512
394 /* If we get anything on stderr, at least set warning */ 513 /* If we get anything on stderr, at least set warning */
395 if(chld_err.buflen){ 514 if (chld_err.buflen) {
396 stderr_warning=1; 515 stderr_warning = true;
397 result = max_state(result, STATE_WARNING); 516 result.sc = mp_set_subcheck_state(
398 if(verbose){ 517 result.sc, max_state(mp_compute_subcheck_state(result.sc), STATE_WARNING));
399 for(size_t i = 0; i < chld_err.lines; i++) { 518 if (verbose) {
519 for (size_t i = 0; i < chld_err.lines; i++) {
400 fprintf(stderr, "%s\n", chld_err.line[i]); 520 fprintf(stderr, "%s\n", chld_err.line[i]);
401 } 521 }
402 } 522 }
403 } 523 }
524
404 free(cmdline); 525 free(cmdline);
526
405 return result; 527 return result;
406} 528}
407 529
408char* pkg_name(char *line){ 530char *pkg_name(char *line) {
409 char *start=NULL, *space=NULL, *pkg=NULL; 531 char *start = line + strlen(PKGINST_PREFIX);
410 int len=0;
411 532
412 start = line + strlen(PKGINST_PREFIX); 533 size_t len = strlen(start);
413 len = strlen(start);
414 534
415 space = index(start, ' '); 535 char *space = index(start, ' ');
416 if(space!=NULL){ 536 if (space != NULL) {
417 len = space - start; 537 len = space - start;
418 } 538 }
419 539
420 pkg=malloc(sizeof(char)*(len+1)); 540 char *pkg = malloc(sizeof(char) * (len + 1));
421 if(!pkg) die(STATE_UNKNOWN, "malloc failed!\n"); 541 if (!pkg) {
542 die(STATE_UNKNOWN, "malloc failed!\n");
543 }
422 544
423 strncpy(pkg, start, len); 545 strncpy(pkg, start, len);
424 pkg[len]='\0'; 546 pkg[len] = '\0';
425 547
426 return pkg; 548 return pkg;
427} 549}
428 550
429int cmpstringp(const void *p1, const void *p2){ 551int cmpstringp(const void *left_string, const void *right_string) {
430 return strcmp(* (char * const *) p1, * (char * const *) p2); 552 return strcmp(*(char *const *)left_string, *(char *const *)right_string);
431} 553}
432 554
433char* add_to_regexp(char *expr, const char *next){ 555char *add_to_regexp(char *expr, const char *next) {
434 char *re=NULL; 556 char *regex_string = NULL;
435 557
436 if(expr==NULL){ 558 if (expr == NULL) {
437 re=malloc(sizeof(char)*(strlen("()")+strlen(next)+1)); 559 regex_string = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1));
438 if(!re) die(STATE_UNKNOWN, "malloc failed!\n"); 560 if (!regex_string) {
439 sprintf(re, "(%s)", next); 561 die(STATE_UNKNOWN, "malloc failed!\n");
562 }
563 sprintf(regex_string, "(%s)", next);
440 } else { 564 } else {
441 /* resize it, adding an extra char for the new '|' separator */ 565 /* resize it, adding an extra char for the new '|' separator */
442 re=realloc(expr, sizeof(char)*(strlen(expr)+1+strlen(next)+1)); 566 regex_string = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1));
443 if(!re) die(STATE_UNKNOWN, "realloc failed!\n"); 567 if (!regex_string) {
568 die(STATE_UNKNOWN, "realloc failed!\n");
569 }
444 /* append it starting at ')' in the old re */ 570 /* append it starting at ')' in the old re */
445 sprintf((char*)(re+strlen(re)-1), "|%s)", next); 571 sprintf((char *)(regex_string + strlen(regex_string) - 1), "|%s)", next);
446 } 572 }
447 573
448 return re; 574 return regex_string;
449} 575}
450 576
451char* construct_cmdline(upgrade_type u, const char *opts){ 577char *construct_cmdline(upgrade_type upgrade, const char *opts) {
452 int len=0; 578 const char *opts_ptr = NULL;
453 const char *opts_ptr=NULL, *aptcmd=NULL; 579 const char *aptcmd = NULL;
454 char *cmd=NULL;
455 580
456 switch(u){ 581 switch (upgrade) {
457 case UPGRADE: 582 case UPGRADE:
458 if(opts==NULL) opts_ptr=UPGRADE_DEFAULT_OPTS; 583 if (opts == NULL) {
459 else opts_ptr=opts; 584 opts_ptr = UPGRADE_DEFAULT_OPTS;
460 aptcmd="upgrade"; 585 } else {
586 opts_ptr = opts;
587 }
588 aptcmd = "upgrade";
461 break; 589 break;
462 case DIST_UPGRADE: 590 case DIST_UPGRADE:
463 if(opts==NULL) opts_ptr=UPGRADE_DEFAULT_OPTS; 591 if (opts == NULL) {
464 else opts_ptr=opts; 592 opts_ptr = UPGRADE_DEFAULT_OPTS;
465 aptcmd="dist-upgrade"; 593 } else {
594 opts_ptr = opts;
595 }
596 aptcmd = "dist-upgrade";
466 break; 597 break;
467 case NO_UPGRADE: 598 case NO_UPGRADE:
468 if(opts==NULL) opts_ptr=UPDATE_DEFAULT_OPTS; 599 if (opts == NULL) {
469 else opts_ptr=opts; 600 opts_ptr = UPDATE_DEFAULT_OPTS;
470 aptcmd="update"; 601 } else {
602 opts_ptr = opts;
603 }
604 aptcmd = "update";
471 break; 605 break;
472 } 606 }
473 607
474 len+=strlen(PATH_TO_APTGET)+1; /* "/usr/bin/apt-get " */ 608 size_t len = 0;
475 len+=strlen(opts_ptr)+1; /* "opts " */ 609 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */
476 len+=strlen(aptcmd)+1; /* "upgrade\0" */ 610 len += strlen(opts_ptr) + 1; /* "opts " */
611 len += strlen(aptcmd) + 1; /* "upgrade\0" */
477 612
478 cmd=(char*)malloc(sizeof(char)*len); 613 char *cmd = (char *)malloc(sizeof(char) * len);
479 if(cmd==NULL) die(STATE_UNKNOWN, "malloc failed"); 614 if (cmd == NULL) {
615 die(STATE_UNKNOWN, "malloc failed");
616 }
480 sprintf(cmd, "%s %s %s", PATH_TO_APTGET, opts_ptr, aptcmd); 617 sprintf(cmd, "%s %s %s", PATH_TO_APTGET, opts_ptr, aptcmd);
481 return cmd; 618 return cmd;
482} 619}
483 620
484/* informative help message */ 621/* informative help message */
485void 622void print_help(void) {
486print_help (void) 623 print_revision(progname, NP_VERSION);
487{ 624
488 print_revision(progname, NP_VERSION); 625 printf(_(COPYRIGHT), copyright, email);
489 626
490 printf(_(COPYRIGHT), copyright, email); 627 printf("%s\n", _("This plugin checks for software updates on systems that use"));
491 628 printf("%s\n", _("package management systems based on the apt-get(8) command"));
492 printf("%s\n", _("This plugin checks for software updates on systems that use")); 629 printf("%s\n", _("found in Debian GNU/Linux"));
493 printf("%s\n", _("package management systems based on the apt-get(8) command")); 630
494 printf("%s\n", _("found in Debian GNU/Linux")); 631 printf("\n\n");
495 632
496 printf ("\n\n"); 633 print_usage();
497 634
498 print_usage(); 635 printf(UT_HELP_VRSN);
499 636 printf(UT_EXTRA_OPTS);
500 printf(UT_HELP_VRSN); 637
501 printf(UT_EXTRA_OPTS); 638 printf(UT_PLUG_TIMEOUT, timeout_interval);
502 639
503 printf(UT_PLUG_TIMEOUT, timeout_interval); 640 printf(" %s\n", "-n, --no-upgrade");
504 641 printf(" %s\n", _("Do not run the upgrade. Probably not useful (without -u at least)."));
505 printf (" %s\n", "-n, --no-upgrade"); 642 printf(" %s\n", "-l, --list");
506 printf (" %s\n", _("Do not run the upgrade. Probably not useful (without -u at least).")); 643 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by"));
507 printf (" %s\n", "-l, --list"); 644 printf(" %s\n", _("name with security packages listed first."));
508 printf (" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); 645 printf(" %s\n", "-i, --include=REGEXP");
509 printf (" %s\n", _("name with security packages listed first.")); 646 printf(" %s\n",
510 printf (" %s\n", "-i, --include=REGEXP"); 647 _("Include only packages matching REGEXP. Can be specified multiple times"));
511 printf (" %s\n", _("Include only packages matching REGEXP. Can be specified multiple times")); 648 printf(" %s\n", _("the values will be combined together. Any packages matching this list"));
512 printf (" %s\n", _("the values will be combined together. Any packages matching this list")); 649 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored."));
513 printf (" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); 650 printf(" %s\n", _("Default is to include all packages."));
514 printf (" %s\n", _("Default is to include all packages.")); 651 printf(" %s\n", "-e, --exclude=REGEXP");
515 printf (" %s\n", "-e, --exclude=REGEXP"); 652 printf(" %s\n", _("Exclude packages matching REGEXP from the list of packages that would"));
516 printf (" %s\n", _("Exclude packages matching REGEXP from the list of packages that would")); 653 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values"));
517 printf (" %s\n", _("otherwise be included. Can be specified multiple times; the values")); 654 printf(" %s\n", _("will be combined together. Default is to exclude no packages."));
518 printf (" %s\n", _("will be combined together. Default is to exclude no packages.")); 655 printf(" %s\n", "-c, --critical=REGEXP");
519 printf (" %s\n", "-c, --critical=REGEXP"); 656 printf(" %s\n",
520 printf (" %s\n", _("If the full package information of any of the upgradable packages match")); 657 _("If the full package information of any of the upgradable packages match"));
521 printf (" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified")); 658 printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified"));
522 printf (" %s\n", _("multiple times like above. Default is a regexp matching security")); 659 printf(" %s\n", _("multiple times like above. Default is a regexp matching security"));
523 printf (" %s\n", _("upgrades for Debian and Ubuntu:")); 660 printf(" %s\n", _("upgrades for Debian and Ubuntu:"));
524 printf (" \t%s\n", SECURITY_RE); 661 printf(" \t%s\n", SECURITY_RE);
525 printf (" %s\n", _("Note that the package must first match the include list before its")); 662 printf(" %s\n", _("Note that the package must first match the include list before its"));
526 printf (" %s\n", _("information is compared against the critical list.")); 663 printf(" %s\n", _("information is compared against the critical list."));
527 printf (" %s\n", "-o, --only-critical"); 664 printf(" %s\n", "-o, --only-critical");
528 printf (" %s\n", _("Only warn about upgrades matching the critical list. The total number")); 665 printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number"));
529 printf (" %s\n", _("of upgrades will be printed, but any non-critical upgrades will not cause")); 666 printf(" %s\n",
530 printf (" %s\n", _("the plugin to return WARNING status.")); 667 _("of upgrades will be printed, but any non-critical upgrades will not cause"));
531 printf (" %s\n", "-w, --packages-warning"); 668 printf(" %s\n", _("the plugin to return WARNING status."));
532 printf (" %s\n", _("Minimum number of packages available for upgrade to return WARNING status.")); 669 printf(" %s\n", "-w, --packages-warning");
533 printf (" %s\n\n", _("Default is 1 package.")); 670 printf(" %s\n",
534 671 _("Minimum number of packages available for upgrade to return WARNING status."));
535 printf ("%s\n\n", _("The following options require root privileges and should be used with care:")); 672 printf(" %s\n\n", _("Default is 1 package."));
536 printf (" %s\n", "-u, --update=OPTS"); 673
537 printf (" %s\n", _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); 674 printf(UT_OUTPUT_FORMAT);
538 printf (" %s\n", _("the default options. Note: you may also need to adjust the global")); 675
539 printf (" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); 676 printf("%s\n\n",
540 printf (" %s\n", _("upgrade is expected to take longer than the default timeout.")); 677 _("The following options require root privileges and should be used with care:"));
541 printf (" %s\n", "-U, --upgrade=OPTS"); 678 printf(" %s\n", "-u, --update=OPTS");
542 printf (" %s\n", _("Perform an upgrade. If an optional OPTS argument is provided,")); 679 printf(" %s\n",
543 printf (" %s\n", _("apt-get will be run with these command line options instead of the")); 680 _("First perform an 'apt-get update'. An optional OPTS parameter overrides"));
544 printf (" %s", _("default ")); 681 printf(" %s\n", _("the default options. Note: you may also need to adjust the global"));
545 printf ("(%s).\n", UPGRADE_DEFAULT_OPTS); 682 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get"));
546 printf (" %s\n", _("Note that you may be required to have root privileges if you do not use")); 683 printf(" %s\n", _("upgrade is expected to take longer than the default timeout."));
547 printf (" %s\n", _("the default options, which will only run a simulation and NOT perform the upgrade")); 684 printf(" %s\n", "-U, --upgrade=OPTS");
548 printf (" %s\n", "-d, --dist-upgrade=OPTS"); 685 printf(" %s\n", _("Perform an upgrade. If an optional OPTS argument is provided,"));
549 printf (" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); 686 printf(" %s\n", _("apt-get will be run with these command line options instead of the"));
550 printf (" %s\n", _("can be provided to override the default options.")); 687 printf(" %s", _("default "));
551 688 printf("(%s).\n", UPGRADE_DEFAULT_OPTS);
552 printf(UT_SUPPORT); 689 printf(" %s\n",
690 _("Note that you may be required to have root privileges if you do not use"));
691 printf(" %s\n",
692 _("the default options, which will only run a simulation and NOT perform the upgrade"));
693 printf(" %s\n", "-d, --dist-upgrade=OPTS");
694 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS"));
695 printf(" %s\n", _("can be provided to override the default options."));
696
697 printf(UT_SUPPORT);
553} 698}
554 699
555
556/* simple usage heading */ 700/* simple usage heading */
557void 701void print_usage(void) {
558print_usage(void) 702 printf("%s\n", _("Usage:"));
559{ 703 printf("%s [[-d|-u|-U]opts] [-n] [-l] [-t timeout] [-w packages-warning]\n", progname);
560 printf ("%s\n", _("Usage:"));
561 printf ("%s [[-d|-u|-U]opts] [-n] [-l] [-t timeout] [-w packages-warning]\n", progname);
562} 704}
diff --git a/plugins/check_apt.d/config.h b/plugins/check_apt.d/config.h
new file mode 100644
index 00000000..e4d622f1
--- /dev/null
+++ b/plugins/check_apt.d/config.h
@@ -0,0 +1,46 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include "../lib/output.h"
6
7/* some constants */
8typedef enum {
9 UPGRADE,
10 DIST_UPGRADE,
11 NO_UPGRADE
12} upgrade_type;
13
14typedef struct {
15 bool do_update; /* whether to call apt-get update */
16 upgrade_type upgrade; /* which type of upgrade to do */
17 bool only_critical; /* whether to warn about non-critical updates */
18 bool list; /* list packages available for upgrade */
19 /* number of packages available for upgrade to return WARNING status */
20 size_t packages_warning;
21
22 char *upgrade_opts; /* options to override defaults for upgrade */
23 char *update_opts; /* options to override defaults for update */
24 char *do_include; /* regexp to only include certain packages */
25 char *do_exclude; /* regexp to only exclude certain packages */
26 char *do_critical; /* regexp specifying critical packages */
27 char *input_filename; /* input filename for testing */
28
29 bool output_format_is_set;
30 mp_output_format output_format;
31} check_apt_config;
32
33check_apt_config check_apt_config_init() {
34 check_apt_config tmp = {.do_update = false,
35 .upgrade = UPGRADE,
36 .only_critical = false,
37 .list = false,
38 .packages_warning = 1,
39 .update_opts = NULL,
40 .do_include = NULL,
41 .do_exclude = NULL,
42 .do_critical = NULL,
43 .input_filename = NULL,
44 .output_format_is_set = false};
45 return tmp;
46}
diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c
index 2a23b397..4d0c8e7d 100644
--- a/plugins/check_by_ssh.c
+++ b/plugins/check_by_ssh.c
@@ -1,186 +1,259 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_by_ssh plugin 3 * Monitoring check_by_ssh plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2008 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_by_ssh plugin 10 * This file contains the check_by_ssh plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 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 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 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 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/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28
29const char *progname = "check_by_ssh";
30const char *copyright = "2000-2008";
31const char *email = "devel@monitoring-plugins.org";
32 28
33#include "common.h" 29#include "common.h"
30#include "output.h"
34#include "utils.h" 31#include "utils.h"
35#include "netutils.h"
36#include "utils_cmd.h" 32#include "utils_cmd.h"
33#include "check_by_ssh.d/config.h"
34#include "states.h"
35
36const char *progname = "check_by_ssh";
37const char *copyright = "2000-2024";
38const char *email = "devel@monitoring-plugins.org";
37 39
38#ifndef NP_MAXARGS 40#ifndef NP_MAXARGS
39#define NP_MAXARGS 1024 41# define NP_MAXARGS 1024
40#endif 42#endif
41 43
42int process_arguments (int, char **); 44char *check_by_ssh_output_override(void *remote_output) { return ((char *)remote_output); }
43int validate_arguments (void);
44void comm_append (const char *);
45void print_help (void);
46void print_usage (void);
47
48unsigned int commands = 0;
49unsigned int services = 0;
50int skip_stdout = 0;
51int skip_stderr = 0;
52int warn_on_stderr = 0;
53bool unknown_timeout = false;
54char *remotecmd = NULL;
55char **commargv = NULL;
56int commargc = 0;
57char *hostname = NULL;
58char *outputfile = NULL;
59char *host_shortname = NULL;
60char **service;
61bool passive = false;
62bool verbose = false;
63
64int
65main (int argc, char **argv)
66{
67 45
68 char *status_text; 46typedef struct {
69 int cresult; 47 int errorcode;
70 int result = STATE_UNKNOWN; 48 check_by_ssh_config config;
71 time_t local_time; 49} check_by_ssh_config_wrapper;
72 FILE *fp = NULL; 50static check_by_ssh_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
73 output chld_out, chld_err; 51static check_by_ssh_config_wrapper
52 validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/);
53
54static command_construct comm_append(command_construct /*cmd*/, const char * /*str*/);
55static void print_help(void);
56void print_usage(void);
74 57
75 remotecmd = ""; 58static bool verbose = false;
76 comm_append(SSH_COMMAND);
77 59
78 setlocale (LC_ALL, ""); 60int main(int argc, char **argv) {
79 bindtextdomain (PACKAGE, LOCALEDIR); 61 setlocale(LC_ALL, "");
80 textdomain (PACKAGE); 62 bindtextdomain(PACKAGE, LOCALEDIR);
63 textdomain(PACKAGE);
81 64
82 /* Parse extra opts if any */ 65 /* Parse extra opts if any */
83 argv=np_extra_opts (&argc, argv, progname); 66 argv = np_extra_opts(&argc, argv, progname);
67
68 check_by_ssh_config_wrapper tmp_config = process_arguments(argc, argv);
84 69
85 /* process arguments */ 70 /* process arguments */
86 if (process_arguments (argc, argv) == ERROR) 71 if (tmp_config.errorcode == ERROR) {
87 usage_va(_("Could not parse arguments")); 72 usage_va(_("Could not parse arguments"));
73 }
74
75 const check_by_ssh_config config = tmp_config.config;
76
77 if (config.output_format_is_set) {
78 mp_set_format(config.output_format);
79 }
88 80
89 /* Set signal handling and alarm timeout */ 81 /* Set signal handling and alarm timeout */
90 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 82 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
91 usage_va(_("Cannot catch SIGALRM")); 83 usage_va(_("Cannot catch SIGALRM"));
92 } 84 }
93 alarm (timeout_interval); 85 alarm(timeout_interval);
94 86
95 /* run the command */ 87 /* run the command */
96 if (verbose) { 88 if (verbose) {
97 printf ("Command: %s\n", commargv[0]); 89 printf("Command: %s\n", config.cmd.commargv[0]);
98 for (int i = 1; i < commargc; i++) 90 for (int i = 1; i < config.cmd.commargc; i++) {
99 printf ("Argument %i: %s\n", i, commargv[i]); 91 printf("Argument %i: %s\n", i, config.cmd.commargv[i]);
92 }
100 } 93 }
101 94
102 result = cmd_run_array (commargv, &chld_out, &chld_err, 0); 95 cmd_run_result child_result = cmd_run_array2(config.cmd.commargv, 0);
96 mp_check overall = mp_check_init();
103 97
104 /* SSH returns 255 if connection attempt fails; include the first line of error output */ 98 /* SSH returns 255 if connection attempt fails; include the first line of error output */
105 if (result == 255 && unknown_timeout) { 99 // we can sadly not detect other SSH errors
106 printf (_("SSH connection failed: %s\n"), 100 if (child_result.cmd_error_code == 255 && config.unknown_timeout) {
107 chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); 101 mp_subcheck sc_ssh_execution = mp_subcheck_init();
108 return STATE_UNKNOWN; 102 xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s",
103 child_result.err.lines > 0 ? child_result.err.line[0]
104 : "(no error output)");
105
106 sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN);
107 mp_add_subcheck_to_check(&overall, sc_ssh_execution);
108 mp_exit(overall);
109 } 109 }
110 110
111 if (verbose) { 111 if (verbose) {
112 for(size_t i = 0; i < chld_out.lines; i++) 112 for (size_t i = 0; i < child_result.out.lines; i++) {
113 printf("stdout: %s\n", chld_out.line[i]); 113 printf("stdout: %s\n", child_result.out.line[i]);
114 for(size_t i = 0; i < chld_err.lines; i++) 114 }
115 printf("stderr: %s\n", chld_err.line[i]); 115 for (size_t i = 0; i < child_result.err.lines; i++) {
116 printf("stderr: %s\n", child_result.err.line[i]);
117 }
116 } 118 }
117 119
118 if (skip_stdout == -1) /* --skip-stdout specified without argument */ 120 size_t skip_stdout = 0;
119 skip_stdout = chld_out.lines; 121 if (config.skip_stdout) { /* --skip-stdout specified without argument */
120 if (skip_stderr == -1) /* --skip-stderr specified without argument */ 122 skip_stdout = child_result.out.lines;
121 skip_stderr = chld_err.lines; 123 } else {
122 124 skip_stdout = config.stdout_lines_to_ignore;
123 /* UNKNOWN or worse if (non-skipped) output found on stderr */ 125 }
124 if(chld_err.lines > (size_t)skip_stderr) { 126
125 printf (_("Remote command execution failed: %s\n"), 127 size_t skip_stderr = 0;
126 chld_err.line[skip_stderr]); 128 if (config.skip_stderr) { /* --skip-stderr specified without argument */
127 if ( warn_on_stderr ) 129 skip_stderr = child_result.err.lines;
128 return max_state_alt(result, STATE_WARNING); 130 } else {
129 else 131 skip_stderr = config.sterr_lines_to_ignore;
130 return max_state_alt(result, STATE_UNKNOWN); 132 }
133
134 /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */
135 if (child_result.err.lines > skip_stderr &&
136 (config.unknown_on_stderr || config.warn_on_stderr)) {
137 mp_subcheck sc_stderr = mp_subcheck_init();
138 xasprintf(&sc_stderr.output, "remote command execution failed: %s",
139 child_result.err.line[skip_stderr]);
140
141 if (config.unknown_on_stderr) {
142 sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN);
143 }
144
145 if (config.warn_on_stderr) {
146 sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_WARNING);
147 }
148
149 mp_add_subcheck_to_check(&overall, sc_stderr);
150 // TODO still exit here?
131 } 151 }
132 152
133 /* this is simple if we're not supposed to be passive. 153 /* this is simple if we're not supposed to be passive.
134 * Wrap up quickly and keep the tricks below */ 154 * Wrap up quickly and keep the tricks below */
135 if(!passive) { 155 if (!config.passive) {
136 if (chld_out.lines > (size_t)skip_stdout) 156 mp_subcheck sc_active_check = mp_subcheck_init();
137 for (size_t i = skip_stdout; i < chld_out.lines; i++) 157 xasprintf(&sc_active_check.output, "command stdout:");
138 puts (chld_out.line[i]); 158
139 else 159 if (child_result.out.lines > skip_stdout) {
140 printf (_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), 160
141 state_text(result), remotecmd, result); 161 char *remote_command_output = NULL;
142 return result; /* return error status from remote command */ 162 for (size_t i = skip_stdout; i < child_result.out.lines; i++) {
143 } 163 if (i == skip_stdout) {
164 // first iteration
165 xasprintf(&remote_command_output, "%s", child_result.out.line[i]);
166 } else {
167 xasprintf(&remote_command_output, "%s\n%s", remote_command_output,
168 child_result.out.line[i]);
169 }
170 }
144 171
172 sc_active_check.output = remote_command_output;
173 overall.default_output_override_content = remote_command_output;
174 overall.default_output_override = check_by_ssh_output_override;
175 } else {
176 xasprintf(&sc_active_check.output, "remote command '%s' returned status %d",
177 config.remotecmd, child_result.cmd_error_code);
178 }
179
180 /* return error status from remote command */
181
182 switch (child_result.cmd_error_code) {
183 case 0:
184 sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_OK);
185 break;
186 case 1:
187 sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_WARNING);
188 break;
189 case 2:
190 sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_CRITICAL);
191 break;
192 default:
193 sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_UNKNOWN);
194 break;
195 }
196
197 mp_add_subcheck_to_check(&overall, sc_active_check);
198 mp_exit(overall);
199 }
145 200
146 /* 201 /*
147 * Passive mode 202 * Passive mode
148 */ 203 */
149 204
150 /* process output */ 205 /* process output */
151 if (!(fp = fopen (outputfile, "a"))) { 206 mp_subcheck sc_passive_file = mp_subcheck_init();
152 printf (_("SSH WARNING: could not open %s\n"), outputfile); 207 FILE *output_file = NULL;
153 exit (STATE_UNKNOWN); 208 if (!(output_file = fopen(config.outputfile, "a"))) {
209 xasprintf(&sc_passive_file.output, "could not open %s", config.outputfile);
210 sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_UNKNOWN);
211
212 mp_add_subcheck_to_check(&overall, sc_passive_file);
213 mp_exit(overall);
154 } 214 }
155 215
156 local_time = time (NULL); 216 xasprintf(&sc_passive_file.output, "opened output file %s", config.outputfile);
157 commands = 0; 217 sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_OK);
158 for(size_t i = skip_stdout; i < chld_out.lines; i++) { 218 mp_add_subcheck_to_check(&overall, sc_passive_file);
159 status_text = chld_out.line[i++]; 219
160 if (i == chld_out.lines || strstr (chld_out.line[i], "STATUS CODE: ") == NULL) 220 time_t local_time = time(NULL);
161 die (STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); 221 unsigned int commands = 0;
162 222 char *status_text;
163 if (service[commands] && status_text 223 int cresult;
164 && sscanf (chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) 224 mp_subcheck sc_parse_passive = mp_subcheck_init();
165 { 225 for (size_t i = skip_stdout; i < child_result.out.lines; i++) {
166 fprintf (fp, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", 226 status_text = child_result.out.line[i++];
167 (int) local_time, host_shortname, service[commands++], 227 if (i == child_result.out.lines ||
168 cresult, status_text); 228 strstr(child_result.out.line[i], "STATUS CODE: ") == NULL) {
229
230 sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN);
231 xasprintf(&sc_parse_passive.output, "failed to parse output");
232 mp_add_subcheck_to_check(&overall, sc_parse_passive);
233 mp_exit(overall);
234 }
235
236 if (config.service[commands] && status_text &&
237 sscanf(child_result.out.line[i], "STATUS CODE: %d", &cresult) == 1) {
238 fprintf(output_file, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time,
239 config.host_shortname, config.service[commands++], cresult, status_text);
169 } 240 }
170 } 241 }
171 242
243 sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_OK);
244 xasprintf(&sc_parse_passive.output, "parsed and wrote output");
245 mp_add_subcheck_to_check(&overall, sc_parse_passive);
246
172 /* Multiple commands and passive checking should always return OK */ 247 /* Multiple commands and passive checking should always return OK */
173 return result; 248 mp_exit(overall);
174} 249}
175 250
176/* process command-line arguments */ 251/* process command-line arguments */
177int 252check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
178process_arguments (int argc, char **argv) 253 enum {
179{ 254 output_format_index = CHAR_MAX + 1,
180 int c; 255 };
181 char *p1, *p2;
182 256
183 int option = 0;
184 static struct option longopts[] = { 257 static struct option longopts[] = {
185 {"version", no_argument, 0, 'V'}, 258 {"version", no_argument, 0, 'V'},
186 {"help", no_argument, 0, 'h'}, 259 {"help", no_argument, 0, 'h'},
@@ -188,19 +261,20 @@ process_arguments (int argc, char **argv)
188 {"fork", no_argument, 0, 'f'}, 261 {"fork", no_argument, 0, 'f'},
189 {"timeout", required_argument, 0, 't'}, 262 {"timeout", required_argument, 0, 't'},
190 {"unknown-timeout", no_argument, 0, 'U'}, 263 {"unknown-timeout", no_argument, 0, 'U'},
191 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 264 {"host", required_argument, 0, 'H'}, /* backward compatibility */
192 {"hostname", required_argument, 0, 'H'}, 265 {"hostname", required_argument, 0, 'H'},
193 {"port", required_argument,0,'p'}, 266 {"port", required_argument, 0, 'p'},
194 {"output", required_argument, 0, 'O'}, 267 {"output", required_argument, 0, 'O'},
195 {"name", required_argument, 0, 'n'}, 268 {"name", required_argument, 0, 'n'},
196 {"services", required_argument, 0, 's'}, 269 {"services", required_argument, 0, 's'},
197 {"identity", required_argument, 0, 'i'}, 270 {"identity", required_argument, 0, 'i'},
198 {"user", required_argument, 0, 'u'}, 271 {"user", required_argument, 0, 'u'}, /* backwards compatibility */
199 {"logname", required_argument, 0, 'l'}, 272 {"logname", required_argument, 0, 'l'},
200 {"command", required_argument, 0, 'C'}, 273 {"command", required_argument, 0, 'C'},
201 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ 274 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */
202 {"skip-stdout", optional_argument, 0, 'S'}, 275 {"skip-stdout", optional_argument, 0, 'S'},
203 {"skip-stderr", optional_argument, 0, 'E'}, 276 {"skip-stderr", optional_argument, 0, 'E'},
277 {"unknown-on-stderr", no_argument, 0, 'e'},
204 {"warn-on-stderr", no_argument, 0, 'W'}, 278 {"warn-on-stderr", no_argument, 0, 'W'},
205 {"proto1", no_argument, 0, '1'}, 279 {"proto1", no_argument, 0, '1'},
206 {"proto2", no_argument, 0, '2'}, 280 {"proto2", no_argument, 0, '2'},
@@ -209,287 +283,350 @@ process_arguments (int argc, char **argv)
209 {"ssh-option", required_argument, 0, 'o'}, 283 {"ssh-option", required_argument, 0, 'o'},
210 {"quiet", no_argument, 0, 'q'}, 284 {"quiet", no_argument, 0, 'q'},
211 {"configfile", optional_argument, 0, 'F'}, 285 {"configfile", optional_argument, 0, 'F'},
212 {0, 0, 0, 0} 286 {"output-format", required_argument, 0, output_format_index},
287 {0, 0, 0, 0}};
288
289 check_by_ssh_config_wrapper result = {
290 .errorcode = OK,
291 .config = check_by_ssh_config_init(),
213 }; 292 };
214 293
215 if (argc < 2) 294 if (argc < 2) {
216 return ERROR; 295 result.errorcode = ERROR;
296 return result;
297 }
217 298
218 for (c = 1; c < argc; c++) 299 for (int index = 1; index < argc; index++) {
219 if (strcmp ("-to", argv[c]) == 0) 300 if (strcmp("-to", argv[index]) == 0) {
220 strcpy (argv[c], "-t"); 301 strcpy(argv[index], "-t");
302 }
303 }
221 304
222 while (1) { 305 result.config.cmd = comm_append(result.config.cmd, SSH_COMMAND);
223 c = getopt_long (argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts,
224 &option);
225 306
226 if (c == -1 || c == EOF) 307 int option = 0;
308 while (true) {
309 int opt_index =
310 getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option);
311
312 if (opt_index == -1 || opt_index == EOF) {
227 break; 313 break;
314 }
228 315
229 switch (c) { 316 switch (opt_index) {
230 case 'V': /* version */ 317 case 'V': /* version */
231 print_revision (progname, NP_VERSION); 318 print_revision(progname, NP_VERSION);
232 exit (STATE_UNKNOWN); 319 exit(STATE_UNKNOWN);
233 case 'h': /* help */ 320 case 'h': /* help */
234 print_help (); 321 print_help();
235 exit (STATE_UNKNOWN); 322 exit(STATE_UNKNOWN);
236 case 'v': /* help */ 323 case 'v': /* help */
237 verbose = true; 324 verbose = true;
238 break; 325 break;
239 case 't': /* timeout period */ 326 case 't': /* timeout period */
240 if (!is_integer (optarg)) 327 if (!is_integer(optarg)) {
241 usage_va(_("Timeout interval must be a positive integer")); 328 usage_va(_("Timeout interval must be a positive integer"));
242 else 329 } else {
243 timeout_interval = atoi (optarg); 330 timeout_interval = atoi(optarg);
331 }
244 break; 332 break;
245 case 'U': 333 case 'U':
246 unknown_timeout = true; 334 result.config.unknown_timeout = true;
247 break; 335 break;
248 case 'H': /* host */ 336 case 'H': /* host */
249 hostname = optarg; 337 result.config.hostname = optarg;
250 break; 338 break;
251 case 'p': /* port number */ 339 case 'p': /* port number */
252 if (!is_integer (optarg)) 340 if (!is_integer(optarg)) {
253 usage_va(_("Port must be a positive integer")); 341 usage_va(_("Port must be a positive integer"));
254 comm_append("-p"); 342 }
255 comm_append(optarg); 343 result.config.cmd = comm_append(result.config.cmd, "-p");
344 result.config.cmd = comm_append(result.config.cmd, optarg);
256 break; 345 break;
257 case 'O': /* output file */ 346 case 'O': /* output file */
258 outputfile = optarg; 347 result.config.outputfile = optarg;
259 passive = true; 348 result.config.passive = true;
260 break; 349 break;
261 case 's': /* description of service to check */ 350 case 's': /* description of service to check */ {
351 char *p1;
352 char *p2;
353
262 p1 = optarg; 354 p1 = optarg;
263 service = realloc (service, (++services) * sizeof(char *)); 355 result.config.service = realloc(result.config.service,
264 while ((p2 = index (p1, ':'))) { 356 (++result.config.number_of_services) * sizeof(char *));
357 while ((p2 = index(p1, ':'))) {
265 *p2 = '\0'; 358 *p2 = '\0';
266 service[services - 1] = p1; 359 result.config.service[result.config.number_of_services - 1] = p1;
267 service = realloc (service, (++services) * sizeof(char *)); 360 result.config.service = realloc(
361 result.config.service, (++result.config.number_of_services) * sizeof(char *));
268 p1 = p2 + 1; 362 p1 = p2 + 1;
269 } 363 }
270 service[services - 1] = p1; 364 result.config.service[result.config.number_of_services - 1] = p1;
271 break; 365 break;
272 case 'n': /* short name of host in the monitoring configuration */ 366 case 'n': /* short name of host in the monitoring configuration */
273 host_shortname = optarg; 367 result.config.host_shortname = optarg;
274 break; 368 } break;
275
276 case 'u': 369 case 'u':
277 comm_append("-l"); 370 result.config.cmd = comm_append(result.config.cmd, "-l");
278 comm_append(optarg); 371 result.config.cmd = comm_append(result.config.cmd, optarg);
279 break; 372 break;
280 case 'l': /* login name */ 373 case 'l': /* login name */
281 comm_append("-l"); 374 result.config.cmd = comm_append(result.config.cmd, "-l");
282 comm_append(optarg); 375 result.config.cmd = comm_append(result.config.cmd, optarg);
283 break; 376 break;
284 case 'i': /* identity */ 377 case 'i': /* identity */
285 comm_append("-i"); 378 result.config.cmd = comm_append(result.config.cmd, "-i");
286 comm_append(optarg); 379 result.config.cmd = comm_append(result.config.cmd, optarg);
287 break; 380 break;
288 381
289 case '1': /* Pass these switches directly to ssh */ 382 case '1': /* Pass these switches directly to ssh */
290 comm_append("-1"); 383 result.config.cmd = comm_append(result.config.cmd, "-1");
291 break; 384 break;
292 case '2': /* 1 to force version 1, 2 to force version 2 */ 385 case '2': /* 1 to force version 1, 2 to force version 2 */
293 comm_append("-2"); 386 result.config.cmd = comm_append(result.config.cmd, "-2");
294 break; 387 break;
295 case '4': /* -4 for IPv4 */ 388 case '4': /* -4 for IPv4 */
296 comm_append("-4"); 389 result.config.cmd = comm_append(result.config.cmd, "-4");
297 break; 390 break;
298 case '6': /* -6 for IPv6 */ 391 case '6': /* -6 for IPv6 */
299 comm_append("-6"); 392 result.config.cmd = comm_append(result.config.cmd, "-6");
300 break; 393 break;
301 case 'f': /* fork to background */ 394 case 'f': /* fork to background */
302 comm_append("-f"); 395 result.config.cmd = comm_append(result.config.cmd, "-f");
303 break; 396 break;
304 case 'C': /* Command for remote machine */ 397 case 'C': /* Command for remote machine */
305 commands++; 398 result.config.commands++;
306 if (commands > 1) 399 if (result.config.commands > 1) {
307 xasprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); 400 xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;",
308 xasprintf (&remotecmd, "%s%s", remotecmd, optarg); 401 result.config.remotecmd);
402 }
403 xasprintf(&result.config.remotecmd, "%s%s", result.config.remotecmd, optarg);
309 break; 404 break;
310 case 'S': /* skip n (or all) lines on stdout */ 405 case 'S': /* skip n (or all) lines on stdout */
311 if (optarg == NULL) 406 if (optarg == NULL) {
312 skip_stdout = -1; /* skip all output on stdout */ 407 result.config.skip_stdout = true; /* skip all output on stdout */
313 else if (!is_integer (optarg)) 408
409 if (verbose) {
410 printf("Setting the skip_stdout flag\n");
411 }
412 } else if (!is_integer(optarg)) {
314 usage_va(_("skip-stdout argument must be an integer")); 413 usage_va(_("skip-stdout argument must be an integer"));
315 else 414 } else {
316 skip_stdout = atoi (optarg); 415 result.config.stdout_lines_to_ignore = atoi(optarg);
416 }
317 break; 417 break;
318 case 'E': /* skip n (or all) lines on stderr */ 418 case 'E': /* skip n (or all) lines on stderr */
319 if (optarg == NULL) 419 if (optarg == NULL) {
320 skip_stderr = -1; /* skip all output on stderr */ 420 result.config.skip_stderr = true; /* skip all output on stderr */
321 else if (!is_integer (optarg)) 421 if (verbose) {
422 printf("Setting the skip_stderr flag\n");
423 }
424 } else if (!is_integer(optarg)) {
322 usage_va(_("skip-stderr argument must be an integer")); 425 usage_va(_("skip-stderr argument must be an integer"));
323 else 426 } else {
324 skip_stderr = atoi (optarg); 427 result.config.sterr_lines_to_ignore = atoi(optarg);
428 }
429 break;
430 case 'e': /* exit with unknown if there is an output on stderr */
431 result.config.unknown_on_stderr = true;
432 break;
433 case 'W': /* exit with warning if there is an output on stderr */
434 result.config.warn_on_stderr = true;
325 break; 435 break;
326 case 'W': /* exit with warning if there is an output on stderr */ 436 case 'o': /* Extra options for the ssh command */
327 warn_on_stderr = 1; 437 result.config.cmd = comm_append(result.config.cmd, "-o");
438 result.config.cmd = comm_append(result.config.cmd, optarg);
328 break; 439 break;
329 case 'o': /* Extra options for the ssh command */ 440 case 'q': /* Tell the ssh command to be quiet */
330 comm_append("-o"); 441 result.config.cmd = comm_append(result.config.cmd, "-q");
331 comm_append(optarg);
332 break; 442 break;
333 case 'q': /* Tell the ssh command to be quiet */ 443 case 'F': /* ssh configfile */
334 comm_append("-q"); 444 result.config.cmd = comm_append(result.config.cmd, "-F");
445 result.config.cmd = comm_append(result.config.cmd, optarg);
335 break; 446 break;
336 case 'F': /* ssh configfile */ 447 case output_format_index: {
337 comm_append("-F"); 448 parsed_output_format parser = mp_parse_output_format(optarg);
338 comm_append(optarg); 449 if (!parser.parsing_success) {
450 // TODO List all available formats here, maybe add anothoer usage function
451 printf("Invalid output format: %s\n", optarg);
452 exit(STATE_UNKNOWN);
453 }
454
455 result.config.output_format_is_set = true;
456 result.config.output_format = parser.output_format;
339 break; 457 break;
340 default: /* help */ 458 }
459 default: /* help */
341 usage5(); 460 usage5();
342 } 461 }
343 } 462 }
344 463
345 c = optind; 464 int c = optind;
346 if (hostname == NULL) { 465 if (result.config.hostname == NULL) {
347 if (c <= argc) { 466 if (c <= argc) {
348 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname); 467 die(STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
349 } 468 }
350 hostname = argv[c++]; 469 result.config.hostname = argv[c++];
351 } 470 }
352 471
353 if (strlen(remotecmd) == 0) { 472 if (strlen(result.config.remotecmd) == 0) {
354 for (; c < argc; c++) 473 for (; c < argc; c++) {
355 if (strlen(remotecmd) > 0) 474 if (strlen(result.config.remotecmd) > 0) {
356 xasprintf (&remotecmd, "%s %s", remotecmd, argv[c]); 475 xasprintf(&result.config.remotecmd, "%s %s", result.config.remotecmd, argv[c]);
357 else 476 } else {
358 xasprintf (&remotecmd, "%s", argv[c]); 477 xasprintf(&result.config.remotecmd, "%s", argv[c]);
478 }
479 }
359 } 480 }
360 481
361 if (commands > 1 || passive) 482 if (result.config.commands > 1 || result.config.passive) {
362 xasprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); 483 xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", result.config.remotecmd);
484 }
363 485
364 if (remotecmd == NULL || strlen (remotecmd) <= 1) 486 if (result.config.remotecmd == NULL || strlen(result.config.remotecmd) <= 1) {
365 usage_va(_("No remotecmd")); 487 usage_va(_("No remotecmd"));
488 }
366 489
367 comm_append(hostname); 490 result.config.cmd = comm_append(result.config.cmd, result.config.hostname);
368 comm_append(remotecmd); 491 result.config.cmd = comm_append(result.config.cmd, result.config.remotecmd);
369 492
370 return validate_arguments (); 493 return validate_arguments(result);
371} 494}
372 495
496command_construct comm_append(command_construct cmd, const char *str) {
373 497
374void 498 if (verbose) {
375comm_append (const char *str) 499 for (int i = 0; i < cmd.commargc; i++) {
376{ 500 printf("Current command: [%i] %s\n", i, cmd.commargv[i]);
501 }
502
503 printf("Appending: %s\n", str);
504 }
377 505
378 if (++commargc > NP_MAXARGS) 506 if (++cmd.commargc > NP_MAXARGS) {
379 die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS); 507 die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS);
508 }
380 509
381 if ((commargv = (char **)realloc(commargv, (commargc+1) * sizeof(char *))) == NULL) 510 if ((cmd.commargv = (char **)realloc(cmd.commargv, (cmd.commargc + 1) * sizeof(char *))) ==
511 NULL) {
382 die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n")); 512 die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n"));
513 }
383 514
384 commargv[commargc-1] = strdup(str); 515 cmd.commargv[cmd.commargc - 1] = strdup(str);
385 commargv[commargc] = NULL; 516 cmd.commargv[cmd.commargc] = NULL;
386 517
518 return cmd;
387} 519}
388 520
389int 521check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper config_wrapper) {
390validate_arguments (void) 522 if (config_wrapper.config.remotecmd == NULL || config_wrapper.config.hostname == NULL) {
391{ 523 config_wrapper.errorcode = ERROR;
392 if (remotecmd == NULL || hostname == NULL) 524 return config_wrapper;
393 return ERROR; 525 }
394 526
395 if (passive && commands != services) 527 if (config_wrapper.config.passive &&
396 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname); 528 config_wrapper.config.commands != config_wrapper.config.number_of_services) {
529 die(STATE_UNKNOWN,
530 _("%s: In passive mode, you must provide a service name for each command.\n"),
531 progname);
532 }
397 533
398 if (passive && host_shortname == NULL) 534 if (config_wrapper.config.passive && config_wrapper.config.host_shortname == NULL) {
399 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the monitoring configs.\n"), progname); 535 die(STATE_UNKNOWN,
536 _("%s: In passive mode, you must provide the host short name from the monitoring "
537 "configs.\n"),
538 progname);
539 }
400 540
401 return OK; 541 return config_wrapper;
402} 542}
403 543
404 544void print_help(void) {
405void 545 print_revision(progname, NP_VERSION);
406print_help (void) 546
407{ 547 printf("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
408 print_revision (progname, NP_VERSION); 548 printf(COPYRIGHT, copyright, email);
409 549
410 printf ("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"); 550 printf(_("This plugin uses SSH to execute commands on a remote host"));
411 printf (COPYRIGHT, copyright, email); 551
412 552 printf("\n\n");
413 printf (_("This plugin uses SSH to execute commands on a remote host")); 553
414 554 print_usage();
415 printf ("\n\n"); 555
416 556 printf(UT_HELP_VRSN);
417 print_usage (); 557
418 558 printf(UT_EXTRA_OPTS);
419 printf (UT_HELP_VRSN); 559
420 560 printf(UT_HOST_PORT, 'p', "none");
421 printf (UT_EXTRA_OPTS); 561
422 562 printf(UT_IPv46);
423 printf (UT_HOST_PORT, 'p', "none"); 563
424 564 printf(" %s\n", "-1, --proto1");
425 printf (UT_IPv46); 565 printf(" %s\n", _("tell ssh to use Protocol 1 [optional]"));
426 566 printf(" %s\n", "-2, --proto2");
427 printf (" %s\n", "-1, --proto1"); 567 printf(" %s\n", _("tell ssh to use Protocol 2 [optional]"));
428 printf (" %s\n", _("tell ssh to use Protocol 1 [optional]")); 568 printf(" %s\n", "-S, --skip-stdout[=n]");
429 printf (" %s\n", "-2, --proto2"); 569 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]"));
430 printf (" %s\n", _("tell ssh to use Protocol 2 [optional]")); 570 printf(" %s\n", "-E, --skip-stderr[=n]");
431 printf (" %s\n", "-S, --skip-stdout[=n]"); 571 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]"));
432 printf (" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]")); 572 printf(" %s\n", "-e, --unknown-on-stderr");
433 printf (" %s\n", "-E, --skip-stderr[=n]"); 573 printf(" %s\n", _("Exit with UNKNOWN, if there is output on STDERR"));
434 printf (" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]")); 574 printf(" %s\n", "-W, --warn-on-stderr");
435 printf (" %s\n", "-W, --warn-on-stderr]"); 575 printf(" %s\n", _("Exit with WARNING, if there is output on STDERR"));
436 printf (" %s\n", _("Exit with an warning, if there is an output on STDERR")); 576 printf(" %s\n", "-f");
437 printf (" %s\n", "-f"); 577 printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always "
438 printf (" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always return OK if ssh is executed")); 578 "return OK if ssh is executed"));
439 printf (" %s\n","-C, --command='COMMAND STRING'"); 579 printf(" %s\n", "-C, --command='COMMAND STRING'");
440 printf (" %s\n", _("command to execute on the remote machine")); 580 printf(" %s\n", _("command to execute on the remote machine"));
441 printf (" %s\n","-l, --logname=USERNAME"); 581 printf(" %s\n", "-l, --logname=USERNAME");
442 printf (" %s\n", _("SSH user name on remote host [optional]")); 582 printf(" %s\n", _("SSH user name on remote host [optional]"));
443 printf (" %s\n","-i, --identity=KEYFILE"); 583 printf(" %s\n", "-i, --identity=KEYFILE");
444 printf (" %s\n", _("identity of an authorized key [optional]")); 584 printf(" %s\n", _("identity of an authorized key [optional]"));
445 printf (" %s\n","-O, --output=FILE"); 585 printf(" %s\n", "-O, --output=FILE");
446 printf (" %s\n", _("external command file for monitoring [optional]")); 586 printf(" %s\n", _("external command file for monitoring [optional]"));
447 printf (" %s\n","-s, --services=LIST"); 587 printf(" %s\n", "-s, --services=LIST");
448 printf (" %s\n", _("list of monitoring service names, separated by ':' [optional]")); 588 printf(" %s\n", _("list of monitoring service names, separated by ':' [optional]"));
449 printf (" %s\n","-n, --name=NAME"); 589 printf(" %s\n", "-n, --name=NAME");
450 printf (" %s\n", _("short name of host in the monitoring configuration [optional]")); 590 printf(" %s\n", _("short name of host in the monitoring configuration [optional]"));
451 printf (" %s\n","-o, --ssh-option=OPTION"); 591 printf(" %s\n", "-o, --ssh-option=OPTION");
452 printf (" %s\n", _("Call ssh with '-o OPTION' (may be used multiple times) [optional]")); 592 printf(" %s\n", _("Call ssh with '-o OPTION' (may be used multiple times) [optional]"));
453 printf (" %s\n","-F, --configfile"); 593 printf(" %s\n", "-F, --configfile");
454 printf (" %s\n", _("Tell ssh to use this configfile [optional]")); 594 printf(" %s\n", _("Tell ssh to use this configfile [optional]"));
455 printf (" %s\n","-q, --quiet"); 595 printf(" %s\n", "-q, --quiet");
456 printf (" %s\n", _("Tell ssh to suppress warning and diagnostic messages [optional]")); 596 printf(" %s\n", _("Tell ssh to suppress warning and diagnostic messages [optional]"));
457 printf (UT_WARN_CRIT); 597 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
458 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 598 printf(" %s\n", "-U, --unknown-timeout");
459 printf (" %s\n","-U, --unknown-timeout"); 599 printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL"));
460 printf (" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); 600 printf(UT_VERBOSE);
461 printf (UT_VERBOSE); 601 printf(UT_OUTPUT_FORMAT);
602 printf("\n");
603 printf(" %s\n", _("The most common mode of use is to refer to a local identity file with"));
604 printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null"));
605 printf(" %s\n", _("passphrase and the public key should be listed in the authorized_keys"));
606 printf(" %s\n", _("file of the remote host. Usually the key will be restricted to running"));
607 printf(" %s\n", _("only one command on the remote server. If the remote SSH server tracks"));
608 printf(" %s\n", _("invocation arguments, the one remote program may be an agent that can"));
609 printf(" %s\n", _("execute additional commands as proxy"));
610 printf("\n");
611 printf(" %s\n", _("To use passive mode, provide multiple '-C' options, and provide"));
612 printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)"));
462 printf("\n"); 613 printf("\n");
463 printf (" %s\n", _("The most common mode of use is to refer to a local identity file with")); 614 printf("%s\n", _("Examples:"));
464 printf (" %s\n", _("the '-i' option. In this mode, the identity pair should have a null")); 615 printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C "
465 printf (" %s\n", _("passphrase and the public key should be listed in the authorized_keys")); 616 "uptime -O /tmp/foo");
466 printf (" %s\n", _("file of the remote host. Usually the key will be restricted to running")); 617 printf(" %s\n", "$ cat /tmp/foo");
467 printf (" %s\n", _("only one command on the remote server. If the remote SSH server tracks")); 618 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days");
468 printf (" %s\n", _("invocation arguments, the one remote program may be an agent that can")); 619 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days");
469 printf (" %s\n", _("execute additional commands as proxy")); 620 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days");
470 printf("\n");
471 printf (" %s\n", _("To use passive mode, provide multiple '-C' options, and provide"));
472 printf (" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)"));
473 printf ("\n");
474 printf ("%s\n", _("Examples:"));
475 printf (" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo");
476 printf (" %s\n", "$ cat /tmp/foo");
477 printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days");
478 printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days");
479 printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days");
480 621
481 printf(UT_SUPPORT); 622 printf(UT_SUPPORT);
482} 623}
483 624
484 625void print_usage(void) {
485 626 printf("%s\n", _("Usage:"));
486void 627 printf(" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n"
487print_usage (void) 628 " [-S [lines]] [-E [lines]] [-e|-W] [-t timeout] [-i identity]\n"
488{ 629 " [-l user] [-n name] [-s servicelist] [-O outputfile]\n"
489 printf ("%s\n", _("Usage:")); 630 " [-p port] [-o ssh-option] [-F configfile]\n",
490 printf (" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n" 631 progname);
491 " [-S [lines]] [-E [lines]] [-W] [-t timeout] [-i identity]\n"
492 " [-l user] [-n name] [-s servicelist] [-O outputfile]\n"
493 " [-p port] [-o ssh-option] [-F configfile]\n",
494 progname);
495} 632}
diff --git a/plugins/check_by_ssh.d/config.h b/plugins/check_by_ssh.d/config.h
new file mode 100644
index 00000000..b6a57964
--- /dev/null
+++ b/plugins/check_by_ssh.d/config.h
@@ -0,0 +1,71 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include <stddef.h>
6
7typedef struct {
8 int commargc;
9 char **commargv;
10} command_construct;
11
12typedef struct {
13 char *hostname;
14 char *host_shortname;
15
16 char **service;
17 unsigned int number_of_services;
18
19 unsigned int commands; // Not needed during actual test run
20 char *remotecmd;
21
22 command_construct cmd;
23
24 bool unknown_timeout;
25 bool unknown_on_stderr;
26 bool warn_on_stderr;
27 bool skip_stdout;
28 size_t stdout_lines_to_ignore;
29 bool skip_stderr;
30 size_t sterr_lines_to_ignore;
31
32 bool passive;
33 char *outputfile;
34
35 bool output_format_is_set;
36 mp_output_format output_format;
37} check_by_ssh_config;
38
39check_by_ssh_config check_by_ssh_config_init() {
40 check_by_ssh_config tmp = {
41 .hostname = NULL,
42 .host_shortname = NULL,
43
44 .service = NULL,
45 .number_of_services = 0,
46
47 .commands = 0,
48 .remotecmd = "",
49
50 .cmd =
51 {
52 .commargc = 0,
53 .commargv = NULL,
54 },
55
56 .unknown_timeout = false,
57 .unknown_on_stderr = false,
58 .warn_on_stderr = false,
59
60 .skip_stderr = false,
61 .stdout_lines_to_ignore = 0,
62 .skip_stdout = false,
63 .sterr_lines_to_ignore = 0,
64
65 .passive = false,
66 .outputfile = NULL,
67
68 .output_format_is_set = false,
69 };
70 return tmp;
71}
diff --git a/plugins/check_cluster.c b/plugins/check_cluster.c
index e1ede9f7..92c3827a 100644
--- a/plugins/check_cluster.c
+++ b/plugins/check_cluster.c
@@ -1,92 +1,87 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* check_cluster.c - Host and Service Cluster Plugin for Monitoring 3 * check_cluster.c - Host and Service Cluster Plugin for Monitoring
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2004 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 2000-2004 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2007 Monitoring Plugins Development Team 7 * Copyright (c) 2007-2024 Monitoring Plugins Development Team
8* 8 *
9* This program is free software: you can redistribute it and/or modify 9 * This program is free software: you can redistribute it and/or modify
10* it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
11* the Free Software Foundation, either version 3 of the License, or 11 * the Free Software Foundation, either version 3 of the License, or
12* (at your option) any later version. 12 * (at your option) any later version.
13* 13 *
14* This program is distributed in the hope that it will be useful, 14 * This program is distributed in the hope that it will be useful,
15* but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17* GNU General Public License for more details. 17 * GNU General Public License for more details.
18* 18 *
19* You should have received a copy of the GNU General Public License 19 * You should have received a copy of the GNU General Public License
20* along with this program. If not, see <http://www.gnu.org/licenses/>. 20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21* 21 *
22* 22 *
23*****************************************************************************/ 23 *****************************************************************************/
24 24
25const char *progname = "check_cluster"; 25const char *progname = "check_cluster";
26const char *copyright = "2000-2007"; 26const char *copyright = "2000-2024";
27const char *email = "devel@monitoring-plugins.org"; 27const char *email = "devel@monitoring-plugins.org";
28 28
29#include "output.h"
30#include "states.h"
29#include "common.h" 31#include "common.h"
30#include "utils.h" 32#include "utils.h"
31#include "utils_base.h" 33#include "utils_base.h"
34#include "check_cluster.d/config.h"
32 35
33#define CHECK_SERVICES 1 36static void print_help(void);
34#define CHECK_HOSTS 2 37void print_usage(void);
35 38
36void print_help (void); 39static int verbose = 0;
37void print_usage (void);
38 40
39int total_services_ok=0; 41typedef struct {
40int total_services_warning=0; 42 int errorcode;
41int total_services_unknown=0; 43 check_cluster_config config;
42int total_services_critical=0; 44} check_cluster_config_wrapper;
45static check_cluster_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
43 46
44int total_hosts_up=0; 47int main(int argc, char **argv) {
45int total_hosts_down=0; 48 setlocale(LC_ALL, "");
46int total_hosts_unreachable=0; 49 bindtextdomain(PACKAGE, LOCALEDIR);
47 50 textdomain(PACKAGE);
48char *warn_threshold;
49char *crit_threshold;
50
51int check_type=CHECK_SERVICES;
52
53char *data_vals=NULL;
54char *label=NULL;
55
56int verbose=0;
57
58int process_arguments(int,char **);
59
60
61
62int main(int argc, char **argv){
63 char *ptr;
64 int data_val;
65 int return_code=STATE_OK;
66 thresholds *thresholds = NULL;
67
68 setlocale (LC_ALL, "");
69 bindtextdomain (PACKAGE, LOCALEDIR);
70 textdomain (PACKAGE);
71 51
72 /* Parse extra opts if any */ 52 /* Parse extra opts if any */
73 argv=np_extra_opts(&argc, argv, progname); 53 argv = np_extra_opts(&argc, argv, progname);
74 54
75 if(process_arguments(argc,argv)==ERROR) 55 check_cluster_config_wrapper tmp_config = process_arguments(argc, argv);
56 if (tmp_config.errorcode == ERROR) {
76 usage(_("Could not parse arguments")); 57 usage(_("Could not parse arguments"));
58 }
59
60 const check_cluster_config config = tmp_config.config;
61
62 if (config.output_format_is_set) {
63 mp_set_format(config.output_format);
64 }
77 65
78 /* Initialize the thresholds */ 66 /* Initialize the thresholds */
79 set_thresholds(&thresholds, warn_threshold, crit_threshold); 67 if (verbose) {
80 if(verbose) 68 print_thresholds("check_cluster", config.thresholds);
81 print_thresholds("check_cluster", thresholds); 69 }
82 70
71 int data_val;
72 int total_services_ok = 0;
73 int total_services_warning = 0;
74 int total_services_unknown = 0;
75 int total_services_critical = 0;
76 int total_hosts_up = 0;
77 int total_hosts_down = 0;
78 int total_hosts_unreachable = 0;
83 /* check the data values */ 79 /* check the data values */
84 for(ptr=strtok(data_vals,",");ptr!=NULL;ptr=strtok(NULL,",")){ 80 for (char *ptr = strtok(config.data_vals, ","); ptr != NULL; ptr = strtok(NULL, ",")) {
85 81 data_val = atoi(ptr);
86 data_val=atoi(ptr);
87 82
88 if(check_type==CHECK_SERVICES){ 83 if (config.check_type == CHECK_SERVICES) {
89 switch(data_val){ 84 switch (data_val) {
90 case 0: 85 case 0:
91 total_services_ok++; 86 total_services_ok++;
92 break; 87 break;
@@ -101,10 +96,9 @@ int main(int argc, char **argv){
101 break; 96 break;
102 default: 97 default:
103 break; 98 break;
104 } 99 }
105 } 100 } else {
106 else{ 101 switch (data_val) {
107 switch(data_val){
108 case 0: 102 case 0:
109 total_hosts_up++; 103 total_hosts_up++;
110 break; 104 break;
@@ -116,125 +110,150 @@ int main(int argc, char **argv){
116 break; 110 break;
117 default: 111 default:
118 break; 112 break;
119 } 113 }
120 } 114 }
121 } 115 }
122 116
117 mp_check overall = mp_check_init();
118 mp_subcheck sc_real_test = mp_subcheck_init();
119 sc_real_test = mp_set_subcheck_default_state(sc_real_test, STATE_OK);
123 120
124 /* return the status of the cluster */ 121 /* return the status of the cluster */
125 if(check_type==CHECK_SERVICES){ 122 if (config.check_type == CHECK_SERVICES) {
126 return_code=get_status(total_services_warning+total_services_unknown+total_services_critical, thresholds); 123 sc_real_test = mp_set_subcheck_state(
127 printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n", 124 sc_real_test,
128 state_text(return_code), (label==NULL)?"Service cluster":label, 125 get_status(total_services_warning + total_services_unknown + total_services_critical,
129 total_services_ok,total_services_warning, 126 config.thresholds));
130 total_services_unknown,total_services_critical); 127 xasprintf(&sc_real_test.output, "%s: %d ok, %d warning, %d unknown, %d critical",
131 } 128 (config.label == NULL) ? "Service cluster" : config.label, total_services_ok,
132 else{ 129 total_services_warning, total_services_unknown, total_services_critical);
133 return_code=get_status(total_hosts_down+total_hosts_unreachable, thresholds); 130 } else {
134 printf("CLUSTER %s: %s: %d up, %d down, %d unreachable\n", 131 sc_real_test = mp_set_subcheck_state(
135 state_text(return_code), (label==NULL)?"Host cluster":label, 132 sc_real_test,
136 total_hosts_up,total_hosts_down,total_hosts_unreachable); 133 get_status(total_hosts_down + total_hosts_unreachable, config.thresholds));
134 xasprintf(&sc_real_test.output, "%s: %d up, %d down, %d unreachable\n",
135 (config.label == NULL) ? "Host cluster" : config.label, total_hosts_up,
136 total_hosts_down, total_hosts_unreachable);
137 } 137 }
138 138
139 return return_code; 139 mp_add_subcheck_to_check(&overall, sc_real_test);
140}
141 140
141 mp_exit(overall);
142}
142 143
144check_cluster_config_wrapper process_arguments(int argc, char **argv) {
145 enum {
146 output_format_index = CHAR_MAX + 1,
147 };
143 148
144int process_arguments(int argc, char **argv){ 149 static struct option longopts[] = {{"data", required_argument, 0, 'd'},
145 int c; 150 {"warning", required_argument, 0, 'w'},
146 char *ptr; 151 {"critical", required_argument, 0, 'c'},
147 int option=0; 152 {"label", required_argument, 0, 'l'},
148 static struct option longopts[]={ 153 {"host", no_argument, 0, 'h'},
149 {"data", required_argument,0,'d'}, 154 {"service", no_argument, 0, 's'},
150 {"warning", required_argument,0,'w'}, 155 {"verbose", no_argument, 0, 'v'},
151 {"critical", required_argument,0,'c'}, 156 {"version", no_argument, 0, 'V'},
152 {"label", required_argument,0,'l'}, 157 {"help", no_argument, 0, 'H'},
153 {"host", no_argument, 0,'h'}, 158 {"output-format", required_argument, 0, output_format_index},
154 {"service", no_argument, 0,'s'}, 159 {0, 0, 0, 0}};
155 {"verbose", no_argument, 0,'v'}, 160
156 {"version", no_argument, 0,'V'}, 161 check_cluster_config_wrapper result = {
157 {"help", no_argument, 0,'H'}, 162 .errorcode = OK,
158 {0,0,0,0} 163 .config = check_cluster_config_init(),
159 }; 164 };
160 165
161 /* no options were supplied */ 166 /* no options were supplied */
162 if(argc<2) 167 if (argc < 2) {
163 return ERROR; 168 result.errorcode = ERROR;
164 169 return result;
165 while(1){ 170 }
166 171
167 c=getopt_long(argc,argv,"hHsvVw:c:d:l:",longopts,&option); 172 int option = 0;
173 char *warn_threshold = NULL;
174 char *crit_threshold = NULL;
175 while (true) {
176 int option_index = getopt_long(argc, argv, "hHsvVw:c:d:l:", longopts, &option);
168 177
169 if(c==-1 || c==EOF || c==1) 178 if (CHECK_EOF(option_index) || option_index == 1) {
170 break; 179 break;
180 }
171 181
172 switch(c){ 182 switch (option_index) {
173
174 case 'h': /* host cluster */ 183 case 'h': /* host cluster */
175 check_type=CHECK_HOSTS; 184 result.config.check_type = CHECK_HOSTS;
176 break; 185 break;
177
178 case 's': /* service cluster */ 186 case 's': /* service cluster */
179 check_type=CHECK_SERVICES; 187 result.config.check_type = CHECK_SERVICES;
180 break; 188 break;
181
182 case 'w': /* warning threshold */ 189 case 'w': /* warning threshold */
183 warn_threshold = strdup(optarg); 190 warn_threshold = strdup(optarg);
184 break; 191 break;
185
186 case 'c': /* warning threshold */ 192 case 'c': /* warning threshold */
187 crit_threshold = strdup(optarg); 193 crit_threshold = strdup(optarg);
188 break; 194 break;
189
190 case 'd': /* data values */ 195 case 'd': /* data values */
191 data_vals=(char *)strdup(optarg); 196 result.config.data_vals = strdup(optarg);
192 /* validate data */ 197 /* validate data */
193 for (ptr=data_vals;ptr!=NULL;ptr+=2){ 198 for (char *ptr = result.config.data_vals; ptr != NULL; ptr += 2) {
194 if (ptr[0]<'0' || ptr[0]>'3') 199 if (ptr[0] < '0' || ptr[0] > '3') {
195 return ERROR; 200 result.errorcode = ERROR;
196 if (ptr[1]=='\0') 201 return result;
202 }
203 if (ptr[1] == '\0') {
197 break; 204 break;
198 if (ptr[1]!=',') 205 }
199 return ERROR; 206 if (ptr[1] != ',') {
207 result.errorcode = ERROR;
208 return result;
209 }
200 } 210 }
201 break; 211 break;
202
203 case 'l': /* text label */ 212 case 'l': /* text label */
204 label=(char *)strdup(optarg); 213 result.config.label = strdup(optarg);
205 break; 214 break;
206
207 case 'v': /* verbose */ 215 case 'v': /* verbose */
208 verbose++; 216 verbose++;
209 break; 217 break;
210
211 case 'V': /* version */ 218 case 'V': /* version */
212 print_revision (progname, NP_VERSION); 219 print_revision(progname, NP_VERSION);
213 exit (STATE_UNKNOWN); 220 exit(STATE_UNKNOWN);
214 break; 221 break;
215
216 case 'H': /* help */ 222 case 'H': /* help */
217 print_help(); 223 print_help();
218 exit(STATE_UNKNOWN); 224 exit(STATE_UNKNOWN);
219 break; 225 break;
226 case output_format_index: {
227 parsed_output_format parser = mp_parse_output_format(optarg);
228 if (!parser.parsing_success) {
229 // TODO List all available formats here, maybe add anothoer usage function
230 printf("Invalid output format: %s\n", optarg);
231 exit(STATE_UNKNOWN);
232 }
220 233
234 result.config.output_format_is_set = true;
235 result.config.output_format = parser.output_format;
236 break;
237 }
221 default: 238 default:
222 return ERROR; 239 result.errorcode = ERROR;
240 return result;
223 break; 241 break;
224 } 242 }
225 } 243 }
226 244
227 if(data_vals==NULL) 245 if (result.config.data_vals == NULL) {
228 return ERROR; 246 result.errorcode = ERROR;
247 return result;
248 }
229 249
230 return OK; 250 set_thresholds(&result.config.thresholds, warn_threshold, crit_threshold);
251 return result;
231} 252}
232 253
233void 254void print_help(void) {
234print_help(void)
235{
236 print_revision(progname, NP_VERSION); 255 print_revision(progname, NP_VERSION);
237 printf ("Copyright (c) 2000-2004 Ethan Galstad (nagios@nagios.org)\n"); 256 printf("Copyright (c) 2000-2004 Ethan Galstad (nagios@nagios.org)\n");
238 printf(COPYRIGHT, copyright, email); 257 printf(COPYRIGHT, copyright, email);
239 258
240 printf(_("Host/Service Cluster Plugin for Monitoring")); 259 printf(_("Host/Service Cluster Plugin for Monitoring"));
@@ -245,45 +264,43 @@ print_help(void)
245 printf("\n"); 264 printf("\n");
246 printf("%s\n", _("Options:")); 265 printf("%s\n", _("Options:"));
247 printf(UT_EXTRA_OPTS); 266 printf(UT_EXTRA_OPTS);
248 printf (" %s\n", "-s, --service"); 267 printf(" %s\n", "-s, --service");
249 printf (" %s\n", _("Check service cluster status")); 268 printf(" %s\n", _("Check service cluster status"));
250 printf (" %s\n", "-h, --host"); 269 printf(" %s\n", "-h, --host");
251 printf (" %s\n", _("Check host cluster status")); 270 printf(" %s\n", _("Check host cluster status"));
252 printf (" %s\n", "-l, --label=STRING"); 271 printf(" %s\n", "-l, --label=STRING");
253 printf (" %s\n", _("Optional prepended text output (i.e. \"Host cluster\")")); 272 printf(" %s\n", _("Optional prepended text output (i.e. \"Host cluster\")"));
254 printf (" %s\n", "-w, --warning=THRESHOLD"); 273 printf(" %s\n", "-w, --warning=THRESHOLD");
255 printf (" %s\n", _("Specifies the range of hosts or services in cluster that must be in a")); 274 printf(" %s\n", _("Specifies the range of hosts or services in cluster that must be in a"));
256 printf (" %s\n", _("non-OK state in order to return a WARNING status level")); 275 printf(" %s\n", _("non-OK state in order to return a WARNING status level"));
257 printf (" %s\n", "-c, --critical=THRESHOLD"); 276 printf(" %s\n", "-c, --critical=THRESHOLD");
258 printf (" %s\n", _("Specifies the range of hosts or services in cluster that must be in a")); 277 printf(" %s\n", _("Specifies the range of hosts or services in cluster that must be in a"));
259 printf (" %s\n", _("non-OK state in order to return a CRITICAL status level")); 278 printf(" %s\n", _("non-OK state in order to return a CRITICAL status level"));
260 printf (" %s\n", "-d, --data=LIST"); 279 printf(" %s\n", "-d, --data=LIST");
261 printf (" %s\n", _("The status codes of the hosts or services in the cluster, separated by")); 280 printf(" %s\n", _("The status codes of the hosts or services in the cluster, separated by"));
262 printf (" %s\n", _("commas")); 281 printf(" %s\n", _("commas"));
263 282
264 printf(UT_VERBOSE); 283 printf(UT_VERBOSE);
265 284
285 printf(UT_OUTPUT_FORMAT);
286
266 printf("\n"); 287 printf("\n");
267 printf("%s\n", _("Notes:")); 288 printf("%s\n", _("Notes:"));
268 printf(UT_THRESHOLDS_NOTES); 289 printf(UT_THRESHOLDS_NOTES);
269 290
270 printf ("\n"); 291 printf("\n");
271 printf ("%s\n", _("Examples:")); 292 printf("%s\n", _("Examples:"));
272 printf (" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:"); 293 printf(" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:");
273 printf (" %s\n", _("Will alert critical if there are 3 or more service data points in a non-OK") ); 294 printf(" %s\n",
274 printf (" %s\n", _("state.") ); 295 _("Will alert critical if there are 3 or more service data points in a non-OK"));
296 printf(" %s\n", _("state."));
275 297
276 printf(UT_SUPPORT); 298 printf(UT_SUPPORT);
277} 299}
278 300
279 301void print_usage(void) {
280void
281print_usage(void)
282{
283 302
284 printf("%s\n", _("Usage:")); 303 printf("%s\n", _("Usage:"));
285 printf(" %s (-s | -h) -d val1[,val2,...,valn] [-l label]\n", progname); 304 printf(" %s (-s | -h) -d val1[,val2,...,valn] [-l label]\n", progname);
286 printf("[-w threshold] [-c threshold] [-v] [--help]\n"); 305 printf("[-w threshold] [-c threshold] [-v] [--help]\n");
287
288} 306}
289
diff --git a/plugins/check_cluster.d/config.h b/plugins/check_cluster.d/config.h
new file mode 100644
index 00000000..054657b0
--- /dev/null
+++ b/plugins/check_cluster.d/config.h
@@ -0,0 +1,33 @@
1#pragma once
2
3#include "../../config.h"
4#include "../../lib/thresholds.h"
5#include "output.h"
6#include <stddef.h>
7
8enum {
9 CHECK_SERVICES = 1,
10 CHECK_HOSTS = 2
11};
12
13typedef struct {
14 char *data_vals;
15 thresholds *thresholds;
16 int check_type;
17 char *label;
18
19 mp_output_format output_format;
20 bool output_format_is_set;
21} check_cluster_config;
22
23check_cluster_config check_cluster_config_init() {
24 check_cluster_config tmp = {
25 .data_vals = NULL,
26 .thresholds = NULL,
27 .check_type = CHECK_SERVICES,
28 .label = NULL,
29
30 .output_format_is_set = false,
31 };
32 return tmp;
33}
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index e25d7a79..67d89129 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -1,2736 +1,2029 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_curl plugin 3 * Monitoring check_curl plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2019 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_curl plugin 10 * This file contains the check_curl plugin
11* 11 *
12* This plugin tests the HTTP service on the specified host. It can test 12 * This plugin tests the HTTP service on the specified host. It can test
13* normal (http) and secure (https) servers, follow redirects, search for 13 * normal (http) and secure (https) servers, follow redirects, search for
14* strings and regular expressions, check connection times, and report on 14 * strings and regular expressions, check connection times, and report on
15* certificate expiration times. 15 * certificate expiration times.
16* 16 *
17* This plugin uses functions from the curl library, see 17 * This plugin uses functions from the curl library, see
18* http://curl.haxx.se 18 * http://curl.haxx.se
19* 19 *
20* This program is free software: you can redistribute it and/or modify 20 * This program is free software: you can redistribute it and/or modify
21* it under the terms of the GNU General Public License as published by 21 * it under the terms of the GNU General Public License as published by
22* the Free Software Foundation, either version 3 of the License, or 22 * the Free Software Foundation, either version 3 of the License, or
23* (at your option) any later version. 23 * (at your option) any later version.
24* 24 *
25* This program is distributed in the hope that it will be useful, 25 * This program is distributed in the hope that it will be useful,
26* but WITHOUT ANY WARRANTY; without even the implied warranty of 26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28* GNU General Public License for more details. 28 * GNU General Public License for more details.
29* 29 *
30* You should have received a copy of the GNU General Public License 30 * You should have received a copy of the GNU General Public License
31* along with this program. If not, see <http://www.gnu.org/licenses/>. 31 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32* 32 *
33* 33 *
34*****************************************************************************/ 34 *****************************************************************************/
35const char *progname = "check_curl";
36 35
37const char *copyright = "2006-2019"; 36const char *progname = "check_curl";
37const char *copyright = "2006-2024";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "check_curl.d/config.h"
41#include "states.h"
42#include "thresholds.h"
40#include <stdbool.h> 43#include <stdbool.h>
41#include <ctype.h> 44#include <ctype.h>
45#include "output.h"
46#include "perfdata.h"
42 47
48#include <assert.h>
43#include "common.h" 49#include "common.h"
44#include "utils.h" 50#include "utils.h"
51#include "./check_curl.d/check_curl_helpers.h"
45 52
46#ifndef LIBCURL_PROTOCOL_HTTP 53#ifndef LIBCURL_PROTOCOL_HTTP
47#error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense 54# error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense
48#endif 55#endif
49 56
50#include "curl/curl.h" 57#include "curl/curl.h"
51#include "curl/easy.h" 58#include "curl/easy.h"
52 59
53#include "picohttpparser.h"
54
55#include "uriparser/Uri.h" 60#include "uriparser/Uri.h"
56 61
57#include <arpa/inet.h> 62#include <arpa/inet.h>
58#include <netinet/in.h> 63#include <netinet/in.h>
59 64
60#if defined(HAVE_SSL) && defined(USE_OPENSSL) 65#if defined(HAVE_SSL) && defined(MOPL_USE_OPENSSL)
61#include <openssl/opensslv.h> 66# include <openssl/opensslv.h>
62#endif 67#endif
63 68
64#include <netdb.h> 69#include <netdb.h>
65 70
66#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
67
68#define DEFAULT_BUFFER_SIZE 2048
69#define DEFAULT_SERVER_URL "/"
70#define HTTP_EXPECT "HTTP/"
71#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
72enum { 71enum {
73 MAX_IPV4_HOSTLENGTH = 255, 72 REGS = 2,
74 HTTP_PORT = 80,
75 HTTPS_PORT = 443,
76 MAX_PORT = 65535,
77 DEFAULT_MAX_REDIRS = 15
78}; 73};
79 74
80enum { 75#include "regex.h"
81 STICKY_NONE = 0,
82 STICKY_HOST = 1,
83 STICKY_PORT = 2
84};
85 76
86enum { 77// Globals
87 FOLLOW_HTTP_CURL = 0, 78int verbose = 0;
88 FOLLOW_LIBCURL = 1
89};
90 79
91/* for buffers for header and body */ 80extern char errbuf[MAX_INPUT_BUFFER];
92typedef struct { 81extern bool is_openssl_callback;
93 char *buf; 82extern bool add_sslctx_verify_fun;
94 size_t buflen;
95 size_t bufsize;
96} curlhelp_write_curlbuf;
97 83
98/* for buffering the data sent in PUT */ 84#if defined(HAVE_SSL) && defined(MOPL_USE_OPENSSL)
99typedef struct { 85static X509 *cert = NULL;
100 char *buf; 86#endif /* defined(HAVE_SSL) && defined(MOPL_USE_OPENSSL) */
101 size_t buflen;
102 off_t pos;
103} curlhelp_read_curlbuf;
104 87
105/* for parsing the HTTP status line */
106typedef struct { 88typedef struct {
107 int http_major; /* major version of the protocol, always 1 (HTTP/0.9 89 int errorcode;
108 * never reached the big internet most likely) */ 90 check_curl_config config;
109 int http_minor; /* minor version of the protocol, usually 0 or 1 */ 91} check_curl_config_wrapper;
110 int http_code; /* HTTP return code as in RFC 2145 */ 92static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
111 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
112 * http://support.microsoft.com/kb/318380/en-us */
113 const char *msg; /* the human readable message */
114 char *first_line; /* a copy of the first line */
115} curlhelp_statusline;
116
117/* to know the underlying SSL library used by libcurl */
118typedef enum curlhelp_ssl_library {
119 CURLHELP_SSL_LIBRARY_UNKNOWN,
120 CURLHELP_SSL_LIBRARY_OPENSSL,
121 CURLHELP_SSL_LIBRARY_LIBRESSL,
122 CURLHELP_SSL_LIBRARY_GNUTLS,
123 CURLHELP_SSL_LIBRARY_NSS
124} curlhelp_ssl_library;
125 93
126enum { 94static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState,
127 REGS = 2, 95 long redir_depth);
128 MAX_RE_SIZE = 1024
129};
130#include "regex.h"
131regex_t preg;
132regmatch_t pmatch[REGS];
133char regexp[MAX_RE_SIZE];
134int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
135int errcode;
136bool invert_regex = false;
137int state_regex = STATE_CRITICAL;
138
139char *server_address = NULL;
140char *host_name = NULL;
141char *server_url = 0;
142char server_ip[DEFAULT_BUFFER_SIZE];
143struct curl_slist *server_ips = NULL;
144bool specify_port = false;
145unsigned short server_port = HTTP_PORT;
146unsigned short virtual_port = 0;
147int host_name_length;
148char output_header_search[30] = "";
149char output_string_search[30] = "";
150char *warning_thresholds = NULL;
151char *critical_thresholds = NULL;
152int days_till_exp_warn, days_till_exp_crit;
153thresholds *thlds;
154char user_agent[DEFAULT_BUFFER_SIZE];
155int verbose = 0;
156bool show_extended_perfdata = false;
157bool show_body = false;
158int min_page_len = 0;
159int max_page_len = 0;
160int redir_depth = 0;
161int max_depth = DEFAULT_MAX_REDIRS;
162char *http_method = NULL;
163char *http_post_data = NULL;
164char *http_content_type = NULL;
165CURL *curl;
166bool curl_global_initialized = false;
167bool curl_easy_initialized = false;
168struct curl_slist *header_list = NULL;
169bool body_buf_initialized = false;
170curlhelp_write_curlbuf body_buf;
171bool header_buf_initialized = false;
172curlhelp_write_curlbuf header_buf;
173bool status_line_initialized = false;
174curlhelp_statusline status_line;
175bool put_buf_initialized = false;
176curlhelp_read_curlbuf put_buf;
177char http_header[DEFAULT_BUFFER_SIZE];
178long code;
179long socket_timeout = DEFAULT_SOCKET_TIMEOUT;
180double total_time;
181double time_connect;
182double time_appconnect;
183double time_headers;
184double time_firstbyte;
185char errbuf[MAX_INPUT_BUFFER];
186CURLcode res;
187char url[DEFAULT_BUFFER_SIZE];
188char msg[DEFAULT_BUFFER_SIZE];
189char perfstring[DEFAULT_BUFFER_SIZE];
190char header_expect[MAX_INPUT_BUFFER] = "";
191char string_expect[MAX_INPUT_BUFFER] = "";
192char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
193int server_expect_yn = 0;
194char user_auth[MAX_INPUT_BUFFER] = "";
195char proxy_auth[MAX_INPUT_BUFFER] = "";
196char **http_opt_headers;
197int http_opt_headers_count = 0;
198bool display_html = false;
199int onredirect = STATE_OK;
200int followmethod = FOLLOW_HTTP_CURL;
201int followsticky = STICKY_NONE;
202bool use_ssl = false;
203bool use_sni = true;
204bool check_cert = false;
205bool continue_after_check_cert = false;
206typedef union {
207 struct curl_slist* to_info;
208 struct curl_certinfo* to_certinfo;
209} cert_ptr_union;
210cert_ptr_union cert_ptr;
211int ssl_version = CURL_SSLVERSION_DEFAULT;
212char *client_cert = NULL;
213char *client_privkey = NULL;
214char *ca_cert = NULL;
215bool verify_peer_and_host = false;
216bool is_openssl_callback = false;
217#if defined(HAVE_SSL) && defined(USE_OPENSSL)
218X509 *cert = NULL;
219#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
220bool no_body = false;
221int maximum_age = -1;
222int address_family = AF_UNSPEC;
223curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
224int curl_http_version = CURL_HTTP_VERSION_NONE;
225bool automatic_decompression = false;
226char *cookie_jar_file = NULL;
227bool haproxy_protocol = false;
228
229bool process_arguments (int, char**);
230void handle_curl_option_return_code (CURLcode res, const char* option);
231int check_http (void);
232void redir (curlhelp_write_curlbuf*);
233char *perfd_time (double microsec);
234char *perfd_time_connect (double microsec);
235char *perfd_time_ssl (double microsec);
236char *perfd_time_firstbyte (double microsec);
237char *perfd_time_headers (double microsec);
238char *perfd_time_transfer (double microsec);
239char *perfd_size (int page_len);
240void print_help (void);
241void print_usage (void);
242void print_curl_version (void);
243int curlhelp_initwritebuffer (curlhelp_write_curlbuf*);
244size_t curlhelp_buffer_write_callback(void*, size_t , size_t , void*);
245void curlhelp_freewritebuffer (curlhelp_write_curlbuf*);
246int curlhelp_initreadbuffer (curlhelp_read_curlbuf *, const char *, size_t);
247size_t curlhelp_buffer_read_callback(void *, size_t , size_t , void *);
248void curlhelp_freereadbuffer (curlhelp_read_curlbuf *);
249curlhelp_ssl_library curlhelp_get_ssl_library ();
250const char* curlhelp_get_ssl_library_string (curlhelp_ssl_library);
251int net_noopenssl_check_certificate (cert_ptr_union*, int, int);
252
253int curlhelp_parse_statusline (const char*, curlhelp_statusline *);
254void curlhelp_free_statusline (curlhelp_statusline *);
255char *get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header);
256int check_document_dates (const curlhelp_write_curlbuf *, char (*msg)[DEFAULT_BUFFER_SIZE]);
257int get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf);
258
259#if defined(HAVE_SSL) && defined(USE_OPENSSL)
260int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit);
261#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
262
263void remove_newlines (char *);
264void test_file (char *);
265
266int
267main (int argc, char **argv)
268{
269 int result = STATE_UNKNOWN;
270
271 setlocale (LC_ALL, "");
272 bindtextdomain (PACKAGE, LOCALEDIR);
273 textdomain (PACKAGE);
274
275 /* Parse extra opts if any */
276 argv = np_extra_opts (&argc, argv, progname);
277
278 /* set defaults */
279 snprintf( user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)",
280 progname, NP_VERSION, VERSION, curl_version());
281
282 /* parse arguments */
283 if (process_arguments (argc, argv) == false)
284 usage4 (_("Could not parse arguments"));
285
286 if (display_html)
287 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
288 use_ssl ? "https" : "http",
289 host_name ? host_name : server_address,
290 virtual_port ? virtual_port : server_port,
291 server_url);
292
293 result = check_http ();
294 return result;
295}
296 96
297#ifdef HAVE_SSL 97typedef struct {
298#ifdef USE_OPENSSL 98 long redir_depth;
299 99 check_curl_working_state working_state;
300int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) 100 int error_code;
301{ 101 check_curl_global_state curl_state;
302 (void) preverify_ok; 102} redir_wrapper;
303 /* TODO: we get all certificates of the chain, so which ones 103static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/,
304 * should we test? 104 long redir_depth, check_curl_working_state working_state);
305 * TODO: is the last certificate always the server certificate? 105
306 */ 106static void print_help(void);
307 cert = X509_STORE_CTX_get_current_cert(x509_ctx); 107void print_usage(void);
308#if OPENSSL_VERSION_NUMBER >= 0x10100000L 108
309 X509_up_ref(cert); 109static void print_curl_version(void);
310#endif 110
311 if (verbose>=2) { 111// typedef struct {
312 puts("* SSL verify callback with certificate:"); 112// int errorcode;
313 X509_NAME *subject, *issuer; 113// } check_curl_evaluation_wrapper;
314 printf("* issuer:\n"); 114// check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config,
315 issuer = X509_get_issuer_name( cert ); 115// mp_check overall[static 1]) {}
316 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); 116
317 printf("* curl verify_callback:\n* subject:\n"); 117#if defined(HAVE_SSL) && defined(MOPL_USE_OPENSSL)
318 subject = X509_get_subject_name( cert ); 118mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
319 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); 119 int days_till_exp_crit);
320 puts(""); 120#endif /* defined(HAVE_SSL) && defined(MOPL_USE_OPENSSL) */
321 } 121
322 return 1; 122int main(int argc, char **argv) {
323} 123#ifdef __OpenBSD__
324 124 /* - rpath is required to read --extra-opts, CA and/or client certs
325CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) 125 * - wpath is required to write --cookie-jar (possibly given up later)
326{ 126 * - inet is required for sockets
327 (void) curl; // ignore unused parameter 127 * - dns is required for name lookups */
328 (void) parm; // ignore unused parameter 128 pledge("stdio rpath wpath inet dns", NULL);
329 SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback); 129#endif // __OpenBSD__
130
131 setlocale(LC_ALL, "");
132 bindtextdomain(PACKAGE, LOCALEDIR);
133 textdomain(PACKAGE);
134
135 /* Parse extra opts if any */
136 argv = np_extra_opts(&argc, argv, progname);
137
138 /* parse arguments */
139 check_curl_config_wrapper tmp_config = process_arguments(argc, argv);
140 if (tmp_config.errorcode == ERROR) {
141 usage4(_("Could not parse arguments"));
142 }
330 143
331 return CURLE_OK; 144 const check_curl_config config = tmp_config.config;
332}
333 145
334#endif /* USE_OPENSSL */ 146#ifdef __OpenBSD__
335#endif /* HAVE_SSL */ 147 if (!config.curl_config.cookie_jar_file) {
336 148 if (verbose >= 2) {
337/* returns a string "HTTP/1.x" or "HTTP/2" */ 149 printf(_("* No \"--cookie-jar\" is used, giving up \"wpath\" pledge(2)\n"));
338static char *string_statuscode (int major, int minor) 150 }
339{ 151 pledge("stdio rpath inet dns", NULL);
340 static char buf[10]; 152 }
341 153#endif // __OpenBSD__
342 switch (major) {
343 case 1:
344 snprintf (buf, sizeof (buf), "HTTP/%d.%d", major, minor);
345 break;
346 case 2:
347 case 3:
348 snprintf (buf, sizeof (buf), "HTTP/%d", major);
349 break;
350 default:
351 /* assuming here HTTP/N with N>=4 */
352 snprintf (buf, sizeof (buf), "HTTP/%d", major);
353 break;
354 }
355
356 return buf;
357}
358 154
359/* Checks if the server 'reply' is one of the expected 'statuscodes' */ 155 if (config.output_format_is_set) {
360static int 156 mp_set_format(config.output_format);
361expected_statuscode (const char *reply, const char *statuscodes) 157 }
362{
363 char *expected, *code;
364 int result = 0;
365 158
366 if ((expected = strdup (statuscodes)) == NULL) 159 check_curl_working_state working_state = config.initial_config;
367 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
368 160
369 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ",")) 161 mp_check overall = mp_check_init();
370 if (strstr (reply, code) != NULL) { 162 mp_subcheck sc_test = check_http(config, working_state, 0);
371 result = 1;
372 break;
373 }
374 163
375 free (expected); 164 mp_add_subcheck_to_check(&overall, sc_test);
376 return result;
377}
378 165
379void 166 mp_exit(overall);
380handle_curl_option_return_code (CURLcode res, const char* option)
381{
382 if (res != CURLE_OK) {
383 snprintf (msg,
384 DEFAULT_BUFFER_SIZE,
385 _("Error while setting cURL option '%s': cURL returned %d - %s"),
386 option,
387 res,
388 curl_easy_strerror(res));
389 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
390 }
391} 167}
392 168
393int 169#ifdef HAVE_SSL
394lookup_host (const char *host, char *buf, size_t buflen) 170# ifdef MOPL_USE_OPENSSL
395{ 171int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
396 struct addrinfo hints, *res, *result; 172 (void)preverify_ok;
397 char addrstr[100]; 173 /* TODO: we get all certificates of the chain, so which ones
398 size_t addrstr_len; 174 * should we test?
399 int errcode; 175 * TODO: is the last certificate always the server certificate?
400 void *ptr = { 0 }; 176 */
401 size_t buflen_remaining = buflen - 1; 177 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
402 178# if OPENSSL_VERSION_NUMBER >= 0x10100000L
403 memset (&hints, 0, sizeof (hints)); 179 X509_up_ref(cert);
404 hints.ai_family = address_family; 180# endif
405 hints.ai_socktype = SOCK_STREAM; 181 if (verbose >= 2) {
406 hints.ai_flags |= AI_CANONNAME; 182 puts("* SSL verify callback with certificate:");
407 183 printf("* issuer:\n");
408 errcode = getaddrinfo (host, NULL, &hints, &result); 184 X509_NAME *issuer = X509_get_issuer_name(cert);
409 if (errcode != 0) 185 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE);
410 return errcode; 186 printf("* curl verify_callback:\n* subject:\n");
411 187 X509_NAME *subject = X509_get_subject_name(cert);
412 strcpy(buf, ""); 188 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE);
413 res = result; 189 puts("");
414 190 }
415 while (res) { 191 return 1;
416 switch (res->ai_family) {
417 case AF_INET:
418 ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
419 break;
420 case AF_INET6:
421 ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
422 break;
423 }
424
425 inet_ntop (res->ai_family, ptr, addrstr, 100);
426 if (verbose >= 1) {
427 printf ("* getaddrinfo IPv%d address: %s\n",
428 res->ai_family == PF_INET6 ? 6 : 4, addrstr);
429 }
430
431 // Append all IPs to buf as a comma-separated string
432 addrstr_len = strlen(addrstr);
433 if (buflen_remaining > addrstr_len + 1) {
434 if (buf[0] != '\0') {
435 strncat(buf, ",", buflen_remaining);
436 buflen_remaining -= 1;
437 }
438 strncat(buf, addrstr, buflen_remaining);
439 buflen_remaining -= addrstr_len;
440 }
441
442 res = res->ai_next;
443 }
444
445 freeaddrinfo(result);
446
447 return 0;
448} 192}
193# endif /* MOPL_USE_OPENSSL */
194#endif /* HAVE_SSL */
449 195
450static void 196#ifdef HAVE_SSL
451cleanup (void) 197# ifdef MOPL_USE_OPENSSL
452{ 198CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
453 if (status_line_initialized) curlhelp_free_statusline(&status_line); 199 (void)curl; // ignore unused parameter
454 status_line_initialized = false; 200 (void)parm; // ignore unused parameter
455 if (curl_easy_initialized) curl_easy_cleanup (curl); 201 if (add_sslctx_verify_fun) {
456 curl_easy_initialized = false; 202 SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback);
457 if (curl_global_initialized) curl_global_cleanup (); 203 }
458 curl_global_initialized = false;
459 if (body_buf_initialized) curlhelp_freewritebuffer (&body_buf);
460 body_buf_initialized = false;
461 if (header_buf_initialized) curlhelp_freewritebuffer (&header_buf);
462 header_buf_initialized = false;
463 if (put_buf_initialized) curlhelp_freereadbuffer (&put_buf);
464 put_buf_initialized = false;
465}
466 204
467int 205 // workaround for issue:
468check_http (void) 206 // OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0
469{ 207 // see discussion https://github.com/openssl/openssl/discussions/22690
470 int result = STATE_OK; 208# ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
471 int result_ssl = STATE_OK; 209 SSL_CTX_set_options(sslctx, SSL_OP_IGNORE_UNEXPECTED_EOF);
472 int page_len = 0; 210# endif
473 int i;
474 char *force_host_header = NULL;
475 struct curl_slist *host = NULL;
476 char addrstr[DEFAULT_BUFFER_SIZE/2];
477 char dnscache[DEFAULT_BUFFER_SIZE];
478
479 /* initialize curl */
480 if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK)
481 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
482 curl_global_initialized = true;
483
484 if ((curl = curl_easy_init()) == NULL) {
485 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
486 }
487 curl_easy_initialized = true;
488
489 /* register cleanup function to shut down libcurl properly */
490 atexit (cleanup);
491
492 if (verbose >= 1)
493 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE");
494
495 /* print everything on stdout like check_http would do */
496 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR");
497
498 if (automatic_decompression)
499#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
500 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING");
501#else
502 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
503#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
504
505 /* initialize buffer for body of the answer */
506 if (curlhelp_initwritebuffer(&body_buf) < 0)
507 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
508 body_buf_initialized = true;
509 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION");
510 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA");
511
512 /* initialize buffer for header of the answer */
513 if (curlhelp_initwritebuffer( &header_buf ) < 0)
514 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n" );
515 header_buf_initialized = true;
516 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION");
517 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER");
518
519 /* set the error buffer */
520 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER");
521
522 /* set timeouts */
523 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT");
524 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT");
525
526 /* enable haproxy protocol */
527 if (haproxy_protocol) {
528 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL");
529 }
530
531 // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we use the host_name later on to make SNI happy
532 if(use_ssl && host_name != NULL) {
533 if ( (res=lookup_host (server_address, addrstr, DEFAULT_BUFFER_SIZE/2)) != 0) {
534 snprintf (msg,
535 DEFAULT_BUFFER_SIZE,
536 _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"),
537 server_address,
538 res,
539 gai_strerror (res));
540 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
541 }
542 snprintf (dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr);
543 host = curl_slist_append(NULL, dnscache);
544 curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
545 if (verbose>=1)
546 printf ("* curl CURLOPT_RESOLVE: %s\n", dnscache);
547 }
548
549 // If server_address is an IPv6 address it must be surround by square brackets
550 struct in6_addr tmp_in_addr;
551 if (inet_pton(AF_INET6, server_address, &tmp_in_addr) == 1) {
552 char *new_server_address = malloc(strlen(server_address) + 3);
553 if (new_server_address == NULL) {
554 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
555 }
556 snprintf(new_server_address, strlen(server_address)+3, "[%s]", server_address);
557 free(server_address);
558 server_address = new_server_address;
559 }
560
561 /* compose URL: use the address we want to connect to, set Host: header later */
562 snprintf (url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s",
563 use_ssl ? "https" : "http",
564 ( use_ssl & ( host_name != NULL ) ) ? host_name : server_address,
565 server_port,
566 server_url
567 );
568
569 if (verbose>=1)
570 printf ("* curl CURLOPT_URL: %s\n", url);
571 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, url), "CURLOPT_URL");
572
573 /* extract proxy information for legacy proxy https requests */
574 if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) {
575 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY");
576 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT");
577 if (verbose>=2)
578 printf ("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port);
579 http_method = "GET";
580 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, server_url), "CURLOPT_URL");
581 }
582
583 /* disable body for HEAD request */
584 if (http_method && !strcmp (http_method, "HEAD" )) {
585 no_body = true;
586 }
587
588 /* set HTTP protocol version */
589 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION");
590
591 /* set HTTP method */
592 if (http_method) {
593 if (!strcmp(http_method, "POST"))
594 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POST, 1), "CURLOPT_POST");
595 else if (!strcmp(http_method, "PUT"))
596 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
597 else
598 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST");
599 }
600
601 /* check if Host header is explicitly set in options */
602 if (http_opt_headers_count) {
603 for (i = 0; i < http_opt_headers_count ; i++) {
604 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
605 force_host_header = http_opt_headers[i];
606 }
607 }
608 }
609
610 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */
611 if(host_name != NULL && force_host_header == NULL) {
612 if((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) {
613 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port);
614 } else {
615 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name);
616 }
617 header_list = curl_slist_append (header_list, http_header);
618 }
619
620 /* always close connection, be nice to servers */
621 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
622 header_list = curl_slist_append (header_list, http_header);
623
624 /* attach additional headers supplied by the user */
625 /* optionally send any other header tag */
626 if (http_opt_headers_count) {
627 for (i = 0; i < http_opt_headers_count ; i++) {
628 header_list = curl_slist_append (header_list, http_opt_headers[i]);
629 }
630 /* This cannot be free'd here because a redirection will then try to access this and segfault */
631 /* Covered in a testcase in tests/check_http.t */
632 /* free(http_opt_headers); */
633 }
634
635 /* set HTTP headers */
636 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ), "CURLOPT_HTTPHEADER");
637 211
638#ifdef LIBCURL_FEATURE_SSL 212 return CURLE_OK;
213}
214# endif /* MOPL_USE_OPENSSL */
215#endif /* HAVE_SSL */
639 216
640 /* set SSL version, warn about insecure or unsupported versions */ 217mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState,
641 if (use_ssl) { 218 long redir_depth) {
642 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION");
643 }
644
645 /* client certificate and key to present to server (SSL) */
646 if (client_cert)
647 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT");
648 if (client_privkey)
649 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
650 if (ca_cert) {
651 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
652 }
653 if (ca_cert || verify_peer_and_host) {
654 /* per default if we have a CA verify both the peer and the
655 * hostname in the certificate, can be switched off later */
656 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
657 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
658 } else {
659 /* backward-compatible behaviour, be tolerant in checks
660 * TODO: depending on more options have aspects we want
661 * to be less tolerant about ssl verfications
662 */
663 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER");
664 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST");
665 }
666
667 /* detect SSL library used by libcurl */
668 ssl_library = curlhelp_get_ssl_library ();
669
670 /* try hard to get a stack of certificates to verify against */
671 if (check_cert) {
672#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
673 /* inform curl to report back certificates */
674 switch (ssl_library) {
675 case CURLHELP_SSL_LIBRARY_OPENSSL:
676 case CURLHELP_SSL_LIBRARY_LIBRESSL:
677 /* set callback to extract certificate with OpenSSL context function (works with
678 * OpenSSL-style libraries only!) */
679#ifdef USE_OPENSSL
680 /* libcurl and monitoring plugins built with OpenSSL, good */
681 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
682 is_openssl_callback = true;
683#else /* USE_OPENSSL */
684#endif /* USE_OPENSSL */
685 /* libcurl is built with OpenSSL, monitoring plugins, so falling
686 * back to manually extracting certificate information */
687 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
688 break;
689
690 case CURLHELP_SSL_LIBRARY_NSS:
691#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
692 /* NSS: support for CERTINFO is implemented since 7.34.0 */
693 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
694#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
695 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library));
696#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
697 break;
698
699 case CURLHELP_SSL_LIBRARY_GNUTLS:
700#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
701 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
702 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
703#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
704 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library));
705#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
706 break;
707
708 case CURLHELP_SSL_LIBRARY_UNKNOWN:
709 default:
710 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", curlhelp_get_ssl_library_string (ssl_library));
711 break;
712 }
713#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
714 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
715 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL)
716 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
717 else
718 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl too old and has no CURLOPT_CERTINFO)\n");
719#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
720 }
721 219
722#endif /* LIBCURL_FEATURE_SSL */ 220 // =======================
221 // Initialisation for curl
222 // =======================
223 check_curl_configure_curl_wrapper conf_curl_struct = check_curl_configure_curl(
224 config.curl_config, workingState, config.check_cert, config.on_redirect_dependent,
225 config.followmethod, config.max_depth);
723 226
724 /* set default or user-given user agent identification */ 227 check_curl_global_state curl_state = conf_curl_struct.curl_state;
725 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT"); 228 workingState = conf_curl_struct.working_state;
726
727 /* proxy-authentication */
728 if (strcmp(proxy_auth, ""))
729 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD");
730
731 /* authentication */
732 if (strcmp(user_auth, ""))
733 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD");
734
735 /* TODO: parameter auth method, bitfield of following methods:
736 * CURLAUTH_BASIC (default)
737 * CURLAUTH_DIGEST
738 * CURLAUTH_DIGEST_IE
739 * CURLAUTH_NEGOTIATE
740 * CURLAUTH_NTLM
741 * CURLAUTH_NTLM_WB
742 *
743 * convenience tokens for typical sets of methods:
744 * CURLAUTH_ANYSAFE: most secure, without BASIC
745 * or CURLAUTH_ANY: most secure, even BASIC if necessary
746 *
747 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
748 */
749
750 /* handle redirections */
751 if (onredirect == STATE_DEPENDENT) {
752 if( followmethod == FOLLOW_LIBCURL ) {
753 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
754
755 /* default -1 is infinite, not good, could lead to zombie plugins!
756 Setting it to one bigger than maximal limit to handle errors nicely below
757 */
758 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_MAXREDIRS, max_depth+1), "CURLOPT_MAXREDIRS");
759
760 /* for now allow only http and https (we are a http(s) check plugin in the end) */
761#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
762 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), "CURLOPT_REDIR_PROTOCOLS_STR");
763#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
764 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), "CURLOPT_REDIRECT_PROTOCOLS");
765#endif
766 229
767 /* TODO: handle the following aspects of redirection, make them 230 mp_subcheck sc_result = mp_subcheck_init();
768 * command line options too later:
769 CURLOPT_POSTREDIR: method switch
770 CURLINFO_REDIRECT_URL: custom redirect option
771 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
772 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size?
773 */
774 } else {
775 /* old style redirection is handled below */
776 }
777 }
778
779 /* no-body */
780 if (no_body)
781 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY");
782
783 /* IPv4 or IPv6 forced DNS resolution */
784 if (address_family == AF_UNSPEC)
785 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
786 else if (address_family == AF_INET)
787 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
788#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
789 else if (address_family == AF_INET6)
790 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
791#endif
792 231
793 /* either send http POST data (any data, not only POST)*/ 232 char *url = fmt_url(workingState);
794 if (!strcmp(http_method, "POST") ||!strcmp(http_method, "PUT")) { 233 xasprintf(&sc_result.output, "Testing %s", url);
795 /* set content of payload for POST and PUT */ 234 // TODO add some output here URL or something
796 if (http_content_type) { 235 free(url);
797 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type);
798 header_list = curl_slist_append (header_list, http_header);
799 }
800 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
801 * in case of no POST/PUT data */
802 if (!http_post_data)
803 http_post_data = "";
804 if (!strcmp(http_method, "POST")) {
805 /* POST method, set payload with CURLOPT_POSTFIELDS */
806 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS");
807 } else if (!strcmp(http_method, "PUT")) {
808 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION");
809 if (curlhelp_initreadbuffer (&put_buf, http_post_data, strlen (http_post_data)) < 0)
810 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n");
811 put_buf_initialized = true;
812 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA");
813 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_INFILESIZE, (curl_off_t)strlen (http_post_data)), "CURLOPT_INFILESIZE");
814 }
815 }
816
817 /* cookie handling */
818 if (cookie_jar_file != NULL) {
819 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR");
820 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE");
821 }
822
823 /* do the request */
824 res = curl_easy_perform(curl);
825
826 if (verbose>=2 && http_post_data)
827 printf ("**** REQUEST CONTENT ****\n%s\n", http_post_data);
828
829 /* free header and server IP resolve lists, we don't need it anymore */
830 curl_slist_free_all (header_list); header_list = NULL;
831 curl_slist_free_all (server_ips); server_ips = NULL;
832 if (host) {
833 curl_slist_free_all (host); host = NULL;
834 }
835
836 /* Curl errors, result in critical Nagios state */
837 if (res != CURLE_OK) {
838 snprintf (msg,
839 DEFAULT_BUFFER_SIZE,
840 _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"),
841 server_port,
842 res,
843 errbuf[0] ? errbuf : curl_easy_strerror(res));
844 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
845 }
846
847 /* certificate checks */
848#ifdef LIBCURL_FEATURE_SSL
849 if (use_ssl) {
850 if (check_cert) {
851 if (is_openssl_callback) {
852#ifdef USE_OPENSSL
853 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
854 * and we actually have OpenSSL in the monitoring tools
855 */
856 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
857 if (!continue_after_check_cert) {
858 return result_ssl;
859 }
860#else /* USE_OPENSSL */
861 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n");
862#endif /* USE_OPENSSL */
863 } else {
864 int i;
865 struct curl_slist *slist;
866
867 cert_ptr.to_info = NULL;
868 res = curl_easy_getinfo (curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
869 if (!res && cert_ptr.to_info) {
870#ifdef USE_OPENSSL
871 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing
872 * We only check the first certificate and assume it's the one of the server
873 */
874 const char* raw_cert = NULL;
875 for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
876 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
877 if (verbose >= 2)
878 printf ("%d ** %s\n", i, slist->data);
879 if (strncmp (slist->data, "Cert:", 5) == 0) {
880 raw_cert = &slist->data[5];
881 goto GOT_FIRST_CERT;
882 }
883 }
884 }
885GOT_FIRST_CERT:
886 if (!raw_cert) {
887 snprintf (msg,
888 DEFAULT_BUFFER_SIZE,
889 _("Cannot retrieve certificates from CERTINFO information - certificate data was empty"));
890 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
891 }
892 BIO* cert_BIO = BIO_new (BIO_s_mem());
893 BIO_write (cert_BIO, raw_cert, strlen(raw_cert));
894 cert = PEM_read_bio_X509 (cert_BIO, NULL, NULL, NULL);
895 if (!cert) {
896 snprintf (msg,
897 DEFAULT_BUFFER_SIZE,
898 _("Cannot read certificate from CERTINFO information - BIO error"));
899 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
900 }
901 BIO_free (cert_BIO);
902 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
903 if (!continue_after_check_cert) {
904 return result_ssl;
905 }
906#else /* USE_OPENSSL */
907 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal,
908 * so we use the libcurl CURLINFO data
909 */
910 result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit);
911 if (!continue_after_check_cert) {
912 return result_ssl;
913 }
914#endif /* USE_OPENSSL */
915 } else {
916 snprintf (msg,
917 DEFAULT_BUFFER_SIZE,
918 _("Cannot retrieve certificates - cURL returned %d - %s"),
919 res,
920 curl_easy_strerror(res));
921 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
922 }
923 }
924 }
925 }
926#endif /* LIBCURL_FEATURE_SSL */
927 236
928 /* we got the data and we executed the request in a given time, so we can append 237 // ==============
929 * performance data to the answer always 238 // do the request
930 */ 239 // ==============
931 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); 240 CURLcode res = curl_easy_perform(curl_state.curl);
932 page_len = get_content_length(&header_buf, &body_buf);
933 if(show_extended_perfdata) {
934 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME");
935 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME");
936 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME");
937 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME");
938 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s",
939 perfd_time(total_time),
940 perfd_size(page_len),
941 perfd_time_connect(time_connect),
942 use_ssl ? perfd_time_ssl (time_appconnect-time_connect) : "",
943 perfd_time_headers(time_headers - time_appconnect),
944 perfd_time_firstbyte(time_firstbyte - time_headers),
945 perfd_time_transfer(total_time-time_firstbyte)
946 );
947 } else {
948 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s",
949 perfd_time(total_time),
950 perfd_size(page_len)
951 );
952 }
953
954 /* return a CRITICAL status if we couldn't read any data */
955 if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0)
956 die (STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n"));
957
958 /* get status line of answer, check sanity of HTTP code */
959 if (curlhelp_parse_statusline (header_buf.buf, &status_line) < 0) {
960 snprintf (msg,
961 DEFAULT_BUFFER_SIZE,
962 "Unparsable status line in %.3g seconds response time|%s\n",
963 total_time,
964 perfstring);
965 /* we cannot know the major/minor version here for sure as we cannot parse the first line */
966 die (STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg);
967 }
968 status_line_initialized = true;
969
970 /* get result code from cURL */
971 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE");
972 if (verbose>=2)
973 printf ("* curl CURLINFO_RESPONSE_CODE is %ld\n", code);
974
975 /* print status line, header, body if verbose */
976 if (verbose >= 2) {
977 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf,
978 (no_body ? " [[ skipped ]]" : body_buf.buf));
979 }
980
981 /* make sure the status line matches the response we are looking for */
982 if (!expected_statuscode(status_line.first_line, server_expect)) {
983 if (server_port == HTTP_PORT)
984 snprintf(msg,
985 DEFAULT_BUFFER_SIZE,
986 _("Invalid HTTP response received from host: %s\n"),
987 status_line.first_line);
988 else
989 snprintf(msg,
990 DEFAULT_BUFFER_SIZE,
991 _("Invalid HTTP response received from host on port %d: %s\n"),
992 server_port,
993 status_line.first_line);
994 die (STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg,
995 show_body ? "\n" : "",
996 show_body ? body_buf.buf : "");
997 }
998
999 if( server_expect_yn ) {
1000 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect);
1001 if (verbose)
1002 printf ("%s\n",msg);
1003 result = STATE_OK;
1004 }
1005 else {
1006 /* illegal return codes result in a critical state */
1007 if (code >= 600 || code < 100) {
1008 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg);
1009 /* server errors result in a critical state */
1010 } else if (code >= 500) {
1011 result = STATE_CRITICAL;
1012 /* client errors result in a warning state */
1013 } else if (code >= 400) {
1014 result = STATE_WARNING;
1015 /* check redirected page if specified */
1016 } else if (code >= 300) {
1017 if (onredirect == STATE_DEPENDENT) {
1018 if( followmethod == FOLLOW_LIBCURL ) {
1019 code = status_line.http_code;
1020 } else {
1021 /* old check_http style redirection, if we come
1022 * back here, we are in the same status as with
1023 * the libcurl method
1024 */
1025 redir (&header_buf);
1026 }
1027 } else {
1028 /* this is a specific code in the command line to
1029 * be returned when a redirection is encountered
1030 */
1031 }
1032 result = max_state_alt (onredirect, result);
1033 /* all other codes are considered ok */
1034 } else {
1035 result = STATE_OK;
1036 }
1037 }
1038
1039 /* libcurl redirection internally, handle error states here */
1040 if( followmethod == FOLLOW_LIBCURL ) {
1041 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT");
1042 if (verbose >= 2)
1043 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
1044 if (redir_depth > max_depth) {
1045 snprintf (msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl",
1046 max_depth);
1047 die (STATE_WARNING, "HTTP WARNING - %s", msg);
1048 }
1049 }
1050
1051 /* check status codes, set exit status accordingly */
1052 if( status_line.http_code != code ) {
1053 die (STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"),
1054 string_statuscode (status_line.http_major, status_line.http_minor),
1055 status_line.http_code, status_line.msg, code);
1056 }
1057
1058 if (maximum_age >= 0) {
1059 result = max_state_alt(check_document_dates(&header_buf, &msg), result);
1060 }
1061
1062 /* Page and Header content checks go here */
1063
1064 if (strlen (header_expect)) {
1065 if (!strstr (header_buf.buf, header_expect)) {
1066
1067 strncpy(&output_header_search[0],header_expect,sizeof(output_header_search));
1068
1069 if(output_header_search[sizeof(output_header_search)-1]!='\0') {
1070 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4);
1071 }
1072
1073 char tmp[DEFAULT_BUFFER_SIZE];
1074
1075 snprintf (tmp,
1076 DEFAULT_BUFFER_SIZE,
1077 _("%sheader '%s' not found on '%s://%s:%d%s', "),
1078 msg,
1079 output_header_search,
1080 use_ssl ? "https" : "http",
1081 host_name ? host_name : server_address,
1082 server_port,
1083 server_url);
1084
1085 strcpy(msg, tmp);
1086
1087 result = STATE_CRITICAL;
1088 }
1089 }
1090
1091 if (strlen (string_expect)) {
1092 if (!strstr (body_buf.buf, string_expect)) {
1093
1094 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
1095
1096 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
1097 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
1098 }
1099
1100 char tmp[DEFAULT_BUFFER_SIZE];
1101
1102 snprintf (tmp,
1103 DEFAULT_BUFFER_SIZE,
1104 _("%sstring '%s' not found on '%s://%s:%d%s', "),
1105 msg,
1106 output_string_search,
1107 use_ssl ? "https" : "http",
1108 host_name ? host_name : server_address,
1109 server_port,
1110 server_url);
1111
1112 strcpy(msg, tmp);
1113
1114 result = STATE_CRITICAL;
1115 }
1116 }
1117
1118 if (strlen (regexp)) {
1119 errcode = regexec (&preg, body_buf.buf, REGS, pmatch, 0);
1120 if ((errcode == 0 && !invert_regex) || (errcode == REG_NOMATCH && invert_regex)) {
1121 /* OK - No-op to avoid changing the logic around it */
1122 result = max_state_alt(STATE_OK, result);
1123 }
1124 else if ((errcode == REG_NOMATCH && !invert_regex) || (errcode == 0 && invert_regex)) {
1125 if (!invert_regex) {
1126 char tmp[DEFAULT_BUFFER_SIZE];
1127
1128 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg);
1129 strcpy(msg, tmp);
1130 241
1131 } else { 242 if (verbose > 1) {
1132 char tmp[DEFAULT_BUFFER_SIZE]; 243 printf("* curl_easy_perform returned: %s\n", curl_easy_strerror(res));
244 }
1133 245
1134 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); 246 if (verbose >= 2 && workingState.http_post_data) {
1135 strcpy(msg, tmp); 247 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data);
248 }
1136 249
250 // curl_state is updated after curl_easy_perform, and with updated curl_state certificate checks
251 // can be done Check_http tries to check certs as early as possible, and exits with certificate
252 // check result by default. Behave similarly.
253#ifdef LIBCURL_FEATURE_SSL
254 if (workingState.use_ssl && config.check_cert) {
255 if (verbose > 1) {
256 printf("* adding a subcheck for the certificate\n");
257 }
258 mp_subcheck sc_certificate = check_curl_certificate_checks(
259 curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit);
260
261 mp_add_subcheck_to_subcheck(&sc_result, sc_certificate);
262 if (!config.continue_after_check_cert) {
263 if (verbose > 1) {
264 printf("* returning after adding the subcheck for certificate, continuing after "
265 "checking the certificate is turned off\n");
1137 } 266 }
1138 result = state_regex; 267 return sc_result;
1139 } else {
1140 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1141
1142 char tmp[DEFAULT_BUFFER_SIZE];
1143
1144 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf);
1145 strcpy(msg, tmp);
1146 result = STATE_UNKNOWN;
1147 } 268 }
1148 } 269 }
1149 270#endif
1150 /* make sure the page is of an appropriate size */
1151 if ((max_page_len > 0) && (page_len > max_page_len)) {
1152 char tmp[DEFAULT_BUFFER_SIZE];
1153
1154 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len);
1155
1156 strcpy(msg, tmp);
1157
1158 result = max_state_alt(STATE_WARNING, result);
1159 271
1160 } else if ((min_page_len > 0) && (page_len < min_page_len)) { 272 mp_subcheck sc_curl = mp_subcheck_init();
1161 char tmp[DEFAULT_BUFFER_SIZE];
1162 273
1163 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); 274 /* Curl errors, result in critical Nagios state */
1164 strcpy(msg, tmp); 275 if (res != CURLE_OK) {
1165 result = max_state_alt(STATE_WARNING, result); 276 xasprintf(&sc_curl.output, _("Error while performing connection: cURL returned %d - %s"),
277 res, errbuf[0] ? errbuf : curl_easy_strerror(res));
278 sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL);
279 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
280 return sc_result;
1166 } 281 }
1167 282
1168 /* -w, -c: check warning and critical level */ 283 /* get status line of answer, check sanity of HTTP code */
1169 result = max_state_alt(get_status(total_time, thlds), result); 284 if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) {
1170 285 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
1171 /* Cut-off trailing characters */ 286 /* we cannot know the major/minor version here for sure as we cannot parse the first
1172 if (strlen(msg) >= 2) { 287 * line */
1173 if(msg[strlen(msg)-2] == ',') 288 xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line");
1174 msg[strlen(msg)-2] = '\0'; 289 return sc_result;
1175 else 290 }
1176 msg[strlen(msg)-3] = '\0';
1177 }
1178
1179 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */
1180 die (max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s",
1181 state_text(result), string_statuscode (status_line.http_major, status_line.http_minor),
1182 status_line.http_code, status_line.msg,
1183 strlen(msg) > 0 ? " - " : "",
1184 msg, page_len, total_time,
1185 (display_html ? "</A>" : ""),
1186 perfstring,
1187 (show_body ? body_buf.buf : ""),
1188 (show_body ? "\n" : "") );
1189
1190 return max_state_alt(result, result_ssl);
1191}
1192 291
1193int 292 curl_state.status_line_initialized = true;
1194uri_strcmp (const UriTextRangeA range, const char* s) 293
1195{ 294 size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf);
1196 if (!range.first) return -1; 295
1197 if ( (size_t)(range.afterLast - range.first) < strlen (s) ) return -1; 296 double total_time;
1198 return strncmp (s, range.first, min( (size_t)(range.afterLast - range.first), strlen (s))); 297 handle_curl_option_return_code(
1199} 298 curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time),
299 "CURLINFO_TOTAL_TIME");
300
301 xasprintf(
302 &sc_curl.output, "%s %d %s - %ld bytes in %.3f second response time",
303 string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor),
304 curl_state.status_line->http_code, curl_state.status_line->msg, page_len, total_time);
305 sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK);
306 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
307
308 // ==========
309 // Evaluation
310 // ==========
311
312 /* we got the data and we executed the request in a given time, so we can append
313 * performance data to the answer always
314 */
315
316 // total time the query took
317 mp_perfdata pd_total_time = perfdata_init();
318 mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time);
319 pd_total_time.value = pd_val_total_time;
320 pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds);
321 pd_total_time.label = "time";
322 pd_total_time.uom = "s";
323
324 mp_subcheck sc_total_time = mp_subcheck_init();
325 sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time));
326 xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time);
327 mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time);
328
329 mp_add_subcheck_to_subcheck(&sc_result, sc_total_time);
330
331 if (config.show_extended_perfdata) {
332 // overall connection time
333 mp_perfdata pd_time_connect = perfdata_init();
334 double time_connect;
335 handle_curl_option_return_code(
336 curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect),
337 "CURLINFO_CONNECT_TIME");
338
339 mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect);
340 pd_time_connect.value = pd_val_time_connect;
341 pd_time_connect.label = "time_connect";
342 pd_time_connect.uom = "s";
343 pd_time_connect = mp_set_pd_max_value(
344 pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout));
345
346 pd_time_connect = mp_pd_set_thresholds(pd_time_connect, config.thlds);
347 mp_add_perfdata_to_subcheck(&sc_result, pd_time_connect);
348
349 // application connection time, used to compute other timings
350 double time_appconnect;
351 handle_curl_option_return_code(
352 curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect),
353 "CURLINFO_APPCONNECT_TIME");
354
355 if (workingState.use_ssl) {
356 mp_perfdata pd_time_tls = perfdata_init();
357 {
358 mp_perfdata_value pd_val_time_tls =
359 mp_create_pd_value(time_appconnect - time_connect);
360
361 pd_time_tls.value = pd_val_time_tls;
362 }
363 pd_time_tls.label = "time_tls";
364 pd_time_tls.uom = "s";
365 mp_add_perfdata_to_subcheck(&sc_result, pd_time_tls);
366 }
1200 367
1201char* 368 mp_perfdata pd_time_headers = perfdata_init();
1202uri_string (const UriTextRangeA range, char* buf, size_t buflen) 369 {
1203{ 370 double time_headers;
1204 if (!range.first) return "(null)"; 371 handle_curl_option_return_code(
1205 strncpy (buf, range.first, max (buflen-1, (size_t)(range.afterLast - range.first))); 372 curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers),
1206 buf[max (buflen-1, (size_t)(range.afterLast - range.first))] = '\0'; 373 "CURLINFO_PRETRANSFER_TIME");
1207 buf[range.afterLast - range.first] = '\0';
1208 return buf;
1209}
1210 374
1211void 375 mp_perfdata_value pd_val_time_headers =
1212redir (curlhelp_write_curlbuf* header_buf) 376 mp_create_pd_value(time_headers - time_appconnect);
1213{
1214 char *location = NULL;
1215 curlhelp_statusline status_line;
1216 struct phr_header headers[255];
1217 size_t nof_headers = 255;
1218 size_t msglen;
1219 char buf[DEFAULT_BUFFER_SIZE];
1220 char ipstr[INET_ADDR_MAX_SIZE];
1221 int new_port;
1222 char *new_host;
1223 char *new_url;
1224
1225 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
1226 &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
1227 headers, &nof_headers, 0);
1228 377
1229 if (res == -1) { 378 pd_time_headers.value = pd_val_time_headers;
1230 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); 379 }
380 pd_time_headers.label = "time_headers";
381 pd_time_headers.uom = "s";
382 mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers);
383
384 mp_perfdata pd_time_firstbyte = perfdata_init();
385 double time_firstbyte;
386 handle_curl_option_return_code(
387 curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
388 "CURLINFO_STARTTRANSFER_TIME");
389
390 mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte);
391 pd_time_firstbyte.value = pd_val_time_firstbyte;
392 pd_time_firstbyte.label = "time_firstbyte";
393 pd_time_firstbyte.uom = "s";
394 mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte);
395
396 mp_perfdata pd_time_transfer = perfdata_init();
397 pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte);
398 pd_time_transfer.label = "time_transfer";
399 pd_time_transfer.uom = "s";
400 mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer);
1231 } 401 }
1232 402
1233 location = get_header_value (headers, nof_headers, "location"); 403 /* return a CRITICAL status if we couldn't read any data */
1234 404 if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) {
1235 if (verbose >= 2) 405 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
1236 printf(_("* Seen redirect location %s\n"), location); 406 xasprintf(&sc_result.output, "No header received from host");
1237 407 return sc_result;
1238 if (++redir_depth > max_depth) 408 }
1239 die (STATE_WARNING,
1240 _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"),
1241 max_depth, location, (display_html ? "</A>" : ""));
1242
1243 UriParserStateA state;
1244 UriUriA uri;
1245 state.uri = &uri;
1246 if (uriParseUriA (&state, location) != URI_SUCCESS) {
1247 if (state.errorCode == URI_ERROR_SYNTAX) {
1248 die (STATE_UNKNOWN,
1249 _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"),
1250 location, (display_html ? "</A>" : ""));
1251 } else if (state.errorCode == URI_ERROR_MALLOC) {
1252 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1253 }
1254 }
1255
1256 if (verbose >= 2) {
1257 printf (_("** scheme: %s\n"),
1258 uri_string (uri.scheme, buf, DEFAULT_BUFFER_SIZE));
1259 printf (_("** host: %s\n"),
1260 uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1261 printf (_("** port: %s\n"),
1262 uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
1263 if (uri.hostData.ip4) {
1264 inet_ntop (AF_INET, uri.hostData.ip4->data, ipstr, sizeof (ipstr));
1265 printf (_("** IPv4: %s\n"), ipstr);
1266 }
1267 if (uri.hostData.ip6) {
1268 inet_ntop (AF_INET, uri.hostData.ip6->data, ipstr, sizeof (ipstr));
1269 printf (_("** IPv6: %s\n"), ipstr);
1270 }
1271 if (uri.pathHead) {
1272 printf (_("** path: "));
1273 const UriPathSegmentA* p = uri.pathHead;
1274 for (; p; p = p->next) {
1275 printf ("/%s", uri_string (p->text, buf, DEFAULT_BUFFER_SIZE));
1276 }
1277 puts ("");
1278 }
1279 if (uri.query.first) {
1280 printf (_("** query: %s\n"),
1281 uri_string (uri.query, buf, DEFAULT_BUFFER_SIZE));
1282 }
1283 if (uri.fragment.first) {
1284 printf (_("** fragment: %s\n"),
1285 uri_string (uri.fragment, buf, DEFAULT_BUFFER_SIZE));
1286 }
1287 }
1288
1289 if (uri.scheme.first) {
1290 if (!uri_strcmp (uri.scheme, "https"))
1291 use_ssl = true;
1292 else
1293 use_ssl = false;
1294 }
1295
1296 /* we do a sloppy test here only, because uriparser would have failed
1297 * above, if the port would be invalid, we just check for MAX_PORT
1298 */
1299 if (uri.portText.first) {
1300 new_port = atoi (uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
1301 } else {
1302 new_port = HTTP_PORT;
1303 if (use_ssl)
1304 new_port = HTTPS_PORT;
1305 }
1306 if (new_port > MAX_PORT)
1307 die (STATE_UNKNOWN,
1308 _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"),
1309 MAX_PORT, location, display_html ? "</A>" : "");
1310
1311 /* by RFC 7231 relative URLs in Location should be taken relative to
1312 * the original URL, so we try to form a new absolute URL here
1313 */
1314 if (!uri.scheme.first && !uri.hostText.first) {
1315 new_host = strdup (host_name ? host_name : server_address);
1316 new_port = server_port;
1317 if(use_ssl)
1318 uri_string (uri.scheme, "https", DEFAULT_BUFFER_SIZE);
1319 } else {
1320 new_host = strdup (uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1321 }
1322
1323 /* compose new path */
1324 /* TODO: handle fragments and query part of URL */
1325 new_url = (char *)calloc( 1, DEFAULT_BUFFER_SIZE);
1326 if (uri.pathHead) {
1327 const UriPathSegmentA* p = uri.pathHead;
1328 for (; p; p = p->next) {
1329 strncat (new_url, "/", DEFAULT_BUFFER_SIZE);
1330 strncat (new_url, uri_string (p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE-1);
1331 }
1332 }
1333
1334 if (server_port==new_port &&
1335 !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
1336 (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
1337 !strcmp(server_url, new_url))
1338 die (STATE_CRITICAL,
1339 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1340 use_ssl ? "https" : "http", new_host, new_port, new_url, (display_html ? "</A>" : ""));
1341
1342 /* set new values for redirected request */
1343
1344 if (!(followsticky & STICKY_HOST)) {
1345 free (server_address);
1346 server_address = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1347 }
1348 if (!(followsticky & STICKY_PORT)) {
1349 server_port = (unsigned short)new_port;
1350 }
1351
1352 free (host_name);
1353 host_name = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1354
1355 /* reset virtual port */
1356 virtual_port = server_port;
1357
1358 free(new_host);
1359 free (server_url);
1360 server_url = new_url;
1361
1362 uriFreeUriMembersA (&uri);
1363
1364 if (verbose)
1365 printf (_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http",
1366 host_name ? host_name : server_address, server_port, server_url);
1367
1368 /* TODO: the hash component MUST be taken from the original URL and
1369 * attached to the URL in Location
1370 */
1371
1372 cleanup ();
1373 check_http ();
1374}
1375 409
1376/* check whether a file exists */ 410 /* get result code from cURL */
1377void 411 long httpReturnCode;
1378test_file (char *path) 412 handle_curl_option_return_code(
1379{ 413 curl_easy_getinfo(curl_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode),
1380 if (access(path, R_OK) == 0) 414 "CURLINFO_RESPONSE_CODE");
1381 return; 415 if (verbose >= 2) {
1382 usage2 (_("file does not exist or is not readable"), path); 416 printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode);
1383} 417 }
1384 418
1385bool 419 /* print status line, header, body if verbose */
1386process_arguments (int argc, char **argv) 420 if (verbose >= 2) {
1387{ 421 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf->buf,
1388 char *p; 422 (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf->buf));
1389 int c = 1;
1390 char *temp;
1391
1392 enum {
1393 INVERT_REGEX = CHAR_MAX + 1,
1394 SNI_OPTION,
1395 MAX_REDIRS_OPTION,
1396 CONTINUE_AFTER_CHECK_CERT,
1397 CA_CERT_OPTION,
1398 HTTP_VERSION_OPTION,
1399 AUTOMATIC_DECOMPRESSION,
1400 COOKIE_JAR,
1401 HAPROXY_PROTOCOL,
1402 STATE_REGEX
1403 };
1404
1405 int option = 0;
1406 int got_plus = 0;
1407 static struct option longopts[] = {
1408 STD_LONG_OPTS,
1409 {"link", no_argument, 0, 'L'},
1410 {"nohtml", no_argument, 0, 'n'},
1411 {"ssl", optional_argument, 0, 'S'},
1412 {"sni", no_argument, 0, SNI_OPTION},
1413 {"post", required_argument, 0, 'P'},
1414 {"method", required_argument, 0, 'j'},
1415 {"IP-address", required_argument, 0, 'I'},
1416 {"url", required_argument, 0, 'u'},
1417 {"port", required_argument, 0, 'p'},
1418 {"authorization", required_argument, 0, 'a'},
1419 {"proxy-authorization", required_argument, 0, 'b'},
1420 {"header-string", required_argument, 0, 'd'},
1421 {"string", required_argument, 0, 's'},
1422 {"expect", required_argument, 0, 'e'},
1423 {"regex", required_argument, 0, 'r'},
1424 {"ereg", required_argument, 0, 'r'},
1425 {"eregi", required_argument, 0, 'R'},
1426 {"linespan", no_argument, 0, 'l'},
1427 {"onredirect", required_argument, 0, 'f'},
1428 {"certificate", required_argument, 0, 'C'},
1429 {"client-cert", required_argument, 0, 'J'},
1430 {"private-key", required_argument, 0, 'K'},
1431 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
1432 {"verify-cert", no_argument, 0, 'D'},
1433 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
1434 {"useragent", required_argument, 0, 'A'},
1435 {"header", required_argument, 0, 'k'},
1436 {"no-body", no_argument, 0, 'N'},
1437 {"max-age", required_argument, 0, 'M'},
1438 {"content-type", required_argument, 0, 'T'},
1439 {"pagesize", required_argument, 0, 'm'},
1440 {"invert-regex", no_argument, NULL, INVERT_REGEX},
1441 {"state-regex", required_argument, 0, STATE_REGEX},
1442 {"use-ipv4", no_argument, 0, '4'},
1443 {"use-ipv6", no_argument, 0, '6'},
1444 {"extended-perfdata", no_argument, 0, 'E'},
1445 {"show-body", no_argument, 0, 'B'},
1446 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
1447 {"http-version", required_argument, 0, HTTP_VERSION_OPTION},
1448 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION},
1449 {"cookie-jar", required_argument, 0, COOKIE_JAR},
1450 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL},
1451 {0, 0, 0, 0}
1452 };
1453
1454 if (argc < 2)
1455 return false;
1456
1457 /* support check_http compatible arguments */
1458 for (c = 1; c < argc; c++) {
1459 if (strcmp ("-to", argv[c]) == 0)
1460 strcpy (argv[c], "-t");
1461 if (strcmp ("-hn", argv[c]) == 0)
1462 strcpy (argv[c], "-H");
1463 if (strcmp ("-wt", argv[c]) == 0)
1464 strcpy (argv[c], "-w");
1465 if (strcmp ("-ct", argv[c]) == 0)
1466 strcpy (argv[c], "-c");
1467 if (strcmp ("-nohtml", argv[c]) == 0)
1468 strcpy (argv[c], "-n");
1469 }
1470
1471 server_url = strdup(DEFAULT_SERVER_URL);
1472
1473 while (1) {
1474 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option);
1475 if (c == -1 || c == EOF || c == 1)
1476 break;
1477
1478 switch (c) {
1479 case 'h':
1480 print_help();
1481 exit(STATE_UNKNOWN);
1482 break;
1483 case 'V':
1484 print_revision(progname, NP_VERSION);
1485 print_curl_version();
1486 exit(STATE_UNKNOWN);
1487 break;
1488 case 'v':
1489 verbose++;
1490 break;
1491 case 't': /* timeout period */
1492 if (!is_intnonneg (optarg))
1493 usage2 (_("Timeout interval must be a positive integer"), optarg);
1494 else
1495 socket_timeout = (int)strtol (optarg, NULL, 10);
1496 break;
1497 case 'c': /* critical time threshold */
1498 critical_thresholds = optarg;
1499 break;
1500 case 'w': /* warning time threshold */
1501 warning_thresholds = optarg;
1502 break;
1503 case 'H': /* virtual host */
1504 host_name = strdup (optarg);
1505 if (host_name[0] == '[') {
1506 if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */
1507 virtual_port = atoi (p + 2);
1508 /* cut off the port */
1509 host_name_length = strlen (host_name) - strlen (p) - 1;
1510 free (host_name);
1511 host_name = strndup (optarg, host_name_length);
1512 } 423 }
1513 } else if ((p = strchr (host_name, ':')) != NULL
1514 && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */
1515 virtual_port = atoi (p);
1516 /* cut off the port */
1517 host_name_length = strlen (host_name) - strlen (p) - 1;
1518 free (host_name);
1519 host_name = strndup (optarg, host_name_length);
1520 }
1521 break;
1522 case 'I': /* internet address */
1523 server_address = strdup (optarg);
1524 break;
1525 case 'u': /* URL path */
1526 server_url = strdup (optarg);
1527 break;
1528 case 'p': /* Server port */
1529 if (!is_intnonneg (optarg))
1530 usage2 (_("Invalid port number, expecting a non-negative number"), optarg);
1531 else {
1532 if( strtol(optarg, NULL, 10) > MAX_PORT)
1533 usage2 (_("Invalid port number, supplied port number is too big"), optarg);
1534 server_port = (unsigned short)strtol(optarg, NULL, 10);
1535 specify_port = true;
1536 }
1537 break;
1538 case 'a': /* authorization info */
1539 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
1540 user_auth[MAX_INPUT_BUFFER - 1] = 0;
1541 break;
1542 case 'b': /* proxy-authorization info */
1543 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1544 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1545 break;
1546 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1547 if (! http_post_data)
1548 http_post_data = strdup (optarg);
1549 if (! http_method)
1550 http_method = strdup("POST");
1551 break;
1552 case 'j': /* Set HTTP method */
1553 if (http_method)
1554 free(http_method);
1555 http_method = strdup (optarg);
1556 break;
1557 case 'A': /* useragent */
1558 strncpy (user_agent, optarg, DEFAULT_BUFFER_SIZE);
1559 user_agent[DEFAULT_BUFFER_SIZE-1] = '\0';
1560 break;
1561 case 'k': /* Additional headers */
1562 if (http_opt_headers_count == 0)
1563 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
1564 else
1565 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
1566 http_opt_headers[http_opt_headers_count - 1] = optarg;
1567 break;
1568 case 'L': /* show html link */
1569 display_html = true;
1570 break;
1571 case 'n': /* do not show html link */
1572 display_html = false;
1573 break;
1574 case 'C': /* Check SSL cert validity */
1575#ifdef LIBCURL_FEATURE_SSL
1576 if ((temp=strchr(optarg,','))!=NULL) {
1577 *temp='\0';
1578 if (!is_intnonneg (optarg))
1579 usage2 (_("Invalid certificate expiration period"), optarg);
1580 days_till_exp_warn = atoi(optarg);
1581 *temp=',';
1582 temp++;
1583 if (!is_intnonneg (temp))
1584 usage2 (_("Invalid certificate expiration period"), temp);
1585 days_till_exp_crit = atoi (temp);
1586 }
1587 else {
1588 days_till_exp_crit=0;
1589 if (!is_intnonneg (optarg))
1590 usage2 (_("Invalid certificate expiration period"), optarg);
1591 days_till_exp_warn = atoi (optarg);
1592 }
1593 check_cert = true;
1594 goto enable_ssl;
1595#endif
1596 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
1597#ifdef HAVE_SSL
1598 continue_after_check_cert = true;
1599 break;
1600#endif
1601 case 'J': /* use client certificate */
1602#ifdef LIBCURL_FEATURE_SSL
1603 test_file(optarg);
1604 client_cert = optarg;
1605 goto enable_ssl;
1606#endif
1607 case 'K': /* use client private key */
1608#ifdef LIBCURL_FEATURE_SSL
1609 test_file(optarg);
1610 client_privkey = optarg;
1611 goto enable_ssl;
1612#endif
1613#ifdef LIBCURL_FEATURE_SSL
1614 case CA_CERT_OPTION: /* use CA chain file */
1615 test_file(optarg);
1616 ca_cert = optarg;
1617 goto enable_ssl;
1618#endif
1619#ifdef LIBCURL_FEATURE_SSL
1620 case 'D': /* verify peer certificate & host */
1621 verify_peer_and_host = true;
1622 break;
1623#endif
1624 case 'S': /* use SSL */
1625#ifdef LIBCURL_FEATURE_SSL
1626 enable_ssl:
1627 use_ssl = true;
1628 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1629 * Only set if it's non-zero. This helps when we include multiple
1630 * parameters, like -S and -C combinations */
1631 ssl_version = CURL_SSLVERSION_DEFAULT;
1632 if (c=='S' && optarg != NULL) {
1633 char *plus_ptr = strchr(optarg, '+');
1634 if (plus_ptr) {
1635 got_plus = 1;
1636 *plus_ptr = '\0';
1637 }
1638
1639 if (optarg[0] == '2')
1640 ssl_version = CURL_SSLVERSION_SSLv2;
1641 else if (optarg[0] == '3')
1642 ssl_version = CURL_SSLVERSION_SSLv3;
1643 else if (!strcmp (optarg, "1") || !strcmp (optarg, "1.0"))
1644#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1645 ssl_version = CURL_SSLVERSION_TLSv1_0;
1646#else
1647 ssl_version = CURL_SSLVERSION_DEFAULT;
1648#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1649 else if (!strcmp (optarg, "1.1"))
1650#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1651 ssl_version = CURL_SSLVERSION_TLSv1_1;
1652#else
1653 ssl_version = CURL_SSLVERSION_DEFAULT;
1654#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1655 else if (!strcmp (optarg, "1.2"))
1656#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1657 ssl_version = CURL_SSLVERSION_TLSv1_2;
1658#else
1659 ssl_version = CURL_SSLVERSION_DEFAULT;
1660#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1661 else if (!strcmp (optarg, "1.3"))
1662#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1663 ssl_version = CURL_SSLVERSION_TLSv1_3;
1664#else
1665 ssl_version = CURL_SSLVERSION_DEFAULT;
1666#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1667 else
1668 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)"));
1669 }
1670#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1671 if (got_plus) {
1672 switch (ssl_version) {
1673 case CURL_SSLVERSION_TLSv1_3:
1674 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1675 break;
1676 case CURL_SSLVERSION_TLSv1_2:
1677 case CURL_SSLVERSION_TLSv1_1:
1678 case CURL_SSLVERSION_TLSv1_0:
1679 ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1680 break;
1681 }
1682 } else {
1683 switch (ssl_version) {
1684 case CURL_SSLVERSION_TLSv1_3:
1685 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1686 break;
1687 case CURL_SSLVERSION_TLSv1_2:
1688 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1689 break;
1690 case CURL_SSLVERSION_TLSv1_1:
1691 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1692 break;
1693 case CURL_SSLVERSION_TLSv1_0:
1694 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1695 break;
1696 }
1697 }
1698#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1699 if (verbose >= 2)
1700 printf(_("* Set SSL/TLS version to %d\n"), ssl_version);
1701 if (!specify_port)
1702 server_port = HTTPS_PORT;
1703 break;
1704#else /* LIBCURL_FEATURE_SSL */
1705 /* -C -J and -K fall through to here without SSL */
1706 usage4 (_("Invalid option - SSL is not available"));
1707 break;
1708 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */
1709 use_sni = true;
1710 break;
1711#endif /* LIBCURL_FEATURE_SSL */
1712 case MAX_REDIRS_OPTION:
1713 if (!is_intnonneg (optarg))
1714 usage2 (_("Invalid max_redirs count"), optarg);
1715 else {
1716 max_depth = atoi (optarg);
1717 }
1718 break;
1719 case 'f': /* onredirect */
1720 if (!strcmp (optarg, "ok"))
1721 onredirect = STATE_OK;
1722 else if (!strcmp (optarg, "warning"))
1723 onredirect = STATE_WARNING;
1724 else if (!strcmp (optarg, "critical"))
1725 onredirect = STATE_CRITICAL;
1726 else if (!strcmp (optarg, "unknown"))
1727 onredirect = STATE_UNKNOWN;
1728 else if (!strcmp (optarg, "follow"))
1729 onredirect = STATE_DEPENDENT;
1730 else if (!strcmp (optarg, "stickyport"))
1731 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST|STICKY_PORT;
1732 else if (!strcmp (optarg, "sticky"))
1733 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST;
1734 else if (!strcmp (optarg, "follow"))
1735 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE;
1736 else if (!strcmp (optarg, "curl"))
1737 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL;
1738 else usage2 (_("Invalid onredirect option"), optarg);
1739 if (verbose >= 2)
1740 printf(_("* Following redirects set to %s\n"), state_text(onredirect));
1741 break;
1742 case 'd': /* string or substring */
1743 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
1744 header_expect[MAX_INPUT_BUFFER - 1] = 0;
1745 break;
1746 case 's': /* string or substring */
1747 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
1748 string_expect[MAX_INPUT_BUFFER - 1] = 0;
1749 break;
1750 case 'e': /* string or substring */
1751 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
1752 server_expect[MAX_INPUT_BUFFER - 1] = 0;
1753 server_expect_yn = 1;
1754 break;
1755 case 'T': /* Content-type */
1756 http_content_type = strdup (optarg);
1757 break;
1758 case 'l': /* linespan */
1759 cflags &= ~REG_NEWLINE;
1760 break;
1761 case 'R': /* regex */
1762 cflags |= REG_ICASE;
1763 // fall through
1764 case 'r': /* regex */
1765 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
1766 regexp[MAX_RE_SIZE - 1] = 0;
1767 errcode = regcomp (&preg, regexp, cflags);
1768 if (errcode != 0) {
1769 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1770 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
1771 return false;
1772 }
1773 break;
1774 case INVERT_REGEX:
1775 invert_regex = true;
1776 break;
1777 case STATE_REGEX:
1778 if (!strcmp (optarg, "critical"))
1779 state_regex = STATE_CRITICAL;
1780 else if (!strcmp (optarg, "warning"))
1781 state_regex = STATE_WARNING;
1782 else usage2 (_("Invalid state-regex option"), optarg);
1783 break;
1784 case '4':
1785 address_family = AF_INET;
1786 break;
1787 case '6':
1788#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
1789 address_family = AF_INET6;
1790#else
1791 usage4 (_("IPv6 support not available"));
1792#endif
1793 break;
1794 case 'm': /* min_page_length */
1795 {
1796 char *tmp;
1797 if (strchr(optarg, ':') != (char *)NULL) {
1798 /* range, so get two values, min:max */
1799 tmp = strtok(optarg, ":");
1800 if (tmp == NULL) {
1801 printf("Bad format: try \"-m min:max\"\n");
1802 exit (STATE_WARNING);
1803 } else
1804 min_page_len = atoi(tmp);
1805
1806 tmp = strtok(NULL, ":");
1807 if (tmp == NULL) {
1808 printf("Bad format: try \"-m min:max\"\n");
1809 exit (STATE_WARNING);
1810 } else
1811 max_page_len = atoi(tmp);
1812 } else
1813 min_page_len = atoi (optarg);
1814 break;
1815 }
1816 case 'N': /* no-body */
1817 no_body = true;
1818 break;
1819 case 'M': /* max-age */
1820 {
1821 int L = strlen(optarg);
1822 if (L && optarg[L-1] == 'm')
1823 maximum_age = atoi (optarg) * 60;
1824 else if (L && optarg[L-1] == 'h')
1825 maximum_age = atoi (optarg) * 60 * 60;
1826 else if (L && optarg[L-1] == 'd')
1827 maximum_age = atoi (optarg) * 60 * 60 * 24;
1828 else if (L && (optarg[L-1] == 's' ||
1829 isdigit (optarg[L-1])))
1830 maximum_age = atoi (optarg);
1831 else {
1832 fprintf (stderr, "unparsable max-age: %s\n", optarg);
1833 exit (STATE_WARNING);
1834 }
1835 if (verbose >= 2)
1836 printf ("* Maximal age of document set to %d seconds\n", maximum_age);
1837 }
1838 break;
1839 case 'E': /* show extended perfdata */
1840 show_extended_perfdata = true;
1841 break;
1842 case 'B': /* print body content after status line */
1843 show_body = true;
1844 break;
1845 case HTTP_VERSION_OPTION:
1846 curl_http_version = CURL_HTTP_VERSION_NONE;
1847 if (strcmp (optarg, "1.0") == 0) {
1848 curl_http_version = CURL_HTTP_VERSION_1_0;
1849 } else if (strcmp (optarg, "1.1") == 0) {
1850 curl_http_version = CURL_HTTP_VERSION_1_1;
1851 } else if ((strcmp (optarg, "2.0") == 0) || (strcmp (optarg, "2") == 0)) {
1852#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0)
1853 curl_http_version = CURL_HTTP_VERSION_2_0;
1854#else
1855 curl_http_version = CURL_HTTP_VERSION_NONE;
1856#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */
1857 } else {
1858 fprintf (stderr, "unknown http-version parameter: %s\n", optarg);
1859 exit (STATE_WARNING);
1860 }
1861 break;
1862 case AUTOMATIC_DECOMPRESSION:
1863 automatic_decompression = true;
1864 break;
1865 case COOKIE_JAR:
1866 cookie_jar_file = optarg;
1867 break;
1868 case HAPROXY_PROTOCOL:
1869 haproxy_protocol = true;
1870 break;
1871 case '?':
1872 /* print short usage statement if args not parsable */
1873 usage5 ();
1874 break;
1875 }
1876 }
1877
1878 c = optind;
1879
1880 if (server_address == NULL && c < argc)
1881 server_address = strdup (argv[c++]);
1882
1883 if (host_name == NULL && c < argc)
1884 host_name = strdup (argv[c++]);
1885
1886 if (server_address == NULL) {
1887 if (host_name == NULL)
1888 usage4 (_("You must specify a server address or host name"));
1889 else
1890 server_address = strdup (host_name);
1891 }
1892
1893 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
1894
1895 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
1896 socket_timeout = (int)thlds->critical->end + 1;
1897 if (verbose >= 2)
1898 printf ("* Socket timeout set to %ld seconds\n", socket_timeout);
1899
1900 if (http_method == NULL)
1901 http_method = strdup ("GET");
1902
1903 if (client_cert && !client_privkey)
1904 usage4 (_("If you use a client certificate you must also specify a private key file"));
1905
1906 if (virtual_port == 0)
1907 virtual_port = server_port;
1908 else {
1909 if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT))
1910 if(!specify_port)
1911 server_port = virtual_port;
1912 }
1913
1914 return true;
1915}
1916 424
1917char *perfd_time (double elapsed_time) 425 /* make sure the status line matches the response we are looking for */
1918{ 426 mp_subcheck sc_expect = mp_subcheck_init();
1919 return fperfdata ("time", elapsed_time, "s", 427 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK);
1920 thlds->warning?true:false, thlds->warning?thlds->warning->end:0, 428 if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) {
1921 thlds->critical?true:false, thlds->critical?thlds->critical->end:0, 429 if (workingState.serverPort == HTTP_PORT) {
1922 true, 0, true, socket_timeout); 430 xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"),
1923} 431 curl_state.status_line->first_line);
432 } else {
433 xasprintf(&sc_expect.output,
434 _("Invalid HTTP response received from host on port %d: %s\n"),
435 workingState.serverPort, curl_state.status_line->first_line);
436 }
437 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_CRITICAL);
438 } else {
439 xasprintf(&sc_expect.output, _("Status line output matched \"%s\""),
440 config.server_expect.string);
441 }
442 mp_add_subcheck_to_subcheck(&sc_result, sc_expect);
443
444 if (!config.server_expect.is_present) {
445 /* illegal return codes result in a critical state */
446 mp_subcheck sc_return_code = mp_subcheck_init();
447 sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK);
448 xasprintf(&sc_return_code.output, "HTTP return code: %d",
449 curl_state.status_line->http_code);
450
451 if (httpReturnCode >= 600 || httpReturnCode < 100) {
452 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
453 xasprintf(&sc_return_code.output, _("Invalid Status (%d, %.40s)"),
454 curl_state.status_line->http_code, curl_state.status_line->msg);
455 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
456 return sc_result;
457 }
1924 458
1925char *perfd_time_connect (double elapsed_time_connect) 459 // server errors result in a critical state
1926{ 460 if (httpReturnCode >= 500) {
1927 return fperfdata ("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); 461 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
1928} 462 /* client errors result in a warning state */
463 } else if (httpReturnCode >= 400) {
464 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING);
465 /* check redirected page if specified */
466 } else if (httpReturnCode >= 300) {
467 if (config.on_redirect_dependent) {
468 if (config.followmethod == FOLLOW_LIBCURL) {
469 httpReturnCode = curl_state.status_line->http_code;
470 handle_curl_option_return_code(
471 curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth),
472 "CURLINFO_REDIRECT_COUNT");
473
474 if (verbose >= 2) {
475 printf(_("* curl LIBINFO_REDIRECT_COUNT is %ld\n"), redir_depth);
476 }
477
478 mp_subcheck sc_redir_depth = mp_subcheck_init();
479 if (redir_depth > config.max_depth) {
480 xasprintf(&sc_redir_depth.output,
481 "maximum redirection depth %ld exceeded in libcurl",
482 config.max_depth);
483 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL);
484 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
485 return sc_result;
486 }
487 xasprintf(&sc_redir_depth.output, "redirection depth %ld (of a maximum %ld)",
488 redir_depth, config.max_depth);
489 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
490
491 } else {
492 /* old check_http style redirection, if we come
493 * back here, we are in the same status as with
494 * the libcurl method
495 */
496 redir_wrapper redir_result =
497 redir(curl_state.header_buf, config, redir_depth, workingState);
498 cleanup(curl_state);
499 mp_subcheck sc_redir =
500 check_http(config, redir_result.working_state, redir_result.redir_depth);
501 mp_add_subcheck_to_subcheck(&sc_result, sc_redir);
502
503 return sc_result;
504 }
505 } else {
506 /* this is a specific code in the command line to
507 * be returned when a redirection is encountered
508 */
509 sc_return_code =
510 mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state);
511 }
512 } else {
513 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK);
514 }
1929 515
1930char *perfd_time_ssl (double elapsed_time_ssl) 516 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
1931{ 517 }
1932 return fperfdata ("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1933}
1934 518
1935char *perfd_time_headers (double elapsed_time_headers) 519 /* check status codes, set exit status accordingly */
1936{ 520 if (curl_state.status_line->http_code != httpReturnCode) {
1937 return fperfdata ("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); 521 mp_subcheck sc_http_return_code_sanity = mp_subcheck_init();
1938} 522 sc_http_return_code_sanity =
523 mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL);
524 xasprintf(&sc_http_return_code_sanity.output,
525 _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"),
526 string_statuscode(curl_state.status_line->http_major,
527 curl_state.status_line->http_minor),
528 curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode);
529
530 mp_add_subcheck_to_subcheck(&sc_result, sc_http_return_code_sanity);
531 return sc_result;
532 }
1939 533
1940char *perfd_time_firstbyte (double elapsed_time_firstbyte) 534 if (config.maximum_age >= 0) {
1941{ 535 mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age);
1942 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); 536 mp_add_subcheck_to_subcheck(&sc_result, sc_max_age);
1943} 537 }
1944 538
1945char *perfd_time_transfer (double elapsed_time_transfer) 539 /* Page and Header content checks go here */
1946{ 540 if (strlen(config.header_expect)) {
1947 return fperfdata ("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); 541 mp_subcheck sc_header_expect = mp_subcheck_init();
1948} 542 sc_header_expect = mp_set_subcheck_default_state(sc_header_expect, STATE_OK);
543 xasprintf(&sc_header_expect.output, "Expect %s in header", config.header_expect);
1949 544
1950char *perfd_size (int page_len) 545 if (!strstr(curl_state.header_buf->buf, config.header_expect)) {
1951{ 546 char output_header_search[30] = "";
1952 return perfdata ("size", page_len, "B", 547 strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search));
1953 (min_page_len>0?true:false), min_page_len,
1954 (min_page_len>0?true:false), 0,
1955 true, 0, false, 0);
1956}
1957 548
1958void 549 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1959print_help (void) 550 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
1960{ 551 }
1961 print_revision (progname, NP_VERSION);
1962 552
1963 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 553 xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "),
1964 printf (COPYRIGHT, copyright, email); 554 output_header_search, workingState.use_ssl ? "https" : "http",
555 workingState.host_name ? workingState.host_name : workingState.server_address,
556 workingState.serverPort, workingState.server_url);
1965 557
1966 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); 558 sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL);
1967 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for")); 559 }
1968 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1969 printf ("%s\n", _("certificate expiration times."));
1970 printf ("\n");
1971 printf ("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1972 printf ("%s\n", _("as possible."));
1973 560
1974 printf ("\n\n"); 561 mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect);
562 }
1975 563
1976 print_usage (); 564 if (strlen(config.string_expect)) {
565 mp_subcheck sc_string_expect = mp_subcheck_init();
566 sc_string_expect = mp_set_subcheck_default_state(sc_string_expect, STATE_OK);
567 xasprintf(&sc_string_expect.output, "Expect string \"%s\" in body", config.string_expect);
1977 568
1978 printf (_("NOTE: One or both of -H and -I must be specified")); 569 if (!strstr(curl_state.body_buf->buf, config.string_expect)) {
570 char output_string_search[30] = "";
571 strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search));
1979 572
1980 printf ("\n"); 573 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
574 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
575 }
1981 576
1982 printf (UT_HELP_VRSN); 577 xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "),
1983 printf (UT_EXTRA_OPTS); 578 output_string_search, workingState.use_ssl ? "https" : "http",
579 workingState.host_name ? workingState.host_name : workingState.server_address,
580 workingState.serverPort, workingState.server_url);
1984 581
1985 printf (" %s\n", "-H, --hostname=ADDRESS"); 582 sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL);
1986 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)")); 583 }
1987 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1988 printf (" %s\n", "-I, --IP-address=ADDRESS");
1989 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1990 printf (" %s\n", "-p, --port=INTEGER");
1991 printf (" %s", _("Port number (default: "));
1992 printf ("%d)\n", HTTP_PORT);
1993 584
1994 printf (UT_IPv46); 585 mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect);
586 }
1995 587
1996#ifdef LIBCURL_FEATURE_SSL 588 if (strlen(config.regexp)) {
1997 printf (" %s\n", "-S, --ssl=VERSION[+]"); 589 mp_subcheck sc_body_regex = mp_subcheck_init();
1998 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); 590 xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp);
1999 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); 591 regmatch_t pmatch[REGS];
2000 printf (" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted."));
2001 printf (" %s\n", _("Note: SSLv2 and SSLv3 are deprecated and are usually disabled in libcurl"));
2002 printf (" %s\n", "--sni");
2003 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
2004#if LIBCURL_VERSION_NUM >= 0x071801
2005 printf (" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
2006 printf (" %s\n", _(" SNI only really works since TLSv1.0"));
2007#else
2008 printf (" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
2009#endif
2010 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
2011 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443."));
2012 printf (" %s\n", _("A STATE_WARNING is returned if the certificate has a validity less than the"));
2013 printf (" %s\n", _("first agument's value. If there is a second argument and the certificate's"));
2014 printf (" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned."));
2015 printf (" %s\n", _("(When this option is used the URL is not checked by default. You can use"));
2016 printf (" %s\n", _(" --continue-after-certificate to override this behavior)"));
2017 printf (" %s\n", "--continue-after-certificate");
2018 printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check."));
2019 printf (" %s\n", _("Does nothing unless -C is used."));
2020 printf (" %s\n", "-J, --client-cert=FILE");
2021 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
2022 printf (" %s\n", _("to be used in establishing the SSL session"));
2023 printf (" %s\n", "-K, --private-key=FILE");
2024 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
2025 printf (" %s\n", _("matching the client certificate"));
2026 printf (" %s\n", "--ca-cert=FILE");
2027 printf (" %s\n", _("CA certificate file to verify peer against"));
2028 printf (" %s\n", "-D, --verify-cert");
2029 printf (" %s\n", _("Verify the peer's SSL certificate and hostname"));
2030#endif
2031 592
2032 printf (" %s\n", "-e, --expect=STRING"); 593 int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0);
2033 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
2034 printf (" %s", _("the first (status) line of the server response (default: "));
2035 printf ("%s)\n", HTTP_EXPECT);
2036 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
2037 printf (" %s\n", "-d, --header-string=STRING");
2038 printf (" %s\n", _("String to expect in the response headers"));
2039 printf (" %s\n", "-s, --string=STRING");
2040 printf (" %s\n", _("String to expect in the content"));
2041 printf (" %s\n", "-u, --url=PATH");
2042 printf (" %s\n", _("URL to GET or POST (default: /)"));
2043 printf (" %s\n", "-P, --post=STRING");
2044 printf (" %s\n", _("URL decoded http POST data"));
2045 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
2046 printf (" %s\n", _("Set HTTP method."));
2047 printf (" %s\n", "-N, --no-body");
2048 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
2049 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
2050 printf (" %s\n", "-M, --max-age=SECONDS");
2051 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
2052 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
2053 printf (" %s\n", "-T, --content-type=STRING");
2054 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
2055 printf (" %s\n", "-l, --linespan");
2056 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
2057 printf (" %s\n", "-r, --regex, --ereg=STRING");
2058 printf (" %s\n", _("Search page for regex STRING"));
2059 printf (" %s\n", "-R, --eregi=STRING");
2060 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
2061 printf (" %s\n", "--invert-regex");
2062 printf (" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
2063 printf (" %s\n", _("can be changed with --state--regex)"));
2064 printf (" %s\n", "--regex-state=STATE");
2065 printf (" %s\n", _("Return STATE if regex is found, OK if not\n"));
2066 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
2067 printf (" %s\n", _("Username:password on sites with basic authentication"));
2068 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
2069 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
2070 printf (" %s\n", "-A, --useragent=STRING");
2071 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
2072 printf (" %s\n", "-k, --header=STRING");
2073 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
2074 printf (" %s\n", "-E, --extended-perfdata");
2075 printf (" %s\n", _("Print additional performance data"));
2076 printf (" %s\n", "-B, --show-body");
2077 printf (" %s\n", _("Print body content below status line"));
2078 printf (" %s\n", "-L, --link");
2079 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
2080 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
2081 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
2082 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
2083 printf (" %s\n", _("follow uses the old redirection algorithm of check_http."));
2084 printf (" %s\n", _("curl uses CURL_FOLLOWLOCATION built into libcurl."));
2085 printf (" %s\n", "--max-redirs=INTEGER");
2086 printf (" %s", _("Maximal number of redirects (default: "));
2087 printf ("%d)\n", DEFAULT_MAX_REDIRS);
2088 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
2089 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
2090 printf ("\n");
2091 printf (" %s\n", "--http-version=VERSION");
2092 printf (" %s\n", _("Connect via specific HTTP protocol."));
2093 printf (" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)"));
2094 printf (" %s\n", "--enable-automatic-decompression");
2095 printf (" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING)."));
2096 printf(" %s\n", "--haproxy-protocol");
2097 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL)."));
2098 printf (" %s\n", "--cookie-jar=FILE");
2099 printf (" %s\n", _("Store cookies in the cookie jar and send them out when requested."));
2100 printf ("\n");
2101
2102 printf (UT_WARN_CRIT);
2103
2104 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
2105
2106 printf (UT_VERBOSE);
2107
2108 printf ("\n");
2109 printf ("%s\n", _("Notes:"));
2110 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
2111 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
2112 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
2113 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
2114 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
2115 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
2116 594
2117#ifdef LIBCURL_FEATURE_SSL 595 if (errcode == 0) {
2118 printf ("\n"); 596 // got a match
2119 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to")); 597 if (config.invert_regex) {
2120 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 ")); 598 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
2121 printf (" %s\n", _("certificate is still valid for the specified number of days.")); 599 } else {
2122 printf ("\n"); 600 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
2123 printf (" %s\n", _("Please note that this plugin does not check if the presented server")); 601 }
2124 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate")); 602 } else if (errcode == REG_NOMATCH) {
2125 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs.")); 603 // got no match
2126 printf ("\n"); 604 xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output);
2127 printf ("%s\n", _("Examples:"));
2128 printf (" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
2129 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
2130 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
2131 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2132 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
2133 printf ("\n");
2134 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
2135 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
2136 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
2137 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
2138 printf (" %s\n\n", _("the certificate is expired."));
2139 printf ("\n");
2140 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
2141 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
2142 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
2143 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
2144 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
2145#endif
2146 605
2147 printf ("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); 606 if (config.invert_regex) {
2148 printf (" %s\n", _("It is recommended to use an environment proxy like:")); 607 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
2149 printf (" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); 608 } else {
2150 printf (" %s\n", _("legacy proxy requests in check_http style still work:")); 609 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
2151 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org")); 610 }
611 } else {
612 // error in regexec
613 char error_buffer[DEFAULT_BUFFER_SIZE];
614 regerror(errcode, &config.compiled_regex, &error_buffer[0], DEFAULT_BUFFER_SIZE);
615 xasprintf(&sc_body_regex.output, "regexec error: %s", error_buffer);
616 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_UNKNOWN);
617 }
2152 618
2153#ifdef LIBCURL_FEATURE_SSL 619 mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex);
2154 printf ("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); 620 }
2155 printf (" %s\n", _("It is recommended to use an environment proxy like:"));
2156 printf (" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
2157 printf (" %s\n", _("legacy proxy requests in check_http style still work:"));
2158 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
2159 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
2160 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
2161 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2162 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
2163 621
2164#endif 622 // size a.k.a. page length
623 mp_perfdata pd_page_length = perfdata_init();
624 mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len);
625 pd_page_length.value = pd_val_page_length;
626 pd_page_length.label = "size";
627 pd_page_length.uom = "B";
628 pd_page_length.min = mp_create_pd_value(0);
629 pd_page_length.warn = config.page_length_limits;
630 pd_page_length.warn_present = true;
631
632 /* make sure the page is of an appropriate size */
633 if (config.page_length_limits_is_set) {
634 mp_thresholds page_length_threshold = mp_thresholds_init();
635 page_length_threshold.warning = config.page_length_limits;
636 page_length_threshold.warning_is_set = true;
637
638 pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold);
639
640 mp_subcheck sc_page_length = mp_subcheck_init();
641
642 mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length);
643
644 mp_state_enum tmp_state = mp_get_pd_status(pd_page_length);
645 sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state);
646
647 switch (tmp_state) {
648 case STATE_CRITICAL:
649 case STATE_WARNING:
650 xasprintf(&sc_page_length.output, _("page size %zu violates threshold"), page_len);
651 break;
652 case STATE_OK:
653 xasprintf(&sc_page_length.output, _("page size %zu is OK"), page_len);
654 break;
655 default:
656 assert(false);
657 }
2165 658
2166 printf (UT_SUPPORT); 659 mp_add_subcheck_to_subcheck(&sc_result, sc_page_length);
660 }
2167 661
662 return sc_result;
2168} 663}
2169 664
2170 665int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) {
2171 666 if (!range.first) {
2172void 667 return -1;
2173print_usage (void) 668 }
2174{ 669 if ((size_t)(range.afterLast - range.first) < strlen(stringToCompare)) {
2175 printf ("%s\n", _("Usage:")); 670 return -1;
2176 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname); 671 }
2177 printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>] [-D]\n"); 672 return strncmp(stringToCompare, range.first,
2178 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 673 min((size_t)(range.afterLast - range.first), strlen(stringToCompare)));
2179 printf (" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n");
2180 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
2181 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
2182 printf (" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
2183 printf (" [-T <content-type>] [-j method]\n");
2184 printf (" [--http-version=<version>] [--enable-automatic-decompression]\n");
2185 printf (" [--cookie-jar=<cookie jar file>\n");
2186 printf (" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n",progname);
2187 printf (" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
2188 printf ("\n");
2189#ifdef LIBCURL_FEATURE_SSL
2190 printf ("%s\n", _("In the first form, make an HTTP request."));
2191 printf ("%s\n\n", _("In the second form, connect to the server and check the TLS certificate."));
2192#endif
2193} 674}
2194 675
2195void 676char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) {
2196print_curl_version (void) 677 if (!range.first) {
2197{ 678 return "(null)";
2198 printf( "%s\n", curl_version()); 679 }
680 strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first)));
681 buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0';
682 buf[range.afterLast - range.first] = '\0';
683 return buf;
2199} 684}
2200 685
2201int 686redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config,
2202curlhelp_initwritebuffer (curlhelp_write_curlbuf *buf) 687 long redir_depth, check_curl_working_state working_state) {
2203{ 688 curlhelp_statusline status_line;
2204 buf->bufsize = DEFAULT_BUFFER_SIZE; 689 struct phr_header headers[255];
2205 buf->buflen = 0; 690 size_t msglen;
2206 buf->buf = (char *)malloc ((size_t)buf->bufsize); 691 size_t nof_headers = 255;
2207 if (buf->buf == NULL) return -1; 692 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
2208 return 0; 693 &status_line.http_minor, &status_line.http_code, &status_line.msg,
2209} 694 &msglen, headers, &nof_headers, 0);
2210 695
2211size_t curlhelp_buffer_write_callback (void *buffer, size_t size, size_t nmemb, void *stream) 696 if (res == -1) {
2212{ 697 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2213 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream; 698 }
2214 699
2215 while (buf->bufsize < buf->buflen + size * nmemb + 1) { 700 char *location = get_header_value(headers, nof_headers, "location");
2216 buf->bufsize = buf->bufsize * 2;
2217 buf->buf = (char *)realloc (buf->buf, buf->bufsize);
2218 if (buf->buf == NULL) {
2219 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
2220 return -1;
2221 }
2222 }
2223 701
2224 memcpy (buf->buf + buf->buflen, buffer, size * nmemb); 702 if (location == NULL) {
2225 buf->buflen += size * nmemb; 703 // location header not found
2226 buf->buf[buf->buflen] = '\0'; 704 die(STATE_UNKNOWN, "HTTP UNKNOWN - could not find \"location\" header\n");
705 }
2227 706
2228 return (int)(size * nmemb); 707 if (verbose >= 2) {
2229} 708 printf(_("* Seen redirect location %s\n"), location);
709 }
2230 710
2231size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) 711 if (++redir_depth > config.max_depth) {
2232{ 712 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %ld exceeded - %s\n"),
2233 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; 713 config.max_depth, location);
714 }
2234 715
2235 size_t n = min (nmemb * size, buf->buflen - buf->pos); 716 UriParserStateA state;
717 UriUriA uri;
718 state.uri = &uri;
719 if (uriParseUriA(&state, location) != URI_SUCCESS) {
720 if (state.errorCode == URI_ERROR_SYNTAX) {
721 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'\n"),
722 location);
723 } else if (state.errorCode == URI_ERROR_MALLOC) {
724 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
725 }
726 }
2236 727
2237 memcpy (buffer, buf->buf + buf->pos, n); 728 char ipstr[INET_ADDR_MAX_SIZE];
2238 buf->pos += n; 729 char buf[DEFAULT_BUFFER_SIZE];
730 if (verbose >= 2) {
731 printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE));
732 printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
733 printf(_("** port: %s\n"), uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE));
734 if (uri.hostData.ip4) {
735 inet_ntop(AF_INET, uri.hostData.ip4->data, ipstr, sizeof(ipstr));
736 printf(_("** IPv4: %s\n"), ipstr);
737 }
738 if (uri.hostData.ip6) {
739 inet_ntop(AF_INET, uri.hostData.ip6->data, ipstr, sizeof(ipstr));
740 printf(_("** IPv6: %s\n"), ipstr);
741 }
742 if (uri.pathHead) {
743 printf(_("** path: "));
744 for (UriPathSegmentA *path_segment = uri.pathHead; path_segment;
745 path_segment = path_segment->next) {
746 printf("/%s", uri_string(path_segment->text, buf, DEFAULT_BUFFER_SIZE));
747 }
748 puts("");
749 }
750 if (uri.query.first) {
751 printf(_("** query: %s\n"), uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE));
752 }
753 if (uri.fragment.first) {
754 printf(_("** fragment: %s\n"), uri_string(uri.fragment, buf, DEFAULT_BUFFER_SIZE));
755 }
756 }
2239 757
2240 return (int)n; 758 if (uri.scheme.first) {
2241} 759 working_state.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https"));
760 }
2242 761
2243void 762 /* we do a sloppy test here only, because uriparser would have failed
2244curlhelp_freewritebuffer (curlhelp_write_curlbuf *buf) 763 * above, if the port would be invalid, we just check for MAX_PORT
2245{ 764 */
2246 free (buf->buf); 765 int new_port;
2247 buf->buf = NULL; 766 if (uri.portText.first) {
2248} 767 new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE));
768 } else {
769 new_port = HTTP_PORT;
770 if (working_state.use_ssl) {
771 new_port = HTTPS_PORT;
772 }
773 }
774 if (new_port > MAX_PORT) {
775 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s\n"), MAX_PORT,
776 location);
777 }
2249 778
2250int 779 /* by RFC 7231 relative URLs in Location should be taken relative to
2251curlhelp_initreadbuffer (curlhelp_read_curlbuf *buf, const char *data, size_t datalen) 780 * the original URL, so we try to form a new absolute URL here
2252{ 781 */
2253 buf->buflen = datalen; 782 char *new_host;
2254 buf->buf = (char *)malloc ((size_t)buf->buflen); 783 if (!uri.scheme.first && !uri.hostText.first) {
2255 if (buf->buf == NULL) return -1; 784 new_host = strdup(working_state.host_name ? working_state.host_name
2256 memcpy (buf->buf, data, datalen); 785 : working_state.server_address);
2257 buf->pos = 0; 786 new_port = working_state.serverPort;
2258 return 0; 787 if (working_state.use_ssl) {
2259} 788 uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE);
789 }
790 } else {
791 new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
792 }
2260 793
2261void 794 /* compose new path */
2262curlhelp_freereadbuffer (curlhelp_read_curlbuf *buf) 795 /* TODO: handle fragments of URL */
2263{ 796 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE);
2264 free (buf->buf); 797 if (uri.pathHead) {
2265 buf->buf = NULL; 798 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment;
2266} 799 pathSegment = pathSegment->next) {
800 strncat(new_url, "/", DEFAULT_BUFFER_SIZE);
801 strncat(new_url, uri_string(pathSegment->text, buf, DEFAULT_BUFFER_SIZE),
802 DEFAULT_BUFFER_SIZE - 1);
803 }
804 }
2267 805
2268/* TODO: where to put this, it's actually part of sstrings2 (logically)? 806 /* missing components have null,null in their UriTextRangeA
2269 */ 807 * add query parameters if they exist.
2270const char* 808 */
2271strrstr2(const char *haystack, const char *needle) 809 if (uri.query.first && uri.query.afterLast) {
2272{ 810 // Ensure we have space for '?' + query_str + '\0' ahead of time, instead of calling strncat
2273 int counter; 811 // twice
2274 size_t len; 812 size_t current_len = strlen(new_url);
2275 const char *prev_pos; 813 size_t remaining_space = DEFAULT_BUFFER_SIZE - current_len - 1;
2276 const char *pos; 814
2277 815 const char *query_str = uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE);
2278 if (haystack == NULL || needle == NULL) 816 size_t query_str_len = strlen(query_str);
2279 return NULL; 817
2280 818 if (remaining_space >= query_str_len + 1) {
2281 if (haystack[0] == '\0' || needle[0] == '\0') 819 strcat(new_url, "?");
2282 return NULL; 820 strcat(new_url, query_str);
2283 821 } else {
2284 counter = 0; 822 die(STATE_UNKNOWN,
2285 prev_pos = NULL; 823 _("HTTP UNKNOWN - No space to add query part of size %zu to the buffer, buffer has "
2286 pos = haystack; 824 "remaining size %zu"),
2287 len = strlen (needle); 825 query_str_len, current_len);
2288 for (;;) { 826 }
2289 pos = strstr (pos, needle); 827 }
2290 if (pos == NULL) {
2291 if (counter == 0)
2292 return NULL;
2293 else
2294 return prev_pos;
2295 }
2296 counter++;
2297 prev_pos = pos;
2298 pos += len;
2299 if (*pos == '\0') return prev_pos;
2300 }
2301}
2302 828
2303int 829 if (working_state.serverPort == new_port &&
2304curlhelp_parse_statusline (const char *buf, curlhelp_statusline *status_line) 830 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
2305{ 831 (working_state.host_name &&
2306 char *first_line_end; 832 !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
2307 char *p; 833 !strcmp(working_state.server_url, new_url)) {
2308 size_t first_line_len; 834 die(STATE_CRITICAL,
2309 char *pp; 835 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s\n"),
2310 const char *start; 836 working_state.use_ssl ? "https" : "http", new_host, new_port, new_url);
2311 char *first_line_buf; 837 }
2312
2313 /* find last start of a new header */
2314 start = strrstr2 (buf, "\r\nHTTP/");
2315 if (start != NULL) {
2316 start += 2;
2317 buf = start;
2318 }
2319
2320 first_line_end = strstr(buf, "\r\n");
2321 if (first_line_end == NULL) return -1;
2322
2323 first_line_len = (size_t)(first_line_end - buf);
2324 status_line->first_line = (char *)malloc (first_line_len + 1);
2325 if (status_line->first_line == NULL) return -1;
2326 memcpy (status_line->first_line, buf, first_line_len);
2327 status_line->first_line[first_line_len] = '\0';
2328 first_line_buf = strdup( status_line->first_line );
2329
2330 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
2331
2332 p = strtok(first_line_buf, "/");
2333 if( p == NULL ) { free( first_line_buf ); return -1; }
2334 if( strcmp( p, "HTTP" ) != 0 ) { free( first_line_buf ); return -1; }
2335
2336 p = strtok( NULL, " " );
2337 if( p == NULL ) { free( first_line_buf ); return -1; }
2338 if( strchr( p, '.' ) != NULL ) {
2339
2340 /* HTTP 1.x case */
2341 strtok( p, "." );
2342 status_line->http_major = (int)strtol( p, &pp, 10 );
2343 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2344 strtok( NULL, " " );
2345 status_line->http_minor = (int)strtol( p, &pp, 10 );
2346 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2347 p += 4; /* 1.x SP */
2348 } else {
2349 /* HTTP 2 case */
2350 status_line->http_major = (int)strtol( p, &pp, 10 );
2351 status_line->http_minor = 0;
2352 p += 2; /* 2 SP */
2353 }
2354
2355 /* status code: "404" or "404.1", then SP */
2356
2357 p = strtok( p, " " );
2358 if( p == NULL ) { free( first_line_buf ); return -1; }
2359 if( strchr( p, '.' ) != NULL ) {
2360 char *ppp;
2361 ppp = strtok( p, "." );
2362 status_line->http_code = (int)strtol( ppp, &pp, 10 );
2363 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2364 ppp = strtok( NULL, "" );
2365 status_line->http_subcode = (int)strtol( ppp, &pp, 10 );
2366 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2367 p += 6; /* 400.1 SP */
2368 } else {
2369 status_line->http_code = (int)strtol( p, &pp, 10 );
2370 status_line->http_subcode = -1;
2371 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2372 p += 4; /* 400 SP */
2373 }
2374
2375 /* Human readable message: "Not Found" CRLF */
2376
2377 p = strtok( p, "" );
2378 if( p == NULL ) { status_line->msg = ""; return 0; }
2379 status_line->msg = status_line->first_line + ( p - first_line_buf );
2380 free( first_line_buf );
2381
2382 return 0;
2383}
2384 838
2385void 839 /* set new values for redirected request */
2386curlhelp_free_statusline (curlhelp_statusline *status_line)
2387{
2388 free (status_line->first_line);
2389}
2390 840
2391void 841 if (!(config.followsticky & STICKY_HOST)) {
2392remove_newlines (char *s) 842 // free(working_state.server_address);
2393{ 843 working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH);
2394 char *p; 844 }
845 if (!(config.followsticky & STICKY_PORT)) {
846 working_state.serverPort = (unsigned short)new_port;
847 }
2395 848
2396 for (p = s; *p != '\0'; p++) 849 // free(working_state.host_name);
2397 if (*p == '\r' || *p == '\n') 850 working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH);
2398 *p = ' ';
2399}
2400 851
2401char * 852 /* reset virtual port */
2402get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header) 853 working_state.virtualPort = working_state.serverPort;
2403{
2404 for(size_t i = 0; i < nof_headers; i++ ) {
2405 if(headers[i].name != NULL && strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) {
2406 return strndup( headers[i].value, headers[i].value_len );
2407 }
2408 }
2409 return NULL;
2410}
2411 854
2412int 855 free(new_host);
2413check_document_dates (const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE]) 856 // free(working_state.server_url);
2414{ 857 working_state.server_url = new_url;
2415 char *server_date = NULL;
2416 char *document_date = NULL;
2417 int date_result = STATE_OK;
2418 curlhelp_statusline status_line;
2419 struct phr_header headers[255];
2420 size_t nof_headers = 255;
2421 size_t msglen;
2422
2423 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
2424 &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
2425 headers, &nof_headers, 0);
2426 858
2427 if (res == -1) { 859 uriFreeUriMembersA(&uri);
2428 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); 860
861 if (verbose) {
862 printf(_("Redirection to %s://%s:%d%s\n"), working_state.use_ssl ? "https" : "http",
863 working_state.host_name ? working_state.host_name : working_state.server_address,
864 working_state.serverPort, working_state.server_url);
2429 } 865 }
2430 866
2431 server_date = get_header_value (headers, nof_headers, "date"); 867 /* TODO: the hash component MUST be taken from the original URL and
2432 document_date = get_header_value (headers, nof_headers, "last-modified"); 868 * attached to the URL in Location
869 */
2433 870
2434 if (!server_date || !*server_date) { 871 redir_wrapper result = {
2435 char tmp[DEFAULT_BUFFER_SIZE]; 872 .redir_depth = redir_depth,
873 .working_state = working_state,
874 .error_code = OK,
875 };
876 return result;
877}
2436 878
2437 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg); 879check_curl_config_wrapper process_arguments(int argc, char **argv) {
2438 strcpy(*msg, tmp); 880 enum {
881 INVERT_REGEX = CHAR_MAX + 1,
882 SNI_OPTION,
883 MAX_REDIRS_OPTION,
884 CONTINUE_AFTER_CHECK_CERT,
885 CA_CERT_OPTION,
886 HTTP_VERSION_OPTION,
887 AUTOMATIC_DECOMPRESSION,
888 COOKIE_JAR,
889 HAPROXY_PROTOCOL,
890 STATE_REGEX,
891 OUTPUT_FORMAT,
892 NO_PROXY,
893 };
894
895 static struct option longopts[] = {
896 STD_LONG_OPTS,
897 {"link", no_argument, 0, 'L'},
898 {"nohtml", no_argument, 0, 'n'},
899 {"ssl", optional_argument, 0, 'S'},
900 {"sni", no_argument, 0, SNI_OPTION},
901 {"post", required_argument, 0, 'P'},
902 {"method", required_argument, 0, 'j'},
903 {"IP-address", required_argument, 0, 'I'},
904 {"url", required_argument, 0, 'u'},
905 {"port", required_argument, 0, 'p'},
906 {"authorization", required_argument, 0, 'a'},
907 {"proxy", required_argument, 0, 'x'},
908 {"noproxy", required_argument, 0, NO_PROXY},
909 {"proxy-authorization", required_argument, 0, 'b'},
910 {"header-string", required_argument, 0, 'd'},
911 {"string", required_argument, 0, 's'},
912 {"expect", required_argument, 0, 'e'},
913 {"regex", required_argument, 0, 'r'},
914 {"ereg", required_argument, 0, 'r'},
915 {"eregi", required_argument, 0, 'R'},
916 {"linespan", no_argument, 0, 'l'},
917 {"onredirect", required_argument, 0, 'f'},
918 {"certificate", required_argument, 0, 'C'},
919 {"client-cert", required_argument, 0, 'J'},
920 {"private-key", required_argument, 0, 'K'},
921 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
922 {"verify-cert", no_argument, 0, 'D'},
923 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
924 {"useragent", required_argument, 0, 'A'},
925 {"header", required_argument, 0, 'k'},
926 {"no-body", no_argument, 0, 'N'},
927 {"max-age", required_argument, 0, 'M'},
928 {"content-type", required_argument, 0, 'T'},
929 {"pagesize", required_argument, 0, 'm'},
930 {"invert-regex", no_argument, NULL, INVERT_REGEX},
931 {"state-regex", required_argument, 0, STATE_REGEX},
932 {"use-ipv4", no_argument, 0, '4'},
933 {"use-ipv6", no_argument, 0, '6'},
934 {"extended-perfdata", no_argument, 0, 'E'},
935 {"show-body", no_argument, 0, 'B'},
936 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
937 {"http-version", required_argument, 0, HTTP_VERSION_OPTION},
938 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION},
939 {"cookie-jar", required_argument, 0, COOKIE_JAR},
940 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL},
941 {"output-format", required_argument, 0, OUTPUT_FORMAT},
942 {0, 0, 0, 0}};
943
944 check_curl_config_wrapper result = {
945 .errorcode = OK,
946 .config = check_curl_config_init(),
947 };
948
949 if (argc < 2) {
950 result.errorcode = ERROR;
951 return result;
952 }
2439 953
2440 date_result = max_state_alt(STATE_UNKNOWN, date_result); 954 /* support check_http compatible arguments */
955 for (int index = 1; index < argc; index++) {
956 if (strcmp("-to", argv[index]) == 0) {
957 strcpy(argv[index], "-t");
958 }
959 if (strcmp("-hn", argv[index]) == 0) {
960 strcpy(argv[index], "-H");
961 }
962 if (strcmp("-wt", argv[index]) == 0) {
963 strcpy(argv[index], "-w");
964 }
965 if (strcmp("-ct", argv[index]) == 0) {
966 strcpy(argv[index], "-c");
967 }
968 if (strcmp("-nohtml", argv[index]) == 0) {
969 strcpy(argv[index], "-n");
970 }
971 }
972
973 int option = 0;
974 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
975 bool specify_port = false;
976 bool enable_tls = false;
977 char *tls_option_optarg = NULL;
978
979 while (true) {
980 int option_index = getopt_long(
981 argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:x:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB",
982 longopts, &option);
983 if (CHECK_EOF(option_index) || option_index == 1) {
984 break;
985 }
2441 986
2442 } else if (!document_date || !*document_date) { 987 switch (option_index) {
2443 char tmp[DEFAULT_BUFFER_SIZE]; 988 case 'h':
989 print_help();
990 exit(STATE_UNKNOWN);
991 break;
992 case 'V':
993 print_revision(progname, NP_VERSION);
994 print_curl_version();
995 exit(STATE_UNKNOWN);
996 break;
997 case 'v':
998 verbose++;
999 break;
1000 case 't': /* timeout period */
1001 if (!is_intnonneg(optarg)) {
1002 usage2(_("Timeout interval must be a positive integer"), optarg);
1003 } else {
1004 result.config.curl_config.socket_timeout = (int)strtol(optarg, NULL, 10);
1005 }
1006 break;
1007 case 'c': /* critical time threshold */
1008 {
1009 mp_range_parsed critical_range = mp_parse_range_string(optarg);
1010 if (critical_range.error != MP_PARSING_SUCCESS) {
1011 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg);
1012 }
1013 result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range);
1014 } break;
1015 case 'w': /* warning time threshold */
1016 {
1017 mp_range_parsed warning_range = mp_parse_range_string(optarg);
1018
1019 if (warning_range.error != MP_PARSING_SUCCESS) {
1020 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg);
1021 }
1022 result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range);
1023 } break;
1024 case 'H': /* virtual host */
1025 result.config.initial_config.host_name = strdup(optarg);
1026 char *tmp_string;
1027 size_t host_name_length;
1028 if (result.config.initial_config.host_name[0] == '[') {
1029 if ((tmp_string = strstr(result.config.initial_config.host_name, "]:")) !=
1030 NULL) { /* [IPv6]:port */
1031 result.config.initial_config.virtualPort = atoi(tmp_string + 2);
1032 /* cut off the port */
1033 host_name_length =
1034 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
1035 free(result.config.initial_config.host_name);
1036 result.config.initial_config.host_name = strndup(optarg, host_name_length);
1037 }
1038 } else if ((tmp_string = strchr(result.config.initial_config.host_name, ':')) != NULL &&
1039 strchr(++tmp_string, ':') == NULL) { /* IPv4:port or host:port */
1040 result.config.initial_config.virtualPort = atoi(tmp_string);
1041 /* cut off the port */
1042 host_name_length =
1043 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
1044 free(result.config.initial_config.host_name);
1045 result.config.initial_config.host_name = strndup(optarg, host_name_length);
1046 }
1047 break;
1048 case 'I': /* internet address */
1049 result.config.initial_config.server_address = strdup(optarg);
1050 break;
1051 case 'u': /* URL path */
1052 result.config.initial_config.server_url = strdup(optarg);
1053 break;
1054 case 'p': /* Server port */
1055 if (!is_intnonneg(optarg)) {
1056 usage2(_("Invalid port number, expecting a non-negative number"), optarg);
1057 } else {
1058 if (strtol(optarg, NULL, 10) > MAX_PORT) {
1059 usage2(_("Invalid port number, supplied port number is too big"), optarg);
1060 }
1061 result.config.initial_config.serverPort = (unsigned short)strtol(optarg, NULL, 10);
1062 specify_port = true;
1063 }
1064 break;
1065 case 'a': /* authorization info */
1066 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1);
1067 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0;
1068 break;
1069 case 'x': /* proxy info */
1070 strncpy(result.config.curl_config.proxy, optarg, DEFAULT_BUFFER_SIZE - 1);
1071 result.config.curl_config.proxy[DEFAULT_BUFFER_SIZE - 1] = 0;
1072 break;
1073 case 'b': /* proxy-authorization info */
1074 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1075 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1076 break;
1077 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1078 if (!result.config.initial_config.http_post_data) {
1079 result.config.initial_config.http_post_data = strdup(optarg);
1080 }
1081 if (!result.config.initial_config.http_method) {
1082 result.config.initial_config.http_method = strdup("POST");
1083 }
1084 break;
1085 case 'j': /* Set HTTP method */
1086 if (result.config.initial_config.http_method) {
1087 free(result.config.initial_config.http_method);
1088 }
1089 result.config.initial_config.http_method = strdup(optarg);
1090 break;
1091 case 'A': /* useragent */
1092 strncpy(result.config.curl_config.user_agent, optarg, DEFAULT_BUFFER_SIZE);
1093 result.config.curl_config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0';
1094 break;
1095 case 'k': /* Additional headers */
1096 if (result.config.curl_config.http_opt_headers_count == 0) {
1097 result.config.curl_config.http_opt_headers =
1098 malloc(sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1099 } else {
1100 result.config.curl_config.http_opt_headers =
1101 realloc(result.config.curl_config.http_opt_headers,
1102 sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1103 }
1104 result.config.curl_config
1105 .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg;
1106 break;
1107 case 'L': /* show html link */
1108 case 'n': /* do not show html link */
1109 // HTML link related options are deprecated
1110 break;
1111 case 'C': /* Check SSL cert validity */
1112#ifndef LIBCURL_FEATURE_SSL
1113 usage4(_("Invalid option - SSL is not available"));
1114#endif
1115 {
1116 char *temp;
1117 if ((temp = strchr(optarg, ',')) != NULL) {
1118 *temp = '\0';
1119 if (!is_intnonneg(optarg)) {
1120 usage2(_("Invalid certificate expiration period"), optarg);
1121 }
1122 result.config.days_till_exp_warn = atoi(optarg);
1123 *temp = ',';
1124 temp++;
1125 if (!is_intnonneg(temp)) {
1126 usage2(_("Invalid certificate expiration period"), temp);
1127 }
1128 result.config.days_till_exp_crit = atoi(temp);
1129 } else {
1130 result.config.days_till_exp_crit = 0;
1131 if (!is_intnonneg(optarg)) {
1132 usage2(_("Invalid certificate expiration period"), optarg);
1133 }
1134 result.config.days_till_exp_warn = atoi(optarg);
1135 }
1136 result.config.check_cert = true;
1137 enable_tls = true;
1138 }
1139 break;
1140 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
1141#ifdef HAVE_SSL
1142 result.config.continue_after_check_cert = true;
1143 break;
1144#endif
1145 case 'J': /* use client certificate */
1146#ifndef LIBCURL_FEATURE_SSL
1147 usage4(_("Invalid option - SSL is not available"));
1148#endif
1149 test_file(optarg);
1150 result.config.curl_config.client_cert = optarg;
1151 enable_tls = true;
1152 break;
1153 case 'K': /* use client private key */
1154#ifndef LIBCURL_FEATURE_SSL
1155 usage4(_("Invalid option - SSL is not available"));
1156#endif
1157 test_file(optarg);
1158 result.config.curl_config.client_privkey = optarg;
1159 enable_tls = true;
1160 break;
1161 case CA_CERT_OPTION: /* use CA chain file */
1162#ifndef LIBCURL_FEATURE_SSL
1163 usage4(_("Invalid option - SSL is not available"));
1164#endif
1165 test_file(optarg);
1166 result.config.curl_config.ca_cert = optarg;
1167 enable_tls = true;
1168 break;
1169 case 'D': /* verify peer certificate & host */
1170#ifndef LIBCURL_FEATURE_SSL
1171 usage4(_("Invalid option - SSL is not available"));
1172#endif
1173 result.config.curl_config.verify_peer_and_host = true;
1174 enable_tls = true;
1175 break;
1176 case 'S': /* use SSL */
1177 tls_option_optarg = optarg;
1178 enable_tls = true;
1179#ifndef LIBCURL_FEATURE_SSL
1180 usage4(_("Invalid option - SSL is not available"));
1181#endif
1182 break;
1183 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */
1184#ifndef LIBCURL_FEATURE_SSL
1185 usage4(_("Invalid option - SSL is not available"));
1186#endif /* LIBCURL_FEATURE_SSL */
1187 break;
1188 case MAX_REDIRS_OPTION:
1189 if (!is_intnonneg(optarg)) {
1190 usage2(_("Invalid max_redirs count"), optarg);
1191 } else {
1192 result.config.max_depth = atoi(optarg);
1193 }
1194 break;
1195 case 'f': /* onredirect */
1196 if (!strcmp(optarg, "ok")) {
1197 result.config.on_redirect_result_state = STATE_OK;
1198 result.config.on_redirect_dependent = false;
1199 } else if (!strcmp(optarg, "warning")) {
1200 result.config.on_redirect_result_state = STATE_WARNING;
1201 result.config.on_redirect_dependent = false;
1202 } else if (!strcmp(optarg, "critical")) {
1203 result.config.on_redirect_result_state = STATE_CRITICAL;
1204 result.config.on_redirect_dependent = false;
1205 } else if (!strcmp(optarg, "unknown")) {
1206 result.config.on_redirect_result_state = STATE_UNKNOWN;
1207 result.config.on_redirect_dependent = false;
1208 } else if (!strcmp(optarg, "follow")) {
1209 result.config.on_redirect_dependent = true;
1210 } else if (!strcmp(optarg, "stickyport")) {
1211 result.config.on_redirect_dependent = true;
1212 result.config.followmethod = FOLLOW_HTTP_CURL,
1213 result.config.followsticky = STICKY_HOST | STICKY_PORT;
1214 } else if (!strcmp(optarg, "sticky")) {
1215 result.config.on_redirect_dependent = true;
1216 result.config.followmethod = FOLLOW_HTTP_CURL,
1217 result.config.followsticky = STICKY_HOST;
1218 } else if (!strcmp(optarg, "follow")) {
1219 result.config.on_redirect_dependent = true;
1220 result.config.followmethod = FOLLOW_HTTP_CURL,
1221 result.config.followsticky = STICKY_NONE;
1222 } else if (!strcmp(optarg, "curl")) {
1223 result.config.on_redirect_dependent = true;
1224 result.config.followmethod = FOLLOW_LIBCURL;
1225 } else {
1226 usage2(_("Invalid onredirect option"), optarg);
1227 }
1228 if (verbose >= 2) {
1229 if (result.config.on_redirect_dependent) {
1230 printf(_("* Following redirects\n"));
1231 } else {
1232 printf(_("* Following redirects set to state %s\n"),
1233 state_text(result.config.on_redirect_result_state));
1234 }
1235 }
1236 break;
1237 case 'd': /* string or substring */
1238 strncpy(result.config.header_expect, optarg, MAX_INPUT_BUFFER - 1);
1239 result.config.header_expect[MAX_INPUT_BUFFER - 1] = 0;
1240 break;
1241 case 's': /* string or substring */
1242 strncpy(result.config.string_expect, optarg, MAX_INPUT_BUFFER - 1);
1243 result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0;
1244 break;
1245 case 'e': /* string or substring */
1246 strncpy(result.config.server_expect.string, optarg, MAX_INPUT_BUFFER - 1);
1247 result.config.server_expect.string[MAX_INPUT_BUFFER - 1] = 0;
1248 result.config.server_expect.is_present = true;
1249 break;
1250 case 'T': /* Content-type */
1251 result.config.curl_config.http_content_type = strdup(optarg);
1252 break;
1253 case 'l': /* linespan */
1254 cflags &= ~REG_NEWLINE;
1255 break;
1256 case 'R': /* regex */
1257 cflags |= REG_ICASE;
1258 // fall through
1259 case 'r': /* regex */
1260 strncpy(result.config.regexp, optarg, MAX_RE_SIZE - 1);
1261 result.config.regexp[MAX_RE_SIZE - 1] = 0;
1262 regex_t preg;
1263 int errcode = regcomp(&preg, result.config.regexp, cflags);
1264 if (errcode != 0) {
1265 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1266 printf(_("Could Not Compile Regular Expression: %s"), errbuf);
1267 result.errorcode = ERROR;
1268 return result;
1269 }
2444 1270
2445 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg); 1271 result.config.compiled_regex = preg;
2446 strcpy(*msg, tmp); 1272 break;
1273 case INVERT_REGEX:
1274 result.config.invert_regex = true;
1275 break;
1276 case STATE_REGEX:
1277 if (!strcasecmp(optarg, "critical")) {
1278 result.config.state_regex = STATE_CRITICAL;
1279 } else if (!strcasecmp(optarg, "warning")) {
1280 result.config.state_regex = STATE_WARNING;
1281 } else {
1282 usage2(_("Invalid state-regex option"), optarg);
1283 }
1284 break;
1285 case '4':
1286 result.config.curl_config.sin_family = AF_INET;
1287 break;
1288 case '6':
1289#if defined(LIBCURL_FEATURE_IPV6)
1290 result.config.curl_config.sin_family = AF_INET6;
1291#else
1292 usage4(_("IPv6 support not available"));
1293#endif
1294 break;
1295 case 'm': /* min_page_length */
1296 {
1297 mp_range_parsed foo = mp_parse_range_string(optarg);
2447 1298
2448 date_result = max_state_alt(STATE_CRITICAL, date_result); 1299 if (foo.error != MP_PARSING_SUCCESS) {
1300 die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg);
1301 }
2449 1302
2450 } else { 1303 result.config.page_length_limits = foo.range;
2451 time_t srv_data = curl_getdate (server_date, NULL); 1304 result.config.page_length_limits_is_set = true;
2452 time_t doc_data = curl_getdate (document_date, NULL); 1305 break;
2453 if (verbose >= 2) 1306 }
2454 printf ("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); 1307 case 'N': /* no-body */
2455 if (srv_data <= 0) { 1308 result.config.initial_config.no_body = true;
2456 char tmp[DEFAULT_BUFFER_SIZE]; 1309 break;
1310 case 'M': /* max-age */
1311 {
1312 size_t option_length = strlen(optarg);
1313 if (option_length && optarg[option_length - 1] == 'm') {
1314 result.config.maximum_age = atoi(optarg) * 60;
1315 } else if (option_length && optarg[option_length - 1] == 'h') {
1316 result.config.maximum_age = atoi(optarg) * 60 * 60;
1317 } else if (option_length && optarg[option_length - 1] == 'd') {
1318 result.config.maximum_age = atoi(optarg) * 60 * 60 * 24;
1319 } else if (option_length &&
1320 (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) {
1321 result.config.maximum_age = atoi(optarg);
1322 } else {
1323 fprintf(stderr, "unparsable max-age: %s\n", optarg);
1324 exit(STATE_WARNING);
1325 }
1326 if (verbose >= 2) {
1327 printf("* Maximal age of document set to %d seconds\n", result.config.maximum_age);
1328 }
1329 } break;
1330 case 'E': /* show extended perfdata */
1331 result.config.show_extended_perfdata = true;
1332 break;
1333 case 'B': /* print body content after status line */
1334 result.config.show_body = true;
1335 break;
1336 case HTTP_VERSION_OPTION:
1337 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1338 if (strcmp(optarg, "1.0") == 0) {
1339 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_0;
1340 } else if (strcmp(optarg, "1.1") == 0) {
1341 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_1;
1342 } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) {
1343#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0)
1344 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_2_0;
1345#else
1346 result.config.curl_http_version = CURL_HTTP_VERSION_NONE;
1347#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */
1348 } else if ((strcmp(optarg, "3") == 0)) {
1349#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0)
1350 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3;
1351#else
1352 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1353#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */
1354 } else {
1355 fprintf(stderr, "unknown http-version parameter: %s\n", optarg);
1356 exit(STATE_WARNING);
1357 }
1358 break;
1359 case AUTOMATIC_DECOMPRESSION:
1360 result.config.curl_config.automatic_decompression = true;
1361 break;
1362 case COOKIE_JAR:
1363 result.config.curl_config.cookie_jar_file = optarg;
1364 break;
1365 case HAPROXY_PROTOCOL:
1366 result.config.curl_config.haproxy_protocol = true;
1367 break;
1368 case NO_PROXY:
1369 strncpy(result.config.curl_config.no_proxy, optarg, DEFAULT_BUFFER_SIZE - 1);
1370 result.config.curl_config.no_proxy[DEFAULT_BUFFER_SIZE - 1] = 0;
1371 break;
1372 case '?':
1373 /* print short usage statement if args not parsable */
1374 usage5();
1375 break;
1376 case OUTPUT_FORMAT: {
1377 parsed_output_format parser = mp_parse_output_format(optarg);
1378 if (!parser.parsing_success) {
1379 // TODO List all available formats here, maybe add anothoer usage function
1380 printf("Invalid output format: %s\n", optarg);
1381 exit(STATE_UNKNOWN);
1382 }
2457 1383
2458 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); 1384 result.config.output_format_is_set = true;
2459 strcpy(*msg, tmp); 1385 result.config.output_format = parser.output_format;
1386 break;
1387 }
1388 }
1389 }
2460 1390
2461 date_result = max_state_alt(STATE_CRITICAL, date_result); 1391 if (enable_tls) {
2462 } else if (doc_data <= 0) { 1392 bool got_plus = false;
2463 char tmp[DEFAULT_BUFFER_SIZE]; 1393 result.config.initial_config.use_ssl = true;
1394 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1395 * Only set if it's non-zero. This helps when we include multiple
1396 * parameters, like -S and -C combinations */
1397 result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT;
1398 if (tls_option_optarg != NULL) {
1399 char *plus_ptr = strchr(tls_option_optarg, '+');
1400 if (plus_ptr) {
1401 got_plus = true;
1402 *plus_ptr = '\0';
1403 }
2464 1404
2465 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); 1405 if (tls_option_optarg[0] == '2') {
2466 strcpy(*msg, tmp); 1406 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2;
1407 } else if (tls_option_optarg[0] == '3') {
1408 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3;
1409 } else if (!strcmp(tls_option_optarg, "1") || !strcmp(tls_option_optarg, "1.0")) {
1410#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1411 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0;
1412#else
1413 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1414#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1415 } else if (!strcmp(tls_option_optarg, "1.1")) {
1416#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1417 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1;
1418#else
1419 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1420#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1421 } else if (!strcmp(tls_option_optarg, "1.2")) {
1422#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1423 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2;
1424#else
1425 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1426#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1427 } else if (!strcmp(tls_option_optarg, "1.3")) {
1428#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1429 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3;
1430#else
1431 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1432#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1433 } else {
1434 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 "
1435 "(with optional '+' suffix)"));
1436 }
1437 }
1438#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1439 if (got_plus) {
1440 switch (result.config.curl_config.ssl_version) {
1441 case CURL_SSLVERSION_TLSv1_3:
1442 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1443 break;
1444 case CURL_SSLVERSION_TLSv1_2:
1445 case CURL_SSLVERSION_TLSv1_1:
1446 case CURL_SSLVERSION_TLSv1_0:
1447 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1448 break;
1449 }
1450 } else {
1451 switch (result.config.curl_config.ssl_version) {
1452 case CURL_SSLVERSION_TLSv1_3:
1453 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1454 break;
1455 case CURL_SSLVERSION_TLSv1_2:
1456 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1457 break;
1458 case CURL_SSLVERSION_TLSv1_1:
1459 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1460 break;
1461 case CURL_SSLVERSION_TLSv1_0:
1462 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1463 break;
1464 }
1465 }
1466#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1467 if (verbose >= 2) {
1468 printf(_("* Set SSL/TLS version to %ld\n"), result.config.curl_config.ssl_version);
1469 }
1470 if (!specify_port) {
1471 result.config.initial_config.serverPort = HTTPS_PORT;
1472 }
1473 }
2467 1474
2468 date_result = max_state_alt(STATE_CRITICAL, date_result); 1475 int option_counter = optind;
2469 } else if (doc_data > srv_data + 30) {
2470 char tmp[DEFAULT_BUFFER_SIZE];
2471 1476
2472 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); 1477 if (result.config.initial_config.server_address == NULL && option_counter < argc) {
2473 strcpy(*msg, tmp); 1478 result.config.initial_config.server_address = strdup(argv[option_counter++]);
1479 }
2474 1480
2475 date_result = max_state_alt(STATE_CRITICAL, date_result); 1481 if (result.config.initial_config.host_name == NULL && option_counter < argc) {
2476 } else if (doc_data < srv_data - maximum_age) { 1482 result.config.initial_config.host_name = strdup(argv[option_counter++]);
2477 int n = (srv_data - doc_data); 1483 }
2478 if (n > (60 * 60 * 24 * 2)) {
2479 char tmp[DEFAULT_BUFFER_SIZE];
2480 1484
2481 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24)); 1485 if (result.config.initial_config.server_address == NULL) {
2482 strcpy(*msg, tmp); 1486 if (result.config.initial_config.host_name == NULL) {
1487 usage4(_("You must specify a server address or host name"));
1488 } else {
1489 result.config.initial_config.server_address =
1490 strdup(result.config.initial_config.host_name);
1491 }
1492 }
2483 1493
2484 date_result = max_state_alt(STATE_CRITICAL, date_result); 1494 if (result.config.initial_config.http_method == NULL) {
2485 } else { 1495 result.config.initial_config.http_method = strdup("GET");
2486 char tmp[DEFAULT_BUFFER_SIZE]; 1496 }
2487 1497
2488 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); 1498 if (result.config.curl_config.client_cert && !result.config.curl_config.client_privkey) {
2489 strcpy(*msg, tmp); 1499 usage4(_("If you use a client certificate you must also specify a private key file"));
1500 }
2490 1501
2491 date_result = max_state_alt(STATE_CRITICAL, date_result); 1502 if (result.config.initial_config.virtualPort == 0) {
1503 result.config.initial_config.virtualPort = result.config.initial_config.serverPort;
1504 } else {
1505 if ((result.config.initial_config.use_ssl &&
1506 result.config.initial_config.serverPort == HTTPS_PORT) ||
1507 (!result.config.initial_config.use_ssl &&
1508 result.config.initial_config.serverPort == HTTP_PORT)) {
1509 if (!specify_port) {
1510 result.config.initial_config.serverPort = result.config.initial_config.virtualPort;
2492 } 1511 }
2493 } 1512 }
2494 } 1513 }
2495 1514
2496 if (server_date) free (server_date); 1515 return result;
2497 if (document_date) free (document_date);
2498
2499 return date_result;
2500} 1516}
2501 1517
1518void print_help(void) {
1519 print_revision(progname, NP_VERSION);
2502 1520
2503int 1521 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
2504get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf) 1522 printf(COPYRIGHT, copyright, email);
2505{
2506 size_t content_length = 0;
2507 struct phr_header headers[255];
2508 size_t nof_headers = 255;
2509 size_t msglen;
2510 char *content_length_s = NULL;
2511 curlhelp_statusline status_line;
2512 1523
2513 int res = phr_parse_response (header_buf->buf, header_buf->buflen, 1524 printf("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
2514 &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, 1525 printf("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
2515 headers, &nof_headers, 0); 1526 printf("%s\n", _("strings and regular expressions, check connection times, and report on"));
1527 printf("%s\n", _("certificate expiration times."));
1528 printf("\n");
1529 printf("%s\n",
1530 _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1531 printf("%s\n", _("as possible."));
2516 1532
2517 if (res == -1) { 1533 printf("\n\n");
2518 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2519 }
2520 1534
2521 content_length_s = get_header_value (headers, nof_headers, "content-length"); 1535 print_usage();
2522 if (!content_length_s) {
2523 return header_buf->buflen + body_buf->buflen;
2524 }
2525 content_length_s += strspn (content_length_s, " \t");
2526 content_length = atoi (content_length_s);
2527 if (content_length != body_buf->buflen) {
2528 /* TODO: should we warn if the actual and the reported body length don't match? */
2529 }
2530 1536
2531 if (content_length_s) free (content_length_s); 1537 printf(_("NOTE: One or both of -H and -I must be specified"));
2532 1538
2533 return header_buf->buflen + body_buf->buflen; 1539 printf("\n");
2534} 1540
1541 printf(UT_HELP_VRSN);
1542 printf(UT_EXTRA_OPTS);
1543
1544 printf(" %s\n", "-H, --hostname=ADDRESS");
1545 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1546 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1547 printf(" %s\n", "-I, --IP-address=ADDRESS");
1548 printf(" %s\n",
1549 "IP address or name (use numeric address if possible to bypass DNS lookup).");
1550 printf(" %s\n", "This overwrites the network address of the target while leaving everything "
1551 "else (HTTP headers) as they are");
1552 printf(" %s\n", "-p, --port=INTEGER");
1553 printf(" %s", _("Port number (default: "));
1554 printf("%d)\n", HTTP_PORT);
2535 1555
2536/* TODO: is there a better way in libcurl to check for the SSL library? */ 1556 printf(UT_IPv46);
2537curlhelp_ssl_library
2538curlhelp_get_ssl_library ()
2539{
2540 curl_version_info_data* version_data;
2541 char *ssl_version;
2542 char *library;
2543 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
2544 1557
2545 version_data = curl_version_info (CURLVERSION_NOW); 1558#ifdef LIBCURL_FEATURE_SSL
2546 if (version_data == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN; 1559 printf(" %s\n", "-S, --ssl=VERSION[+]");
1560 printf(" %s\n",
1561 _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1562 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1563 printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are "
1564 "also accepted."));
1565 printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually "
1566 "disabled in libcurl"));
1567 printf(" %s\n", "--sni");
1568 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1569# if LIBCURL_VERSION_NUM >= 0x071801
1570 printf(" %s\n",
1571 _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
1572 printf(" %s\n", _(" SNI only really works since TLSv1.0"));
1573# else
1574 printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
1575# endif
1576 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1577 printf(" %s\n",
1578 _("Minimum number of days a certificate has to be valid. Port defaults to 443."));
1579 printf(" %s\n",
1580 _("A STATE_WARNING is returned if the certificate has a validity less than the"));
1581 printf(" %s\n",
1582 _("first agument's value. If there is a second argument and the certificate's"));
1583 printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned."));
1584 printf(" %s\n",
1585 _("(When this option is used the URL is not checked by default. You can use"));
1586 printf(" %s\n", _(" --continue-after-certificate to override this behavior)"));
1587 printf(" %s\n", "--continue-after-certificate");
1588 printf(" %s\n",
1589 _("Allows the HTTP check to continue after performing the certificate check."));
1590 printf(" %s\n", _("Does nothing unless -C is used."));
1591 printf(" %s\n", "-J, --client-cert=FILE");
1592 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1593 printf(" %s\n", _("to be used in establishing the SSL session"));
1594 printf(" %s\n", "-K, --private-key=FILE");
1595 printf(" %s\n", _("Name of file containing the private key (PEM format)"));
1596 printf(" %s\n", _("matching the client certificate"));
1597 printf(" %s\n", "--ca-cert=FILE");
1598 printf(" %s\n", _("CA certificate file to verify peer against"));
1599 printf(" %s\n", "-D, --verify-cert");
1600 printf(" %s\n", _("Verify the peer's SSL certificate and hostname"));
1601#endif
2547 1602
2548 ssl_version = strdup (version_data->ssl_version); 1603 printf(" %s\n", "-e, --expect=STRING");
2549 if (ssl_version == NULL ) return CURLHELP_SSL_LIBRARY_UNKNOWN; 1604 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1605 printf(" %s", _("the first (status) line of the server response (default: "));
1606 printf("%s)\n", HTTP_EXPECT);
1607 printf(" %s\n",
1608 _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1609 printf(" %s\n", "-d, --header-string=STRING");
1610 printf(" %s\n", _("String to expect in the response headers"));
1611 printf(" %s\n", "-s, --string=STRING");
1612 printf(" %s\n", _("String to expect in the content"));
1613 printf(" %s\n", "-u, --url=PATH");
1614 printf(" %s\n", _("URL to GET or POST (default: /)"));
1615 printf(" %s\n", _("This is the part after the address in a URL, so for "
1616 "\"https://example.com/index.html\" it would be '-u /index.html'"));
1617 printf(" %s\n", "-P, --post=STRING");
1618 printf(" %s\n", _("URL decoded http POST data"));
1619 printf(" %s\n",
1620 "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1621 printf(" %s\n", _("Set HTTP method."));
1622 printf(" %s\n", "-N, --no-body");
1623 printf(" %s\n", _("Don't wait for document body: stop reading after headers."));
1624 printf(" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1625 printf(" %s\n", "-M, --max-age=SECONDS");
1626 printf(" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1627 printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1628 printf(" %s\n", "-T, --content-type=STRING");
1629 printf(" %s\n", _("specify Content-Type header media type when POSTing"));
1630 printf(" %s\n", "-l, --linespan");
1631 printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1632 printf(" %s\n", "-r, --regex, --ereg=STRING");
1633 printf(" %s\n", _("Search page for regex STRING"));
1634 printf(" %s\n", "-R, --eregi=STRING");
1635 printf(" %s\n", _("Search page for case-insensitive regex STRING"));
1636 printf(" %s\n", "--invert-regex");
1637 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
1638 printf(" %s\n", _("can be changed with --state--regex)"));
1639 printf(" %s\n", "--state-regex=STATE");
1640 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of "
1641 "\"critical\",\"warning\""));
1642 printf(" %s\n", "-x, --proxy=PROXY_SERVER");
1643 printf(" %s\n", _("Specify the proxy in form of <scheme>://<host(name)>:<port>"));
1644 printf(" %s\n", _("Available schemes are http, https, socks4, socks4a, socks5, socks5h"));
1645 printf(" %s\n", _("If port is not specified, libcurl defaults to 1080"));
1646 printf(" %s\n", _("This value will be set as CURLOPT_PROXY"));
1647 printf(" %s\n", "--noproxy=COMMA_SEPARATED_LIST");
1648 printf(" %s\n",
1649 _("Specify hostnames, addresses and subnets where proxy should not be used"));
1650 printf(" %s\n", _("Example usage: \"example.com,::1,1.1.1.1,localhost,192.168.0.0/16\""));
1651 printf(" %s\n", _("Do not use brackets when specifying IPv6 addresses"));
1652 printf(" %s\n", _("Special case when an item is '*' : matches all hosts/addresses "
1653 "and effectively disables proxy."));
1654 printf(" %s\n", _("This value will be set as CURLOPT_NOPROXY"));
1655 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1656 printf(" %s\n", _("Username:password on sites with basic authentication"));
1657 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1658 printf(" %s\n", _("Username:password on proxy-servers with basic authentication"));
1659 printf(" %s\n", "-A, --useragent=STRING");
1660 printf(" %s\n", _("String to be sent in http header as \"User Agent\""));
1661 printf(" %s\n", "-k, --header=STRING");
1662 printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for "
1663 "additional headers"));
1664 printf(" %s\n", "-E, --extended-perfdata");
1665 printf(" %s\n", _("Print additional performance data"));
1666 printf(" %s\n", "-B, --show-body");
1667 printf(" %s\n", _("Print body content below status line"));
1668 // printf(" %s\n", "-L, --link");
1669 // printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1670 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
1671 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1672 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1673 printf(" %s\n", _("follow uses the old redirection algorithm of check_http."));
1674 printf(" %s\n", _("curl uses CURL_FOLLOWLOCATION built into libcurl."));
1675 printf(" %s\n", "--max-redirs=INTEGER");
1676 printf(" %s", _("Maximal number of redirects (default: "));
1677 printf("%d)\n", DEFAULT_MAX_REDIRS);
1678 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1679 printf(" %s\n",
1680 _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1681 printf("\n");
1682 printf(" %s\n", "--http-version=VERSION");
1683 printf(" %s\n", _("Connect via specific HTTP protocol."));
1684 printf(" %s\n",
1685 _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)"));
1686 printf(" %s\n", "--enable-automatic-decompression");
1687 printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING)."));
1688 printf(" %s\n", "--haproxy-protocol");
1689 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL)."));
1690 printf(" %s\n", "--cookie-jar=FILE");
1691 printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested."));
1692 printf(" %s\n",
1693 _("Specify an empty string as FILE to enable curl's cookie engine without saving"));
1694 printf(" %s\n",
1695 _("the cookies to disk. Only enabling the engine without saving to disk requires"));
1696 printf(" %s\n",
1697 _("handling multiple requests internally to curl, so use it with --onredirect=curl"));
1698 printf("\n");
1699
1700 printf(UT_WARN_CRIT);
1701
1702 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1703
1704 printf(UT_VERBOSE);
1705
1706 printf(UT_OUTPUT_FORMAT);
1707
1708 printf("\n");
1709 printf("%s\n", _("Notes:"));
1710 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1711 printf(" %s\n",
1712 _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1713 printf(" %s\n",
1714 _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1715 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1716 printf(" %s\n",
1717 _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1718 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
2550 1719
2551 library = strtok (ssl_version, "/"); 1720#ifdef LIBCURL_FEATURE_SSL
2552 if (library == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN; 1721 printf("\n");
1722 printf(" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1723 printf(" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1724 printf(" %s\n", _("certificate is still valid for the specified number of days."));
1725 printf("\n");
1726 printf(" %s\n", _("Please note that this plugin does not check if the presented server"));
1727 printf(" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1728 printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1729 printf("\n");
1730 printf(" %s\n", _("To also verify certificates, please set --verify-cert."));
1731 printf("\n");
1732 printf("%s\n", _("Examples:"));
1733 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
1734 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1735 printf(" %s\n",
1736 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1737 printf(" %s\n",
1738 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1739 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
1740 printf("\n");
1741 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14 -D");
1742 printf(" %s\n",
1743 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1744 printf(" %s\n",
1745 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1746 printf(" %s\n",
1747 _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1748 printf(" %s\n", _("the certificate is expired."));
1749 printf("\n");
1750 printf(" %s\n", _("The -D flag enforces a certificate validation beyond expiration time."));
1751 printf("\n");
1752 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14 -D");
1753 printf(" %s\n",
1754 _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1755 printf(" %s\n",
1756 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1757 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1758 printf(" %s\n",
1759 _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1760#endif
2553 1761
2554 if (strcmp (library, "OpenSSL") == 0) 1762 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
2555 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; 1763 printf(" %s\n", _("Proxies are specified or disabled for certain hosts/addresses using "
2556 else if (strcmp (library, "LibreSSL") == 0) 1764 "environment variables"
2557 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; 1765 " or -x/--proxy and --noproxy arguments:"));
2558 else if (strcmp (library, "GnuTLS") == 0) 1766 printf(" %s\n",
2559 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; 1767 _("Checked environment variables: all_proxy, http_proxy, https_proxy, no_proxy"));
2560 else if (strcmp (library, "NSS") == 0) 1768 printf(" %s\n",
2561 ssl_library = CURLHELP_SSL_LIBRARY_NSS; 1769 _("Environment variables can also be given in uppercase, but the lowercase ones will "
1770 "take predence if both are defined."));
1771 printf(" %s\n",
1772 _("The environment variables are overwritten by -x/--proxy and --noproxy arguments:"));
1773 printf(" %s\n", _("all_proxy/ALL_PROXY environment variables are read first, but protocol "
1774 "specific environment variables override them."));
1775 printf(" %s\n",
1776 _("If SSL is enabled and used, https_proxy/HTTPS_PROXY will be checked and overwrite "
1777 "http_proxy/HTTPS_PROXY."));
1778 printf(
1779 " %s\n",
1780 _("Curl accepts proxies using http, https, socks4, socks4a, socks5 and socks5h schemes."));
1781 printf(" %s\n",
1782 _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
1783 printf(" %s\n", _("http_proxy=http://used.proxy.com HTTP_PROXY=http://ignored.proxy.com "
1784 "./check_curl -H www.monitoring-plugins.org"));
1785 printf(" %s\n", _(" Lowercase http_proxy takes predence over uppercase HTTP_PROXY"));
1786 printf(" %s\n", _("./check_curl -H www.monitoring-plugins.org -x http://192.168.100.35:3128"));
1787 printf(" %s\n",
1788 _("http_proxy=http://unused.proxy1.com HTTP_PROXY=http://unused.proxy2.com ./check_curl "
1789 "-H www.monitoring-plugins.org --proxy http://used.proxy"));
1790 printf(
1791 " %s\n",
1792 _(" Proxy specified by --proxy overrides any proxy specified by environment variable."));
1793 printf(" %s\n", _(" Curl uses port 1080 by default as port is not specified"));
1794 printf(" %s\n", _("HTTPS_PROXY=http://192.168.100.35:3128 ./check_curl -H "
1795 "www.monitoring-plugins.org --ssl"));
1796 printf(" %s\n", _(" HTTPS_PROXY is read as --ssl is toggled"));
1797 printf(" %s\n",
1798 _("./check_curl -H www.monitoring-plugins.org --proxy socks5h://192.168.122.21"));
1799 printf(
1800 " %s\n",
1801 _("./check_curl -H www.monitoring-plugins.org -x http://unused.proxy.com --noproxy '*'"));
1802 printf(" %s\n", _(" Disabled proxy for all hosts by using '*' in no_proxy ."));
1803 printf(" %s\n", _("NO_PROXY=www.monitoring-plugins.org ./check_curl -H "
1804 "www.monitoring-plugins.org -x http://unused.proxy.com"));
1805 printf(" %s\n", _(" Exact matches with the hostname/address work."));
1806 printf(" %s\n",
1807 _("no_proxy=192.168.178.0/24 ./check_curl -I 192.168.178.10 -x http://proxy.acme.org"));
1808 printf(" %s\n", _("no_proxy=acme.org ./check_curl -H nonpublic.internalwebapp.acme.org -x "
1809 "http://proxy.acme.org"));
1810 printf(" %s\n", _(" Do not use proxy when accessing internal domains/addresses, but use a "
1811 "default proxy when accessing public web."));
1812 printf(" %s\n",
1813 _(" IMPORTANT: Check_curl can not always determine whether itself or the proxy will "
1814 "resolve a hostname before sending a request and getting an answer."
1815 "This can lead to DNS resolvation issues if hostname is only resolvable over proxy."));
1816 printf(" %s\n", _("Legacy proxy requests in check_http style still work:"));
1817 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ "
1818 "-H www.monitoring-plugins.org"));
2562 1819
2563 if (verbose >= 2) 1820#ifdef LIBCURL_FEATURE_SSL
2564 printf ("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library); 1821 printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1822 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
1823 printf(" %s\n",
1824 _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
1825 printf(" %s\n", _("legacy proxy requests in check_http style might still work, but are frowned "
1826 "upon, so DONT:"));
1827 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j "
1828 "CONNECT -H www.verisign.com "));
1829 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
1830 "-S(sl) -j CONNECT -H <webserver>"));
1831 printf(" %s\n",
1832 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1833 printf(" %s\n",
1834 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1835 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
2565 1836
2566 free (ssl_version); 1837#endif
2567 1838
2568 return ssl_library; 1839 printf(UT_SUPPORT);
2569} 1840}
2570 1841
2571const char* 1842void print_usage(void) {
2572curlhelp_get_ssl_library_string (curlhelp_ssl_library ssl_library) 1843 printf("%s\n", _("Usage:"));
2573{ 1844 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
2574 switch (ssl_library) { 1845 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate "
2575 case CURLHELP_SSL_LIBRARY_OPENSSL: 1846 "file>] [-D]\n");
2576 return "OpenSSL"; 1847 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-x <proxy>]\n");
2577 case CURLHELP_SSL_LIBRARY_LIBRESSL: 1848 printf(" [-a auth] [-b proxy_auth] [-f "
2578 return "LibreSSL"; 1849 "<ok|warning|critical|follow|sticky|stickyport|curl>]\n");
2579 case CURLHELP_SSL_LIBRARY_GNUTLS: 1850 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
2580 return "GnuTLS"; 1851 "regex>]\n");
2581 case CURLHELP_SSL_LIBRARY_NSS: 1852 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
2582 return "NSS"; 1853 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
2583 case CURLHELP_SSL_LIBRARY_UNKNOWN: 1854 printf(" [-T <content-type>] [-j method]\n");
2584 default: 1855 printf(" [--noproxy=<comma separated list of hosts, IP addresses, IP CIDR subnets>\n");
2585 return "unknown"; 1856 printf(" [--http-version=<version>] [--enable-automatic-decompression]\n");
2586 } 1857 printf(" [--cookie-jar=<cookie jar file>\n");
1858 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname);
1859 printf(" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
1860 printf("\n");
1861#ifdef LIBCURL_FEATURE_SSL
1862 printf("%s\n", _("In the first form, make an HTTP request."));
1863 printf("%s\n\n", _("In the second form, connect to the server and check the TLS certificate."));
1864#endif
2587} 1865}
2588 1866
1867void print_curl_version(void) { printf("%s\n", curl_version()); }
1868
2589#ifdef LIBCURL_FEATURE_SSL 1869#ifdef LIBCURL_FEATURE_SSL
2590#ifndef USE_OPENSSL 1870# ifndef MOPL_USE_OPENSSL
2591time_t 1871time_t parse_cert_date(const char *s) {
2592parse_cert_date (const char *s) 1872 if (!s) {
2593{ 1873 return -1;
2594 struct tm tm; 1874 }
2595 time_t date; 1875
2596 char *res; 1876 /* Jan 17 14:25:12 2020 GMT */
2597 1877 struct tm tm;
2598 if (!s) return -1; 1878 char *res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm);
2599 1879 /* Sep 11 12:00:00 2020 GMT */
2600 /* Jan 17 14:25:12 2020 GMT */ 1880 if (res == NULL) {
2601 res = strptime (s, "%Y-%m-%d %H:%M:%S GMT", &tm); 1881 strptime(s, "%Y %m %d %H:%M:%S GMT", &tm);
2602 /* Sep 11 12:00:00 2020 GMT */ 1882 }
2603 if (res == NULL) strptime (s, "%Y %m %d %H:%M:%S GMT", &tm); 1883 time_t date = mktime(&tm);
2604 date = mktime (&tm); 1884
2605 1885 return date;
2606 return date;
2607} 1886}
1887# endif /* MOPL_USE_OPENSSL */
1888#endif /* LIBCURL_FEATURE_SSL */
2608 1889
1890#ifdef LIBCURL_FEATURE_SSL
1891# ifndef MOPL_USE_OPENSSL
2609/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to 1892/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to
2610 * OpenSSL could be this function 1893 * OpenSSL could be this function
2611 */ 1894 */
2612int 1895int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn,
2613net_noopenssl_check_certificate (cert_ptr_union* cert_ptr, int days_till_exp_warn, int days_till_exp_crit) 1896 int days_till_exp_crit) {
2614{ 1897
2615 int i; 1898 if (verbose >= 2) {
2616 struct curl_slist* slist; 1899 printf("**** REQUEST CERTIFICATES ****\n");
2617 int cname_found = 0; 1900 }
2618 char* start_date_str = NULL; 1901
2619 char* end_date_str = NULL; 1902 char *start_date_str = NULL;
2620 time_t start_date; 1903 char *end_date_str = NULL;
2621 time_t end_date; 1904 bool have_first_cert = false;
2622 char *tz; 1905 bool cname_found = false;
2623 float time_left; 1906 for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) {
2624 int days_left; 1907 if (have_first_cert) {
2625 int time_remaining; 1908 break;
2626 char timestamp[50] = ""; 1909 }
2627 int status = STATE_UNKNOWN; 1910
2628 1911 struct curl_slist *slist;
2629 if (verbose >= 2) 1912 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) {
2630 printf ("**** REQUEST CERTIFICATES ****\n"); 1913 /* find first common name in subject,
2631 1914 * TODO: check alternative subjects for
2632 for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { 1915 * TODO: have a decent parser here and not a hack
2633 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { 1916 * multi-host certificate, check wildcards
2634 /* find first common name in subject, 1917 */
2635 * TODO: check alternative subjects for 1918 if (strncasecmp(slist->data, "Subject:", 8) == 0) {
2636 * TODO: have a decent parser here and not a hack 1919 int d = 3;
2637 * multi-host certificate, check wildcards 1920 char *p = strstr(slist->data, "CN=");
2638 */ 1921 if (p == NULL) {
2639 if (strncasecmp (slist->data, "Subject:", 8) == 0) { 1922 d = 5;
2640 int d = 3; 1923 p = strstr(slist->data, "CN = ");
2641 char* p = strstr (slist->data, "CN="); 1924 }
2642 if (p == NULL) { 1925 if (p != NULL) {
2643 d = 5; 1926 if (strncmp(host_name, p + d, strlen(host_name)) == 0) {
2644 p = strstr (slist->data, "CN = "); 1927 cname_found = true;
2645 } 1928 }
2646 if (p != NULL) { 1929 }
2647 if (strncmp (host_name, p+d, strlen (host_name)) == 0) { 1930 } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) {
2648 cname_found = 1; 1931 start_date_str = &slist->data[11];
2649 } 1932 } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) {
2650 } 1933 end_date_str = &slist->data[12];
2651 } else if (strncasecmp (slist->data, "Start Date:", 11) == 0) { 1934 } else if (strncasecmp(slist->data, "Cert:", 5) == 0) {
2652 start_date_str = &slist->data[11]; 1935 have_first_cert = true;
2653 } else if (strncasecmp (slist->data, "Expire Date:", 12) == 0) { 1936 break;
2654 end_date_str = &slist->data[12]; 1937 }
2655 } else if (strncasecmp (slist->data, "Cert:", 5) == 0) { 1938 if (verbose >= 2) {
2656 goto HAVE_FIRST_CERT; 1939 printf("%d ** %s\n", i, slist->data);
2657 } 1940 }
2658 if (verbose >= 2) 1941 }
2659 printf ("%d ** %s\n", i, slist->data); 1942 }
2660 } 1943
2661 } 1944 if (verbose >= 2) {
2662HAVE_FIRST_CERT: 1945 printf("**** REQUEST CERTIFICATES ****\n");
2663 1946 }
2664 if (verbose >= 2) 1947
2665 printf ("**** REQUEST CERTIFICATES ****\n"); 1948 if (!cname_found) {
2666 1949 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
2667 if (!cname_found) {
2668 printf("%s\n",_("CRITICAL - Cannot retrieve certificate subject."));
2669 return STATE_CRITICAL; 1950 return STATE_CRITICAL;
2670 } 1951 }
2671 1952
2672 start_date = parse_cert_date (start_date_str); 1953 time_t start_date = parse_cert_date(start_date_str);
2673 if (start_date <= 0) { 1954 if (start_date <= 0) {
2674 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), 1955 snprintf(msg, DEFAULT_BUFFER_SIZE,
2675 start_date_str); 1956 _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str);
2676 puts (msg); 1957 puts(msg);
2677 return STATE_WARNING; 1958 return STATE_WARNING;
2678 } 1959 }
2679 1960
2680 end_date = parse_cert_date (end_date_str); 1961 time_t end_date = parse_cert_date(end_date_str);
2681 if (end_date <= 0) { 1962 if (end_date <= 0) {
2682 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), 1963 snprintf(msg, DEFAULT_BUFFER_SIZE,
2683 start_date_str); 1964 _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str);
2684 puts (msg); 1965 puts(msg);
2685 return STATE_WARNING; 1966 return STATE_WARNING;
2686 } 1967 }
2687 1968
2688 time_left = difftime (end_date, time(NULL)); 1969 float time_left = difftime(end_date, time(NULL));
2689 days_left = time_left / 86400; 1970 int days_left = time_left / 86400;
2690 tz = getenv("TZ"); 1971 char *tz = getenv("TZ");
2691 setenv("TZ", "GMT", 1); 1972 setenv("TZ", "GMT", 1);
2692 tzset(); 1973 tzset();
1974
1975 char timestamp[50] = "";
2693 strftime(timestamp, 50, "%c %z", localtime(&end_date)); 1976 strftime(timestamp, 50, "%c %z", localtime(&end_date));
2694 if (tz) 1977 if (tz) {
2695 setenv("TZ", tz, 1); 1978 setenv("TZ", tz, 1);
2696 else 1979 } else {
2697 unsetenv("TZ"); 1980 unsetenv("TZ");
1981 }
2698 tzset(); 1982 tzset();
2699 1983
1984 mp_state_enum status = STATE_UNKNOWN;
1985 int time_remaining;
2700 if (days_left > 0 && days_left <= days_till_exp_warn) { 1986 if (days_left > 0 && days_left <= days_till_exp_warn) {
2701 printf (_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, days_left, timestamp); 1987 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
2702 if (days_left > days_till_exp_crit) 1988 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left,
1989 timestamp);
1990 if (days_left > days_till_exp_crit) {
2703 status = STATE_WARNING; 1991 status = STATE_WARNING;
2704 else 1992 } else {
2705 status = STATE_CRITICAL; 1993 status = STATE_CRITICAL;
1994 }
2706 } else if (days_left == 0 && time_left > 0) { 1995 } else if (days_left == 0 && time_left > 0) {
2707 if (time_left >= 3600) 1996 if (time_left >= 3600) {
2708 time_remaining = (int) time_left / 3600; 1997 time_remaining = (int)time_left / 3600;
2709 else 1998 } else {
2710 time_remaining = (int) time_left / 60; 1999 time_remaining = (int)time_left / 60;
2000 }
2711 2001
2712 printf (_("%s - Certificate '%s' expires in %u %s (%s)\n"), 2002 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
2713 (days_left>days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining, 2003 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining,
2714 time_left >= 3600 ? "hours" : "minutes", timestamp); 2004 time_left >= 3600 ? "hours" : "minutes", timestamp);
2715 2005
2716 if ( days_left > days_till_exp_crit) 2006 if (days_left > days_till_exp_crit) {
2717 status = STATE_WARNING; 2007 status = STATE_WARNING;
2718 else 2008 } else {
2719 status = STATE_CRITICAL; 2009 status = STATE_CRITICAL;
2010 }
2720 } else if (time_left < 0) { 2011 } else if (time_left < 0) {
2721 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); 2012 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp);
2722 status=STATE_CRITICAL; 2013 status = STATE_CRITICAL;
2723 } else if (days_left == 0) { 2014 } else if (days_left == 0) {
2724 printf (_("%s - Certificate '%s' just expired (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, timestamp); 2015 printf(_("%s - Certificate '%s' just expired (%s).\n"),
2725 if (days_left > days_till_exp_crit) 2016 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp);
2017 if (days_left > days_till_exp_crit) {
2726 status = STATE_WARNING; 2018 status = STATE_WARNING;
2727 else 2019 } else {
2728 status = STATE_CRITICAL; 2020 status = STATE_CRITICAL;
2021 }
2729 } else { 2022 } else {
2730 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp); 2023 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp);
2731 status = STATE_OK; 2024 status = STATE_OK;
2732 } 2025 }
2733 return status; 2026 return status;
2734} 2027}
2735#endif /* USE_OPENSSL */ 2028# endif /* MOPL_USE_OPENSSL */
2736#endif /* LIBCURL_FEATURE_SSL */ 2029#endif /* LIBCURL_FEATURE_SSL */
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
new file mode 100644
index 00000000..80d6f4f6
--- /dev/null
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -0,0 +1,1814 @@
1#include "./check_curl_helpers.h"
2#include <stdbool.h>
3#include <arpa/inet.h>
4#include <netinet/in.h>
5#include <netdb.h>
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/socket.h>
11#include "../utils.h"
12#include "check_curl.d/config.h"
13#include "output.h"
14#include "perfdata.h"
15#include "states.h"
16
17extern int verbose;
18char errbuf[MAX_INPUT_BUFFER];
19bool is_openssl_callback = false;
20bool add_sslctx_verify_fun = false;
21
22check_curl_configure_curl_wrapper
23check_curl_configure_curl(const check_curl_static_curl_config config,
24 check_curl_working_state working_state, bool check_cert,
25 bool on_redirect_dependent, int follow_method, long max_depth) {
26 check_curl_configure_curl_wrapper result = {
27 .errorcode = OK,
28 .curl_state =
29 {
30 .curl_global_initialized = false,
31 .curl_easy_initialized = false,
32 .curl = NULL,
33
34 .body_buf_initialized = false,
35 .body_buf = NULL,
36 .header_buf_initialized = false,
37 .header_buf = NULL,
38 .status_line_initialized = false,
39 .status_line = NULL,
40 .put_buf_initialized = false,
41 .put_buf = NULL,
42
43 .header_list = NULL,
44 .host = NULL,
45 },
46 };
47
48 if ((result.curl_state.status_line = calloc(1, sizeof(curlhelp_statusline))) == NULL) {
49 die(STATE_UNKNOWN, "HTTP UNKNOWN - allocation of statusline failed\n");
50 }
51
52 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
53 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
54 }
55 result.curl_state.curl_global_initialized = true;
56
57 if ((result.curl_state.curl = curl_easy_init()) == NULL) {
58 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
59 }
60 result.curl_state.curl_easy_initialized = true;
61
62 if (verbose >= 1) {
63 handle_curl_option_return_code(
64 curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1L), "CURLOPT_VERBOSE");
65 }
66
67 /* print everything on stdout like check_http would do */
68 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout),
69 "CURLOPT_STDERR");
70
71 if (config.automatic_decompression) {
72#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
73 handle_curl_option_return_code(
74 curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""),
75 "CURLOPT_ACCEPT_ENCODING");
76#else
77 handle_curl_option_return_code(
78 curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
79#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
80 }
81
82 /* initialize buffer for body of the answer */
83 if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) {
84 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
85 }
86 result.curl_state.body_buf_initialized = true;
87
88 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION,
89 curlhelp_buffer_write_callback),
90 "CURLOPT_WRITEFUNCTION");
91 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA,
92 (void *)result.curl_state.body_buf),
93 "CURLOPT_WRITEDATA");
94
95 /* initialize buffer for header of the answer */
96 if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) {
97 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n");
98 }
99 result.curl_state.header_buf_initialized = true;
100
101 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION,
102 curlhelp_buffer_write_callback),
103 "CURLOPT_HEADERFUNCTION");
104 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER,
105 (void *)result.curl_state.header_buf),
106 "CURLOPT_WRITEHEADER");
107
108 /* set the error buffer */
109 handle_curl_option_return_code(
110 curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf),
111 "CURLOPT_ERRORBUFFER");
112
113 /* set timeouts */
114 handle_curl_option_return_code(
115 curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout),
116 "CURLOPT_CONNECTTIMEOUT");
117
118 handle_curl_option_return_code(
119 curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout),
120 "CURLOPT_TIMEOUT");
121
122 /* set proxy */
123 /* http(s) proxy can either be given from the command line, or taken from environment variables
124 */
125 /* socks4(a) / socks5(h) proxy should be given using the command line */
126
127 /* first source to check is the environment variables */
128 /* lower case proxy environment variables are almost always accepted, while some programs also
129 checking uppercase ones. discover both, but take the lowercase one if both are present */
130
131 /* extra information: libcurl does not discover the uppercase version HTTP_PROXY due to security
132 * reasons */
133 /* https://github.com/curl/curl/blob/d445f2d930ae701039518d695481ee53b8490521/lib/url.c#L1987 */
134
135 /* first environment variable to read is all_proxy. it can be overridden by protocol specific
136 * environment variables */
137 char *all_proxy_env = getenv("all_proxy");
138 char *all_proxy_uppercase_env = getenv("ALL_PROXY");
139 if (all_proxy_env != NULL && strlen(all_proxy_env)) {
140 working_state.curlopt_proxy = strdup(all_proxy_env);
141 if (all_proxy_uppercase_env != NULL && verbose >= 1) {
142 printf("* cURL ignoring environment variable 'ALL_PROXY' as 'all_proxy' is set\n");
143 }
144 } else if (all_proxy_uppercase_env != NULL && strlen(all_proxy_uppercase_env) > 0) {
145 working_state.curlopt_proxy = strdup(all_proxy_uppercase_env);
146 }
147
148 /* second environment variable to read is http_proxy. only set curlopt_proxy if ssl is not
149 * toggled */
150 char *http_proxy_env = getenv("http_proxy");
151 char *http_proxy_uppercase_env = getenv("HTTP_PROXY");
152 if (!working_state.use_ssl) {
153 if (http_proxy_env != NULL && strlen(http_proxy_env) > 0) {
154 working_state.curlopt_proxy = strdup(http_proxy_env);
155 if (http_proxy_uppercase_env != NULL && verbose >= 1) {
156 printf(
157 "* cURL ignoring environment variable 'HTTP_PROXY' as 'http_proxy' is set\n");
158 }
159 } else if (http_proxy_uppercase_env != NULL && strlen(http_proxy_uppercase_env) > 0) {
160 working_state.curlopt_proxy = strdup(http_proxy_uppercase_env);
161 }
162 }
163#ifdef LIBCURL_FEATURE_SSL
164 /* optionally read https_proxy environment variable and set curlopt_proxy if ssl is toggled */
165 char *https_proxy_env = getenv("https_proxy");
166 char *https_proxy_uppercase_env = getenv("HTTPS_PROXY");
167 if (working_state.use_ssl) {
168 if (https_proxy_env != NULL && strlen(https_proxy_env) > 0) {
169 working_state.curlopt_proxy = strdup(https_proxy_env);
170 if (https_proxy_uppercase_env != NULL && verbose >= 1) {
171 printf(
172 "* cURL ignoring environment variable 'HTTPS_PROXY' as 'https_proxy' is set\n");
173 }
174 } else if (https_proxy_uppercase_env != NULL) {
175 working_state.curlopt_proxy = strdup(https_proxy_uppercase_env);
176 }
177 }
178#endif /* LIBCURL_FEATURE_SSL */
179
180 /* second source to check for proxies is command line argument, overwriting the environment
181 * variables */
182 if (strlen(config.proxy) > 0) {
183 working_state.curlopt_proxy = strdup(config.proxy);
184 }
185
186 if (working_state.curlopt_proxy != NULL && strlen(working_state.curlopt_proxy)) {
187 handle_curl_option_return_code(
188 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.curlopt_proxy),
189 "CURLOPT_PROXY");
190 if (verbose >= 1) {
191 printf("* curl CURLOPT_PROXY: %s\n", working_state.curlopt_proxy);
192 }
193 }
194
195 /* set no_proxy */
196 /* first source to check is environment variables */
197 char *no_proxy_env = getenv("no_proxy");
198 char *no_proxy_uppercase_env = getenv("NO_PROXY");
199 if (no_proxy_env != NULL && strlen(no_proxy_env)) {
200 working_state.curlopt_noproxy = strdup(no_proxy_env);
201 if (no_proxy_uppercase_env != NULL && verbose >= 1) {
202 printf("* cURL ignoring environment variable 'NO_PROXY' as 'no_proxy' is set\n");
203 }
204 } else if (no_proxy_uppercase_env != NULL && strlen(no_proxy_uppercase_env) > 0) {
205 working_state.curlopt_noproxy = strdup(no_proxy_uppercase_env);
206 }
207
208 /* second source to check for no_proxy is command line argument, overwriting the environment
209 * variables */
210 if (strlen(config.no_proxy) > 0) {
211 working_state.curlopt_noproxy = strdup(config.no_proxy);
212 }
213
214 if (working_state.curlopt_noproxy != NULL && strlen(working_state.curlopt_noproxy)) {
215 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOPROXY,
216 working_state.curlopt_noproxy),
217 "CURLOPT_NOPROXY");
218 if (verbose >= 1) {
219 printf("* curl CURLOPT_NOPROXY: %s\n", working_state.curlopt_noproxy);
220 }
221 }
222
223 bool have_local_resolution = hostname_gets_resolved_locally(working_state);
224 if (verbose >= 1) {
225 printf("* have local name resolution: %s\n", (have_local_resolution ? "true": "false"));
226 }
227
228 /* enable haproxy protocol */
229 if (config.haproxy_protocol) {
230 handle_curl_option_return_code(
231 curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L),
232 "CURLOPT_HAPROXYPROTOCOL");
233 }
234
235 /* fill dns resolve cache to make curl connect to the given server_address instead of the */
236 /* host_name, only required for ssl, because we use the host_name later on to make SNI happy */
237 char dnscache[DEFAULT_BUFFER_SIZE];
238 char addrstr[DEFAULT_BUFFER_SIZE / 2];
239 if (working_state.use_ssl && working_state.host_name != NULL && !have_local_resolution) {
240 char *tmp_mod_address;
241
242 /* lookup_host() requires an IPv6 address without the brackets. */
243 if ((strnlen(working_state.server_address, MAX_IPV4_HOSTLENGTH) > 2) &&
244 (working_state.server_address[0] == '[')) {
245 // Duplicate and strip the leading '['
246 tmp_mod_address =
247 strndup(working_state.server_address + 1, strlen(working_state.server_address) - 2);
248 } else {
249 tmp_mod_address = working_state.server_address;
250 }
251
252 int res;
253 if ((res = lookup_host(tmp_mod_address, addrstr, DEFAULT_BUFFER_SIZE / 2,
254 config.sin_family)) != 0) {
255 die(STATE_CRITICAL,
256 _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"),
257 working_state.server_address, res, gai_strerror(res));
258 }
259
260 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name,
261 working_state.serverPort, addrstr);
262 result.curl_state.host = curl_slist_append(NULL, dnscache);
263 curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host);
264
265 if (verbose >= 1) {
266 printf("* curl CURLOPT_RESOLVE: %s\n", dnscache);
267 }
268 }
269
270 // If server_address is an IPv6 address it must be surround by square brackets
271 struct in6_addr tmp_in_addr;
272 if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) {
273 char *new_server_address = calloc(strlen(working_state.server_address) + 3, sizeof(char));
274 if (new_server_address == NULL) {
275 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
276 }
277 snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]",
278 working_state.server_address);
279 working_state.server_address = new_server_address;
280 }
281
282 /* compose URL: use the address we want to connect to, set Host: header later */
283 char *url = fmt_url(working_state);
284
285 if (verbose >= 1) {
286 printf("* curl CURLOPT_URL: %s\n", url);
287 }
288 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url),
289 "CURLOPT_URL");
290
291 free(url);
292
293 /* extract proxy information for legacy proxy https requests */
294 if (!strcmp(working_state.http_method, "CONNECT") ||
295 strstr(working_state.server_url, "http") == working_state.server_url) {
296 handle_curl_option_return_code(
297 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address),
298 "CURLOPT_PROXY");
299 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT,
300 (long)working_state.serverPort),
301 "CURLOPT_PROXYPORT");
302 if (verbose >= 2) {
303 printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address,
304 working_state.serverPort);
305 }
306 working_state.http_method = "GET";
307 handle_curl_option_return_code(
308 curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url),
309 "CURLOPT_URL");
310 }
311
312 /* disable body for HEAD request */
313 if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) {
314 working_state.no_body = true;
315 }
316
317 /* set HTTP protocol version */
318 handle_curl_option_return_code(
319 curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version),
320 "CURLOPT_HTTP_VERSION");
321
322 /* set HTTP method */
323 if (working_state.http_method) {
324 if (!strcmp(working_state.http_method, "POST")) {
325 handle_curl_option_return_code(
326 curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1L), "CURLOPT_POST");
327 } else if (!strcmp(working_state.http_method, "PUT")) {
328 handle_curl_option_return_code(
329 curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1L), "CURLOPT_UPLOAD");
330 } else {
331 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
332 CURLOPT_CUSTOMREQUEST,
333 working_state.http_method),
334 "CURLOPT_CUSTOMREQUEST");
335 }
336 }
337
338 char *force_host_header = NULL;
339 /* check if Host header is explicitly set in options */
340 if (config.http_opt_headers_count) {
341 for (size_t i = 0; i < config.http_opt_headers_count; i++) {
342 if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) {
343 force_host_header = config.http_opt_headers[i];
344 }
345 }
346 }
347
348 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in
349 * anyway */
350 char http_header[DEFAULT_BUFFER_SIZE];
351 if (working_state.host_name != NULL && force_host_header == NULL) {
352 if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) ||
353 (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) {
354 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name,
355 working_state.virtualPort);
356 } else {
357 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name);
358 }
359 result.curl_state.header_list =
360 curl_slist_append(result.curl_state.header_list, http_header);
361 }
362
363 /* always close connection, be nice to servers */
364 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
365 result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header);
366
367 /* attach additional headers supplied by the user */
368 /* optionally send any other header tag */
369 if (config.http_opt_headers_count) {
370 for (size_t i = 0; i < config.http_opt_headers_count; i++) {
371 result.curl_state.header_list =
372 curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]);
373 }
374 }
375
376 /* set HTTP headers */
377 handle_curl_option_return_code(
378 curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list),
379 "CURLOPT_HTTPHEADER");
380
381#ifdef LIBCURL_FEATURE_SSL
382 /* set SSL version, warn about insecure or unsupported versions */
383 if (working_state.use_ssl) {
384 handle_curl_option_return_code(
385 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version),
386 "CURLOPT_SSLVERSION");
387 }
388
389 /* client certificate and key to present to server (SSL) */
390 if (config.client_cert) {
391 handle_curl_option_return_code(
392 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert),
393 "CURLOPT_SSLCERT");
394 }
395
396 if (config.client_privkey) {
397 handle_curl_option_return_code(
398 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey),
399 "CURLOPT_SSLKEY");
400 }
401
402 if (config.ca_cert) {
403 handle_curl_option_return_code(
404 curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert),
405 "CURLOPT_CAINFO");
406 }
407
408 if (config.ca_cert || config.verify_peer_and_host) {
409 /* per default if we have a CA verify both the peer and the
410 * hostname in the certificate, can be switched off later */
411 handle_curl_option_return_code(
412 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1L),
413 "CURLOPT_SSL_VERIFYPEER");
414 handle_curl_option_return_code(
415 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2L),
416 "CURLOPT_SSL_VERIFYHOST");
417 } else {
418 /* backward-compatible behaviour, be tolerant in checks
419 * TODO: depending on more options have aspects we want
420 * to be less tolerant about ssl verfications
421 */
422 handle_curl_option_return_code(
423 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0L),
424 "CURLOPT_SSL_VERIFYPEER");
425 handle_curl_option_return_code(
426 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0L),
427 "CURLOPT_SSL_VERIFYHOST");
428 }
429
430 /* detect SSL library used by libcurl */
431 curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library();
432
433 /* try hard to get a stack of certificates to verify against */
434 if (check_cert) {
435# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
436 /* inform curl to report back certificates */
437 switch (ssl_library) {
438 case CURLHELP_SSL_LIBRARY_OPENSSL:
439 case CURLHELP_SSL_LIBRARY_LIBRESSL:
440 /* set callback to extract certificate with OpenSSL context function (works with
441 * OpenSSL-style libraries only!) */
442# ifdef MOPL_USE_OPENSSL
443 /* libcurl and monitoring plugins built with OpenSSL, good */
444 add_sslctx_verify_fun = true;
445 is_openssl_callback = true;
446# endif /* MOPL_USE_OPENSSL */
447 /* libcurl is built with OpenSSL, monitoring plugins, so falling
448 * back to manually extracting certificate information */
449 handle_curl_option_return_code(
450 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
451 break;
452
453 case CURLHELP_SSL_LIBRARY_NSS:
454# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
455 /* NSS: support for CERTINFO is implemented since 7.34.0 */
456 handle_curl_option_return_code(
457 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
458# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
459 die(STATE_CRITICAL,
460 "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library "
461 "'%s' is too old)\n",
462 curlhelp_get_ssl_library_string(ssl_library));
463# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
464 break;
465
466 case CURLHELP_SSL_LIBRARY_GNUTLS:
467# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
468 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
469 handle_curl_option_return_code(
470 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
471# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
472 die(STATE_CRITICAL,
473 "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library "
474 "'%s' is too old)\n",
475 curlhelp_get_ssl_library_string(ssl_library));
476# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
477 break;
478
479 case CURLHELP_SSL_LIBRARY_UNKNOWN:
480 default:
481 die(STATE_CRITICAL,
482 "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must "
483 "implement first)\n",
484 curlhelp_get_ssl_library_string(ssl_library));
485 break;
486 }
487# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
488 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
489 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL ||
490 ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) {
491 add_sslctx_verify_fun = true;
492 } else {
493 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no "
494 "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl "
495 "too old and has no CURLOPT_CERTINFO)\n");
496 }
497# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
498 }
499
500# if LIBCURL_VERSION_NUM >= \
501 MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */
502 // ssl ctx function is not available with all ssl backends
503 if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) !=
504 CURLE_UNKNOWN_OPTION) {
505 handle_curl_option_return_code(
506 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun),
507 "CURLOPT_SSL_CTX_FUNCTION");
508 }
509# endif
510#endif /* LIBCURL_FEATURE_SSL */
511
512 /* set default or user-given user agent identification */
513 handle_curl_option_return_code(
514 curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent),
515 "CURLOPT_USERAGENT");
516
517 /* proxy-authentication */
518 if (strcmp(config.proxy_auth, "")) {
519 handle_curl_option_return_code(
520 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth),
521 "CURLOPT_PROXYUSERPWD");
522 }
523
524 /* authentication */
525 if (strcmp(config.user_auth, "")) {
526 handle_curl_option_return_code(
527 curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth),
528 "CURLOPT_USERPWD");
529 }
530 /* TODO: parameter auth method, bitfield of following methods:
531 * CURLAUTH_BASIC (default)
532 * CURLAUTH_DIGEST
533 * CURLAUTH_DIGEST_IE
534 * CURLAUTH_NEGOTIATE
535 * CURLAUTH_NTLM
536 * CURLAUTH_NTLM_WB
537 *
538 * convenience tokens for typical sets of methods:
539 * CURLAUTH_ANYSAFE: most secure, without BASIC
540 * or CURLAUTH_ANY: most secure, even BASIC if necessary
541 *
542 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH,
543 * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
544 */
545
546 /* handle redirections */
547 if (on_redirect_dependent) {
548 if (follow_method == FOLLOW_LIBCURL) {
549 handle_curl_option_return_code(
550 curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1L),
551 "CURLOPT_FOLLOWLOCATION");
552
553 /* default -1 is infinite, not good, could lead to zombie plugins!
554 Setting it to one bigger than maximal limit to handle errors nicely below
555 */
556 handle_curl_option_return_code(
557 curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1),
558 "CURLOPT_MAXREDIRS");
559
560 /* for now allow only http and https (we are a http(s) check plugin in the end) */
561#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
562 handle_curl_option_return_code(
563 curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"),
564 "CURLOPT_REDIR_PROTOCOLS_STR");
565#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
566 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
567 CURLOPT_REDIR_PROTOCOLS,
568 CURLPROTO_HTTP | CURLPROTO_HTTPS),
569 "CURLOPT_REDIRECT_PROTOCOLS");
570#endif
571
572 /* TODO: handle the following aspects of redirection, make them
573 * command line options too later:
574 CURLOPT_POSTREDIR: method switch
575 CURLINFO_REDIRECT_URL: custom redirect option
576 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
577 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range
578 option here is nice like for expected page size?
579 */
580 } else {
581 /* old style redirection*/
582 }
583 }
584 /* no-body */
585 if (working_state.no_body) {
586 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1L),
587 "CURLOPT_NOBODY");
588 }
589
590 /* IPv4 or IPv6 forced DNS resolution */
591 if (config.sin_family == AF_UNSPEC) {
592 handle_curl_option_return_code(
593 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER),
594 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
595 } else if (config.sin_family == AF_INET) {
596 handle_curl_option_return_code(
597 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
598 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
599 }
600#if defined(LIBCURL_FEATURE_IPV6)
601 else if (config.sin_family == AF_INET6) {
602 handle_curl_option_return_code(
603 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
604 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
605 }
606#endif
607
608 /* either send http POST data (any data, not only POST)*/
609 if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) {
610 /* set content of payload for POST and PUT */
611 if (config.http_content_type) {
612 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s",
613 config.http_content_type);
614 result.curl_state.header_list =
615 curl_slist_append(result.curl_state.header_list, http_header);
616 }
617 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
618 * in case of no POST/PUT data */
619 if (!working_state.http_post_data) {
620 working_state.http_post_data = "";
621 }
622
623 if (!strcmp(working_state.http_method, "POST")) {
624 /* POST method, set payload with CURLOPT_POSTFIELDS */
625 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
626 CURLOPT_POSTFIELDS,
627 working_state.http_post_data),
628 "CURLOPT_POSTFIELDS");
629 } else if (!strcmp(working_state.http_method, "PUT")) {
630 handle_curl_option_return_code(
631 curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION,
632 (curl_read_callback)curlhelp_buffer_read_callback),
633 "CURLOPT_READFUNCTION");
634 if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data,
635 strlen(working_state.http_post_data)) < 0) {
636 die(STATE_UNKNOWN,
637 "HTTP CRITICAL - out of memory allocating read buffer for PUT\n");
638 }
639 result.curl_state.put_buf_initialized = true;
640 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
641 CURLOPT_READDATA,
642 (void *)result.curl_state.put_buf),
643 "CURLOPT_READDATA");
644 handle_curl_option_return_code(
645 curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE,
646 (curl_off_t)strlen(working_state.http_post_data)),
647 "CURLOPT_INFILESIZE");
648 }
649 }
650
651 /* cookie handling */
652 if (config.cookie_jar_file != NULL) {
653 /* enable reading cookies from a file, and if the filename is an empty string, only
654 * enable the curl cookie engine */
655 handle_curl_option_return_code(
656 curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file),
657 "CURLOPT_COOKIEFILE");
658 /* now enable saving cookies to a file, but only if the filename is not an empty string,
659 * since writing it would fail */
660 if (*config.cookie_jar_file) {
661 handle_curl_option_return_code(
662 curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file),
663 "CURLOPT_COOKIEJAR");
664 }
665 }
666
667 result.working_state = working_state;
668
669 return result;
670}
671
672void handle_curl_option_return_code(CURLcode res, const char *option) {
673 if (res != CURLE_OK) {
674 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s\n"),
675 option, res, curl_easy_strerror(res));
676 }
677}
678
679char *get_header_value(const struct phr_header *headers, const size_t nof_headers,
680 const char *header) {
681 for (size_t i = 0; i < nof_headers; i++) {
682 if (headers[i].name != NULL &&
683 strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) {
684 return strndup(headers[i].value, headers[i].value_len);
685 }
686 }
687 return NULL;
688}
689
690check_curl_working_state check_curl_working_state_init(void) {
691 check_curl_working_state result = {
692 .server_address = NULL,
693 .server_url = DEFAULT_SERVER_URL,
694 .host_name = NULL,
695 .http_method = NULL,
696 .http_post_data = NULL,
697 .virtualPort = 0,
698 .serverPort = HTTP_PORT,
699 .use_ssl = false,
700 .no_body = false,
701 .curlopt_proxy = NULL,
702 .curlopt_noproxy = NULL,
703 };
704 return result;
705}
706
707check_curl_config check_curl_config_init(void) {
708 check_curl_config tmp = {
709 .initial_config = check_curl_working_state_init(),
710
711 .curl_config =
712 {
713 .automatic_decompression = false,
714 .socket_timeout = DEFAULT_SOCKET_TIMEOUT,
715 .haproxy_protocol = false,
716 .sin_family = AF_UNSPEC,
717 .curl_http_version = CURL_HTTP_VERSION_NONE,
718 .http_opt_headers = NULL,
719 .http_opt_headers_count = 0,
720 .ssl_version = CURL_SSLVERSION_DEFAULT,
721 .client_cert = NULL,
722 .client_privkey = NULL,
723 .ca_cert = NULL,
724 .verify_peer_and_host = false,
725 .user_agent = {'\0'},
726 .proxy = "",
727 .no_proxy = "",
728 .proxy_auth = "",
729 .user_auth = "",
730 .http_content_type = NULL,
731 .cookie_jar_file = NULL,
732 },
733 .max_depth = DEFAULT_MAX_REDIRS,
734 .followmethod = FOLLOW_HTTP_CURL,
735 .followsticky = STICKY_NONE,
736
737 .maximum_age = -1,
738 .regexp = {},
739 .compiled_regex = {},
740 .state_regex = STATE_CRITICAL,
741 .invert_regex = false,
742 .check_cert = false,
743 .continue_after_check_cert = false,
744 .days_till_exp_warn = 0,
745 .days_till_exp_crit = 0,
746 .thlds = mp_thresholds_init(),
747 .page_length_limits = mp_range_init(),
748 .page_length_limits_is_set = false,
749 .server_expect =
750 {
751 .string = HTTP_EXPECT,
752 .is_present = false,
753 },
754 .string_expect = "",
755 .header_expect = "",
756 .on_redirect_result_state = STATE_OK,
757 .on_redirect_dependent = false,
758
759 .show_extended_perfdata = false,
760 .show_body = false,
761
762 .output_format_is_set = false,
763 };
764
765 snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)",
766 "check_curl", NP_VERSION, VERSION, curl_version());
767
768 return tmp;
769}
770
771/* TODO: is there a better way in libcurl to check for the SSL library? */
772curlhelp_ssl_library curlhelp_get_ssl_library(void) {
773 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
774
775 curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW);
776 if (version_data == NULL) {
777 return CURLHELP_SSL_LIBRARY_UNKNOWN;
778 }
779
780 char *ssl_version = strdup(version_data->ssl_version);
781 if (ssl_version == NULL) {
782 return CURLHELP_SSL_LIBRARY_UNKNOWN;
783 }
784
785 char *library = strtok(ssl_version, "/");
786 if (library == NULL) {
787 return CURLHELP_SSL_LIBRARY_UNKNOWN;
788 }
789
790 if (strcmp(library, "OpenSSL") == 0) {
791 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
792 } else if (strcmp(library, "LibreSSL") == 0) {
793 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
794 } else if (strcmp(library, "GnuTLS") == 0) {
795 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
796 } else if (strcmp(library, "NSS") == 0) {
797 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
798 }
799
800 if (verbose >= 2) {
801 printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library,
802 ssl_library);
803 }
804
805 free(ssl_version);
806
807 return ssl_library;
808}
809
810const char *curlhelp_get_ssl_library_string(const curlhelp_ssl_library ssl_library) {
811 switch (ssl_library) {
812 case CURLHELP_SSL_LIBRARY_OPENSSL:
813 return "OpenSSL";
814 case CURLHELP_SSL_LIBRARY_LIBRESSL:
815 return "LibreSSL";
816 case CURLHELP_SSL_LIBRARY_GNUTLS:
817 return "GnuTLS";
818 case CURLHELP_SSL_LIBRARY_NSS:
819 return "NSS";
820 case CURLHELP_SSL_LIBRARY_UNKNOWN:
821 default:
822 return "unknown";
823 }
824}
825
826size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
827 const curlhelp_write_curlbuf *body_buf) {
828 struct phr_header headers[255];
829 size_t nof_headers = 255;
830 size_t msglen;
831 curlhelp_statusline status_line;
832 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
833 &status_line.http_minor, &status_line.http_code, &status_line.msg,
834 &msglen, headers, &nof_headers, 0);
835
836 if (res == -1) {
837 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
838 }
839
840 char *content_length_s = get_header_value(headers, nof_headers, "content-length");
841 if (!content_length_s) {
842 return header_buf->buflen + body_buf->buflen;
843 }
844
845 content_length_s += strspn(content_length_s, " \t");
846 size_t content_length = atoi(content_length_s);
847 if (content_length != body_buf->buflen) {
848 /* TODO: should we warn if the actual and the reported body length don't match? */
849 }
850
851 if (content_length_s) {
852 free(content_length_s);
853 }
854
855 return header_buf->buflen + body_buf->buflen;
856}
857
858mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const int maximum_age) {
859 struct phr_header headers[255];
860 size_t nof_headers = 255;
861 curlhelp_statusline status_line;
862 size_t msglen;
863 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
864 &status_line.http_minor, &status_line.http_code, &status_line.msg,
865 &msglen, headers, &nof_headers, 0);
866
867 if (res == -1) {
868 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
869 }
870
871 char *server_date = get_header_value(headers, nof_headers, "date");
872 char *document_date = get_header_value(headers, nof_headers, "last-modified");
873
874 mp_subcheck sc_document_dates = mp_subcheck_init();
875 if (!server_date || !*server_date) {
876 xasprintf(&sc_document_dates.output, _("Server date unknown"));
877 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_UNKNOWN);
878 } else if (!document_date || !*document_date) {
879 xasprintf(&sc_document_dates.output, _("Document modification date unknown, "));
880 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
881 } else {
882 time_t srv_data = curl_getdate(server_date, NULL);
883 time_t doc_data = curl_getdate(document_date, NULL);
884
885 if (verbose >= 2) {
886 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data,
887 document_date, (int)doc_data);
888 }
889
890 if (srv_data <= 0) {
891 xasprintf(&sc_document_dates.output, _("Server date \"%100s\" unparsable"),
892 server_date);
893 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
894 } else if (doc_data <= 0) {
895
896 xasprintf(&sc_document_dates.output, _("Document date \"%100s\" unparsable"),
897 document_date);
898 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
899 } else if (doc_data > srv_data + 30) {
900
901 xasprintf(&sc_document_dates.output, _("Document is %d seconds in the future"),
902 (int)doc_data - (int)srv_data);
903
904 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
905 } else if (doc_data < srv_data - maximum_age) {
906 time_t last_modified = (srv_data - doc_data);
907 if (last_modified > (60 * 60 * 24 * 2)) { // two days hardcoded?
908 xasprintf(&sc_document_dates.output, _("Last modified %.1f days ago"),
909 ((float)last_modified) / (60 * 60 * 24));
910 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
911 } else {
912 xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"),
913 (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60,
914 (int)last_modified % 60);
915 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
916 }
917 } else {
918 // TODO is this the OK case?
919 time_t last_modified = (srv_data - doc_data);
920 xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"),
921 (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60,
922 (int)last_modified % 60);
923 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK);
924 }
925 }
926
927 if (server_date) {
928 free(server_date);
929 }
930 if (document_date) {
931 free(document_date);
932 }
933
934 return sc_document_dates;
935}
936
937void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); }
938
939int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) {
940 /* find last start of a new header */
941 const char *start = strrstr2(buf, "\r\nHTTP/");
942 if (start != NULL) {
943 start += 2;
944 buf = start;
945 }
946
947 // Accept either LF or CRLF as end of line for the status line
948 // CRLF is the standard (RFC9112), but it is recommended to accept both
949 size_t length_of_first_line = strcspn(buf, "\r\n");
950 const char *first_line_end = &buf[length_of_first_line];
951 if (first_line_end == NULL) {
952 return -1;
953 }
954
955 size_t first_line_len = (size_t)(first_line_end - buf);
956 status_line->first_line = (char *)calloc(first_line_len + 1, sizeof(char));
957 if (status_line->first_line == NULL) {
958 return -1;
959 }
960
961 memcpy(status_line->first_line, buf, first_line_len);
962 status_line->first_line[first_line_len] = '\0';
963 char *first_line_buf = strdup(status_line->first_line);
964
965 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
966 char *temp_string = strtok(first_line_buf, "/");
967 if (temp_string == NULL) {
968 if (verbose > 1) {
969 printf("%s: no / found\n", __func__);
970 }
971 free(first_line_buf);
972 return -1;
973 }
974
975 if (strcmp(temp_string, "HTTP") != 0) {
976 if (verbose > 1) {
977 printf("%s: string 'HTTP' not found\n", __func__);
978 }
979 free(first_line_buf);
980 return -1;
981 }
982
983 // try to find a space in the remaining string?
984 // the space after HTTP/1.1 probably
985 temp_string = strtok(NULL, " ");
986 if (temp_string == NULL) {
987 if (verbose > 1) {
988 printf("%s: no space after protocol definition\n", __func__);
989 }
990 free(first_line_buf);
991 return -1;
992 }
993
994 char *temp_string_2;
995 if (strchr(temp_string, '.') != NULL) {
996 /* HTTP 1.x case */
997 strtok(temp_string, ".");
998 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
999 if (*temp_string_2 != '\0') {
1000 free(first_line_buf);
1001 return -1;
1002 }
1003 strtok(NULL, " ");
1004 status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10);
1005 if (*temp_string_2 != '\0') {
1006 free(first_line_buf);
1007 return -1;
1008 }
1009 temp_string += 4; /* 1.x SP */
1010 } else {
1011 /* HTTP 2 case */
1012 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
1013 status_line->http_minor = 0;
1014 temp_string += 2; /* 2 SP */
1015 }
1016
1017 /* status code: "404" or "404.1", then SP */
1018 temp_string = strtok(temp_string, " ");
1019 if (temp_string == NULL) {
1020 free(first_line_buf);
1021 return -1;
1022 }
1023 if (strchr(temp_string, '.') != NULL) {
1024 char *ppp;
1025 ppp = strtok(temp_string, ".");
1026 status_line->http_code = (int)strtol(ppp, &temp_string_2, 10);
1027 if (*temp_string_2 != '\0') {
1028 free(first_line_buf);
1029 return -1;
1030 }
1031 ppp = strtok(NULL, "");
1032 status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10);
1033 if (*temp_string_2 != '\0') {
1034 free(first_line_buf);
1035 return -1;
1036 }
1037 temp_string += 6; /* 400.1 SP */
1038 } else {
1039 status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10);
1040 status_line->http_subcode = -1;
1041 if (*temp_string_2 != '\0') {
1042 free(first_line_buf);
1043 return -1;
1044 }
1045 temp_string += 4; /* 400 SP */
1046 }
1047
1048 /* Human readable message: "Not Found" CRLF */
1049
1050 temp_string = strtok(temp_string, "");
1051 if (temp_string == NULL) {
1052 status_line->msg = "";
1053 return 0;
1054 }
1055 status_line->msg = status_line->first_line + (temp_string - first_line_buf);
1056 free(first_line_buf);
1057
1058 return 0;
1059}
1060
1061/* TODO: where to put this, it's actually part of sstrings2 (logically)?
1062 */
1063const char *strrstr2(const char *haystack, const char *needle) {
1064 if (haystack == NULL || needle == NULL) {
1065 return NULL;
1066 }
1067
1068 if (haystack[0] == '\0' || needle[0] == '\0') {
1069 return NULL;
1070 }
1071
1072 int counter = 0;
1073 const char *prev_pos = NULL;
1074 const char *pos = haystack;
1075 size_t len = strlen(needle);
1076 for (;;) {
1077 pos = strstr(pos, needle);
1078 if (pos == NULL) {
1079 if (counter == 0) {
1080 return NULL;
1081 }
1082 return prev_pos;
1083 }
1084 counter++;
1085 prev_pos = pos;
1086 pos += len;
1087 if (*pos == '\0') {
1088 return prev_pos;
1089 }
1090 }
1091}
1092
1093void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) {
1094 free(buf->buf);
1095 buf->buf = NULL;
1096}
1097
1098void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) {
1099 free(buf->buf);
1100 buf->buf = NULL;
1101}
1102
1103int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char *data, size_t datalen) {
1104 if ((*buf = calloc(1, sizeof(curlhelp_read_curlbuf))) == NULL) {
1105 return 1;
1106 }
1107
1108 (*buf)->buflen = datalen;
1109 (*buf)->buf = (char *)calloc((*buf)->buflen, sizeof(char));
1110 if ((*buf)->buf == NULL) {
1111 return -1;
1112 }
1113 memcpy((*buf)->buf, data, datalen);
1114 (*buf)->pos = 0;
1115 return 0;
1116}
1117
1118size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
1119 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
1120
1121 size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos);
1122
1123 memcpy(buffer, buf->buf + buf->pos, minimalSize);
1124 buf->pos += minimalSize;
1125
1126 return minimalSize;
1127}
1128
1129int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf) {
1130 if ((*buf = calloc(1, sizeof(curlhelp_write_curlbuf))) == NULL) {
1131 return 1;
1132 }
1133 (*buf)->bufsize = DEFAULT_BUFFER_SIZE * sizeof(char);
1134 (*buf)->buflen = 0;
1135 (*buf)->buf = (char *)calloc((*buf)->bufsize, sizeof(char));
1136 if ((*buf)->buf == NULL) {
1137 return -1;
1138 }
1139 return 0;
1140}
1141
1142size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
1143 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
1144
1145 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
1146 buf->bufsize = buf->bufsize * 2;
1147 buf->buf = (char *)realloc(buf->buf, buf->bufsize);
1148 if (buf->buf == NULL) {
1149 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
1150 return 0;
1151 }
1152 }
1153
1154 memcpy(buf->buf + buf->buflen, buffer, size * nmemb);
1155 buf->buflen += size * nmemb;
1156 buf->buf[buf->buflen] = '\0';
1157
1158 return size * nmemb;
1159}
1160
1161void cleanup(check_curl_global_state global_state) {
1162 if (global_state.status_line_initialized) {
1163 curlhelp_free_statusline(global_state.status_line);
1164 }
1165 global_state.status_line_initialized = false;
1166
1167 if (global_state.curl_easy_initialized) {
1168 curl_easy_cleanup(global_state.curl);
1169 }
1170 global_state.curl_easy_initialized = false;
1171
1172 if (global_state.curl_global_initialized) {
1173 curl_global_cleanup();
1174 }
1175 global_state.curl_global_initialized = false;
1176
1177 if (global_state.body_buf_initialized) {
1178 curlhelp_freewritebuffer(global_state.body_buf);
1179 }
1180 global_state.body_buf_initialized = false;
1181
1182 if (global_state.header_buf_initialized) {
1183 curlhelp_freewritebuffer(global_state.header_buf);
1184 }
1185 global_state.header_buf_initialized = false;
1186
1187 if (global_state.put_buf_initialized) {
1188 curlhelp_freereadbuffer(global_state.put_buf);
1189 }
1190 global_state.put_buf_initialized = false;
1191
1192 if (global_state.header_list) {
1193 curl_slist_free_all(global_state.header_list);
1194 }
1195
1196 if (global_state.host) {
1197 curl_slist_free_all(global_state.host);
1198 }
1199}
1200
1201int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) {
1202 struct addrinfo hints = {
1203 .ai_family = addr_family,
1204 .ai_socktype = SOCK_STREAM,
1205 .ai_flags = AI_CANONNAME,
1206 };
1207
1208 struct addrinfo *result;
1209 int errcode = getaddrinfo(host, NULL, &hints, &result);
1210 if (errcode != 0) {
1211 return errcode;
1212 }
1213
1214 strcpy(buf, "");
1215 struct addrinfo *res = result;
1216
1217 size_t buflen_remaining = buflen - 1;
1218 size_t addrstr_len;
1219 char addrstr[100];
1220 void *ptr = {0};
1221 while (res) {
1222 switch (res->ai_family) {
1223 case AF_INET:
1224 ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1225 break;
1226 case AF_INET6:
1227 ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1228 break;
1229 }
1230
1231 inet_ntop(res->ai_family, ptr, addrstr, 100);
1232 if (verbose >= 1) {
1233 printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4,
1234 addrstr);
1235 }
1236
1237 // Append all IPs to buf as a comma-separated string
1238 addrstr_len = strlen(addrstr);
1239 if (buflen_remaining > addrstr_len + 1) {
1240 if (buf[0] != '\0') {
1241 strncat(buf, ",", buflen_remaining);
1242 buflen_remaining -= 1;
1243 }
1244 strncat(buf, addrstr, buflen_remaining);
1245 buflen_remaining -= addrstr_len;
1246 }
1247
1248 res = res->ai_next;
1249 }
1250
1251 freeaddrinfo(result);
1252
1253 return 0;
1254}
1255
1256/* Checks if the server 'reply' is one of the expected 'statuscodes' */
1257bool expected_statuscode(const char *reply, const char *statuscodes) {
1258 char *expected;
1259
1260 if ((expected = strdup(statuscodes)) == NULL) {
1261 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
1262 }
1263
1264 bool result = false;
1265 for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) {
1266 if (strstr(reply, code) != NULL) {
1267 result = true;
1268 break;
1269 }
1270 }
1271
1272 free(expected);
1273 return result;
1274}
1275
1276/* returns a string "HTTP/1.x" or "HTTP/2" */
1277char *string_statuscode(int major, int minor) {
1278 static char buf[10];
1279
1280 switch (major) {
1281 case 1:
1282 snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor);
1283 break;
1284 case 2:
1285 case 3:
1286 snprintf(buf, sizeof(buf), "HTTP/%d", major);
1287 break;
1288 default:
1289 /* assuming here HTTP/N with N>=4 */
1290 snprintf(buf, sizeof(buf), "HTTP/%d", major);
1291 break;
1292 }
1293
1294 return buf;
1295}
1296
1297/* check whether a file exists */
1298void test_file(char *path) {
1299 if (access(path, R_OK) == 0) {
1300 return;
1301 }
1302 usage2(_("file does not exist or is not readable"), path);
1303}
1304
1305mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
1306 int days_till_exp_crit);
1307
1308mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
1309 int crit_days_till_exp) {
1310 mp_subcheck sc_cert_result = mp_subcheck_init();
1311 sc_cert_result = mp_set_subcheck_default_state(sc_cert_result, STATE_OK);
1312
1313#ifdef LIBCURL_FEATURE_SSL
1314 if (is_openssl_callback) {
1315# ifdef MOPL_USE_OPENSSL
1316 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
1317 * and we actually have OpenSSL in the monitoring tools
1318 */
1319 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1320# else /* MOPL_USE_OPENSSL */
1321 xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL "
1322 "callback used and not linked against OpenSSL\n");
1323 mp_set_subcheck_state(result, STATE_CRITICAL);
1324# endif /* MOPL_USE_OPENSSL */
1325 } else {
1326 struct curl_slist *slist;
1327
1328 cert_ptr_union cert_ptr = {0};
1329 cert_ptr.to_info = NULL;
1330 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_certinfo);
1331 if (!res && cert_ptr.to_info) {
1332# ifdef MOPL_USE_OPENSSL
1333 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert
1334 * parsing We only check the first certificate and assume it's the one of
1335 * the server
1336 */
1337 char *raw_cert = NULL;
1338 bool got_first_cert = false;
1339 for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
1340 if (got_first_cert) {
1341 break;
1342 }
1343
1344 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
1345 if (verbose >= 2) {
1346 printf("%d ** %s\n", i, slist->data);
1347 }
1348 if (strncmp(slist->data, "Cert:", 5) == 0) {
1349 raw_cert = &slist->data[5];
1350 got_first_cert = true;
1351 break;
1352 }
1353 }
1354 }
1355
1356 if (!raw_cert) {
1357
1358 xasprintf(&sc_cert_result.output,
1359 _("Cannot retrieve certificates from CERTINFO information - "
1360 "certificate data was empty"));
1361 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1362 return sc_cert_result;
1363 }
1364
1365 BIO *cert_BIO = BIO_new(BIO_s_mem());
1366 BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert));
1367
1368 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
1369 if (!cert) {
1370 xasprintf(&sc_cert_result.output,
1371 _("Cannot read certificate from CERTINFO information - BIO error"));
1372 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1373 return sc_cert_result;
1374 }
1375
1376 BIO_free(cert_BIO);
1377 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1378# else /* MOPL_USE_OPENSSL */
1379 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our
1380 * disposal, so we use the libcurl CURLINFO data
1381 */
1382 return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn,
1383 days_till_exp_crit);
1384# endif /* MOPL_USE_OPENSSL */
1385 } else {
1386 xasprintf(&sc_cert_result.output,
1387 _("Cannot retrieve certificates - cURL returned %d - %s"), res,
1388 curl_easy_strerror(res));
1389 mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1390 }
1391 }
1392#endif /* LIBCURL_FEATURE_SSL */
1393
1394 return sc_cert_result;
1395}
1396
1397char *fmt_url(check_curl_working_state workingState) {
1398 char *url = calloc(DEFAULT_BUFFER_SIZE, sizeof(char));
1399 if (url == NULL) {
1400 die(STATE_UNKNOWN, "memory allocation failed");
1401 }
1402
1403 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http",
1404 (workingState.use_ssl & (workingState.host_name != NULL))
1405 ? workingState.host_name
1406 : workingState.server_address,
1407 workingState.serverPort, workingState.server_url);
1408
1409 return url;
1410}
1411
1412bool hostname_gets_resolved_locally(const check_curl_working_state working_state) {
1413 char *host_name_display = "NULL";
1414 unsigned long host_name_len = 0;
1415 if (working_state.host_name) {
1416 host_name_len = strlen(working_state.host_name);
1417 host_name_display = working_state.host_name;
1418 }
1419
1420 /* IPv4 or IPv6 version of the address */
1421 char *server_address_clean = strdup(working_state.server_address);
1422 /* server address might be a full length ipv6 address encapsulated in square brackets */
1423 if ((strnlen(working_state.server_address, MAX_IPV4_HOSTLENGTH) > 2) &&
1424 (working_state.server_address[0] == '[') &&
1425 (working_state.server_address[strlen(working_state.server_address) - 1] == ']')) {
1426 server_address_clean =
1427 strndup(working_state.server_address + 1, strlen(working_state.server_address) - 2);
1428 }
1429
1430 /* check curlopt_noproxy option first */
1431 /* https://curl.se/libcurl/c/CURLOPT_NOPROXY.html */
1432
1433 /* curlopt_noproxy is specified as a comma separated list of
1434 direct IPv4 or IPv6 addresses e.g 130.133.8.40, 2001:4860:4802:32::a ,
1435 IPv4 or IPv6 CIDR regions e.g 10.241.0.0/16 , abcd:ef01:2345::/48 ,
1436 direct hostnames e.g example.com, google.de */
1437
1438 if (working_state.curlopt_noproxy != NULL) {
1439 char *curlopt_noproxy_copy = strdup(working_state.curlopt_noproxy);
1440 char *noproxy_item = strtok(curlopt_noproxy_copy, ",");
1441 while (noproxy_item != NULL) {
1442 unsigned long noproxy_item_len = strlen(noproxy_item);
1443
1444 /* According to the CURLOPT_NOPROXY documentation: */
1445 /* https://curl.se/libcurl/c/CURLOPT_NOPROXY.html */
1446 /* The only wildcard available is a single * character, which matches all hosts, and
1447 * effectively disables the proxy. */
1448 if (strlen(noproxy_item) == 1 && noproxy_item[0] == '*') {
1449 if (verbose >= 1) {
1450 printf("* noproxy includes '*' which disables proxy for all host name incl. : "
1451 "%s / server address incl. : %s\n",
1452 host_name_display, server_address_clean);
1453 }
1454 free(curlopt_noproxy_copy);
1455 free(server_address_clean);
1456 return true;
1457 }
1458
1459 /* direct comparison with the server_address */
1460 if (server_address_clean != NULL &&
1461 strlen(server_address_clean) == strlen(noproxy_item) &&
1462 strcmp(server_address_clean, noproxy_item) == 0) {
1463 if (verbose >= 1) {
1464 printf("* server_address is in the no_proxy list: %s\n", noproxy_item);
1465 }
1466 free(curlopt_noproxy_copy);
1467 free(server_address_clean);
1468 return true;
1469 }
1470
1471 /* direct comparison with the host_name */
1472 if (working_state.host_name != NULL && host_name_len == noproxy_item_len &&
1473 strcmp(working_state.host_name, noproxy_item) == 0) {
1474 if (verbose >= 1) {
1475 printf("* host_name is in the no_proxy list: %s\n", noproxy_item);
1476 }
1477 free(curlopt_noproxy_copy);
1478 free(server_address_clean);
1479 return true;
1480 }
1481
1482 /* check if hostname is a subdomain of the item, e.g www.example.com when token is
1483 * example.com */
1484 /* subdomain1.acme.com will not will use a proxy if you only specify 'acme' in the
1485 * noproxy */
1486 /* check if noproxy_item is a suffix */
1487 /* check if the character just before the suffix is '.' */
1488 if (working_state.host_name != NULL && host_name_len > noproxy_item_len) {
1489 unsigned long suffix_start_idx = host_name_len - noproxy_item_len;
1490 if (strcmp(working_state.host_name + suffix_start_idx, noproxy_item) == 0 &&
1491 working_state.host_name[suffix_start_idx - 1] == '.') {
1492 if (verbose >= 1) {
1493 printf("* host_name: %s is a subdomain of the no_proxy list item: %s\n",
1494 working_state.host_name, noproxy_item);
1495 }
1496 free(curlopt_noproxy_copy);
1497 free(server_address_clean);
1498 return true;
1499 }
1500 }
1501
1502 // noproxy_item could be a CIDR IP range
1503 if (server_address_clean != NULL && strlen(server_address_clean)) {
1504 ip_addr_inside ip_addr_inside_cidr_ret =
1505 ip_addr_inside_cidr(noproxy_item, server_address_clean);
1506
1507 if (ip_addr_inside_cidr_ret.error == NO_ERROR) {
1508 if (ip_addr_inside_cidr_ret.inside) {
1509 return true;
1510 } else {
1511 if (verbose >= 1) {
1512 printf("server address: %s is not inside IP cidr: %s\n",
1513 server_address_clean, noproxy_item);
1514 }
1515 }
1516 } else {
1517 if (verbose >= 1) {
1518 printf("could not fully determine if server address: %s is inside the IP "
1519 "cidr: %s\n",
1520 server_address_clean, noproxy_item);
1521 }
1522 }
1523 }
1524
1525 noproxy_item = strtok(NULL, ",");
1526 }
1527
1528 free(curlopt_noproxy_copy);
1529 }
1530
1531 if (working_state.curlopt_proxy != NULL) {
1532 // Libcurl documentation
1533 // Setting the proxy string to "" (an empty string) explicitly disables the use of a proxy,
1534 // even if there is an environment variable set for it.
1535 if (strlen(working_state.curlopt_proxy) == 0) {
1536 return true;
1537 }
1538
1539 if (strncmp(working_state.curlopt_proxy, "http://", 7) == 0) {
1540 if (verbose >= 1) {
1541 printf(
1542 "* proxy scheme is http, proxy: %s resolves host: %s or server_address: %s\n",
1543 working_state.curlopt_proxy, host_name_display, server_address_clean);
1544 }
1545 free(server_address_clean);
1546 return false;
1547 }
1548
1549 if (strncmp(working_state.curlopt_proxy, "https://", 8) == 0) {
1550 if (verbose >= 1) {
1551 printf(
1552 "* proxy scheme is https, proxy: %s resolves host: %s or server_address: %s\n",
1553 working_state.curlopt_proxy, host_name_display, server_address_clean);
1554 }
1555 free(server_address_clean);
1556 return false;
1557 }
1558
1559 if (strncmp(working_state.curlopt_proxy, "socks4://", 9) == 0) {
1560 if (verbose >= 1) {
1561 printf("* proxy scheme is socks, proxy: %s does not resolve host: %s or "
1562 "server_address: %s\n",
1563 working_state.curlopt_proxy, host_name_display, server_address_clean);
1564 }
1565 free(server_address_clean);
1566 return true;
1567 }
1568
1569 if (strncmp(working_state.curlopt_proxy, "socks4a://", 10) == 0) {
1570 if (verbose >= 1) {
1571 printf("* proxy scheme is socks4a, proxy: %s resolves host: %s or server_address: "
1572 "%s\n",
1573 working_state.curlopt_proxy, host_name_display, server_address_clean);
1574 }
1575 free(server_address_clean);
1576 return false;
1577 }
1578
1579 if (strncmp(working_state.curlopt_proxy, "socks5://", 9) == 0) {
1580 if (verbose >= 1) {
1581 printf("* proxy scheme is socks5, proxy: %s does not resolve host: %s or "
1582 "server_address: %s\n",
1583 working_state.curlopt_proxy, host_name_display, server_address_clean);
1584 }
1585 free(server_address_clean);
1586 return true;
1587 }
1588
1589 if (strncmp(working_state.curlopt_proxy, "socks5h://", 10) == 0) {
1590 if (verbose >= 1) {
1591 printf("* proxy scheme is socks5h, proxy: %s resolves host: %s or server_address: "
1592 "%s\n",
1593 working_state.curlopt_proxy, host_name_display, server_address_clean);
1594 }
1595 free(server_address_clean);
1596 return false;
1597 }
1598
1599 // Libcurl documentation:
1600 // Without a scheme prefix, CURLOPT_PROXYTYPE can be used to specify which kind of proxy the
1601 // string identifies. We do not set this value Without a scheme, it is treated as an http
1602 // proxy
1603
1604 return false;
1605 }
1606
1607 if (verbose >= 1) {
1608 printf("* proxy scheme is unknown/unavailable, no proxy is assumed for host: %s or "
1609 "server_address: %s\n",
1610 host_name_display, server_address_clean);
1611 }
1612
1613 free(server_address_clean);
1614 return 0;
1615}
1616
1617ip_addr_inside ip_addr_inside_cidr(const char *cidr_region_or_ip_addr, const char *target_ip) {
1618 unsigned int slash_count = 0;
1619 unsigned int last_slash_idx = 0;
1620 for (size_t i = 0; i < strlen(cidr_region_or_ip_addr); i++) {
1621 if (cidr_region_or_ip_addr[i] == '/') {
1622 slash_count++;
1623 last_slash_idx = (unsigned int)i;
1624 }
1625 }
1626
1627 char *cidr_ip_part = NULL;
1628 int prefix_length = 0;
1629 ip_addr_inside result = {
1630 .inside = false,
1631 .error = NO_ERROR,
1632 };
1633
1634 if (slash_count == 0) {
1635 cidr_ip_part = strdup(cidr_region_or_ip_addr);
1636 if (!cidr_ip_part) {
1637 result.error = FAILED_STRDUP;
1638 return result;
1639 }
1640 } else if (slash_count == 1) {
1641 cidr_ip_part = strndup(cidr_region_or_ip_addr, last_slash_idx);
1642 if (!cidr_ip_part) {
1643 result.error = FAILED_STRDUP;
1644 return result;
1645 }
1646
1647 errno = 0;
1648 long long tmp = strtoll(cidr_region_or_ip_addr + last_slash_idx + 1, NULL, 10);
1649 if (errno == ERANGE) {
1650 if (verbose >= 1) {
1651 printf("cidr_region_or_ip: %s , could not parse subnet length\n",
1652 cidr_region_or_ip_addr);
1653 }
1654 free(cidr_ip_part);
1655 result.error = COULD_NOT_PARSE_SUBNET_LENGTH;
1656 return result;
1657 }
1658 prefix_length = (int)tmp;
1659 } else {
1660 if (verbose >= 1) {
1661 printf("cidr_region_or_ip: %s , has %d number of '/' characters, is not a valid "
1662 "cidr_region or IP\n",
1663 cidr_region_or_ip_addr, slash_count);
1664 }
1665 result.error = CIDR_REGION_INVALID;
1666 return result;
1667 }
1668
1669 int cidr_addr_family, target_addr_family;
1670 if (strchr(cidr_ip_part, ':')) {
1671 cidr_addr_family = AF_INET6;
1672 } else {
1673 cidr_addr_family = AF_INET;
1674 }
1675
1676 if (strchr(target_ip, ':')) {
1677 target_addr_family = AF_INET6;
1678 } else {
1679 target_addr_family = AF_INET;
1680 }
1681
1682 if (cidr_addr_family != target_addr_family) {
1683 if (verbose >= 1) {
1684 printf("cidr address: %s and target ip address: %s have different address families\n",
1685 cidr_ip_part, target_ip);
1686 }
1687 free(cidr_ip_part);
1688 result.inside = false;
1689 return result;
1690 }
1691
1692 // If no prefix is given, treat the cidr as a single address (full-length prefix)
1693 if (slash_count == 0) {
1694 prefix_length = (cidr_addr_family == AF_INET) ? 32 : 128;
1695 }
1696
1697 int max_bits = (cidr_addr_family == AF_INET) ? 32u : 128u;
1698 if (prefix_length < 0 || prefix_length > max_bits) {
1699 if (verbose >= 1) {
1700 printf("cidr_region_or_ip: %s has invalid prefix length: %u\n", cidr_region_or_ip_addr,
1701 prefix_length);
1702 }
1703 free(cidr_ip_part);
1704 result.error = CIDR_REGION_INVALID_PREFIX;
1705 return result;
1706 }
1707
1708 if (verbose >= 1) {
1709 printf("cidr_region_or_ip: %s , has prefix length: %u\n", cidr_region_or_ip_addr,
1710 prefix_length);
1711 }
1712
1713 int inet_pton_rc;
1714 uint8_t *cidr_bytes = NULL;
1715 uint8_t *target_bytes = NULL;
1716 uint8_t cidr_buf[16];
1717 uint8_t target_buf[16];
1718
1719 if (cidr_addr_family == AF_INET) {
1720 struct in_addr cidr_ipv4;
1721 struct in_addr target_ipv4;
1722 inet_pton_rc = inet_pton(AF_INET, cidr_ip_part, &cidr_ipv4);
1723 if (inet_pton_rc != 1) {
1724 if (verbose >= 1) {
1725 printf("ip string: %s contains characters not valid for its address family: IPv4\n",
1726 cidr_ip_part);
1727 }
1728 free(cidr_ip_part);
1729 result.error = IP_CONTAINS_INVALID_CHARACTERS;
1730 return result;
1731 }
1732 inet_pton_rc = inet_pton(AF_INET, target_ip, &target_ipv4);
1733 if (inet_pton_rc != 1) {
1734 if (verbose >= 1) {
1735 printf("ip string: %s contains characters not valid for its address family: IPv4\n",
1736 target_ip);
1737 }
1738 free(cidr_ip_part);
1739 result.error = IP_CONTAINS_INVALID_CHARACTERS;
1740 return result;
1741 }
1742 // copy the addresses in network byte order to a buffer for comparison
1743 memcpy(cidr_buf, &cidr_ipv4.s_addr, 4);
1744 memcpy(target_buf, &target_ipv4.s_addr, 4);
1745 cidr_bytes = cidr_buf;
1746 target_bytes = target_buf;
1747 } else {
1748 struct in6_addr cidr_ipv6;
1749 struct in6_addr target_ipv6;
1750 inet_pton_rc = inet_pton(AF_INET6, cidr_ip_part, &cidr_ipv6);
1751 if (inet_pton_rc != 1) {
1752 if (verbose >= 1) {
1753 printf("ip string: %s contains characters not valid for its address family: IPv6\n",
1754 cidr_ip_part);
1755 }
1756 free(cidr_ip_part);
1757 result.error = IP_CONTAINS_INVALID_CHARACTERS;
1758 return result;
1759 }
1760 inet_pton_rc = inet_pton(AF_INET6, target_ip, &target_ipv6);
1761 if (inet_pton_rc != 1) {
1762 if (verbose >= 1) {
1763 printf("ip string: %s contains characters not valid for its address family: IPv6\n",
1764 target_ip);
1765 }
1766 free(cidr_ip_part);
1767 result.error = IP_CONTAINS_INVALID_CHARACTERS;
1768 return result;
1769 }
1770 memcpy(cidr_buf, &cidr_ipv6, 16);
1771 memcpy(target_buf, &target_ipv6, 16);
1772 cidr_bytes = cidr_buf;
1773 target_bytes = target_buf;
1774 }
1775
1776 int prefix_bytes = prefix_length / 8;
1777 int prefix_bits = prefix_length % 8;
1778
1779 if (prefix_bytes > 0) {
1780 if (memcmp(cidr_bytes, target_bytes, (size_t)prefix_bytes) != 0) {
1781 if (verbose >= 1) {
1782 printf("the first %d bytes of the cidr_region_or_ip: %s and target_ip: %s are "
1783 "different\n",
1784 prefix_bytes, cidr_ip_part, target_ip);
1785 }
1786 free(cidr_ip_part);
1787 result.inside = false;
1788 return result;
1789 }
1790 }
1791
1792 if (prefix_bits != 0) {
1793 uint8_t cidr_oct = cidr_bytes[prefix_bytes];
1794 uint8_t target_oct = target_bytes[prefix_bytes];
1795 // the mask has first prefix_bits bits 1, the rest as 0
1796 uint8_t mask = (uint8_t)(0xFFu << (8 - prefix_bits));
1797 if ((cidr_oct & mask) != (target_oct & mask)) {
1798 if (verbose >= 1) {
1799 printf("looking at the last %d bits of the prefix, cidr_region_or_ip(%s) byte is: "
1800 "%u and target_ip byte(%s) is: %u, applying bitmask: %02X returns different "
1801 "results\n",
1802 prefix_bits, cidr_ip_part, (unsigned)cidr_oct, target_ip,
1803 (unsigned)target_oct, mask);
1804 }
1805 free(cidr_ip_part);
1806 result.inside = false;
1807 return result;
1808 }
1809 }
1810
1811 free(cidr_ip_part);
1812 result.inside = true;
1813 return result;
1814}
diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h
new file mode 100644
index 00000000..55df9bc1
--- /dev/null
+++ b/plugins/check_curl.d/check_curl_helpers.h
@@ -0,0 +1,151 @@
1#include "./config.h"
2#include <curl/curl.h>
3#include "../picohttpparser/picohttpparser.h"
4#include "output.h"
5
6#if defined(HAVE_SSL) && defined(USE_OPENSSL)
7# include <openssl/opensslv.h>
8#endif
9
10enum {
11 MAX_IPV4_HOSTLENGTH = 255,
12};
13
14/* for buffers for header and body */
15typedef struct {
16 size_t buflen;
17 size_t bufsize;
18 char *buf;
19} curlhelp_write_curlbuf;
20
21/* for buffering the data sent in PUT */
22typedef struct {
23 size_t buflen;
24 off_t pos;
25 char *buf;
26} curlhelp_read_curlbuf;
27
28/* for parsing the HTTP status line */
29typedef struct {
30 int http_major; /* major version of the protocol, always 1 (HTTP/0.9
31 * never reached the big internet most likely) */
32 int http_minor; /* minor version of the protocol, usually 0 or 1 */
33 int http_code; /* HTTP return code as in RFC 2145 */
34 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
35 * http://support.microsoft.com/kb/318380/en-us */
36 const char *msg; /* the human readable message */
37 char *first_line; /* a copy of the first line */
38} curlhelp_statusline;
39
40typedef struct {
41 bool curl_global_initialized;
42 bool curl_easy_initialized;
43
44 bool body_buf_initialized;
45 curlhelp_write_curlbuf *body_buf;
46
47 bool header_buf_initialized;
48 curlhelp_write_curlbuf *header_buf;
49
50 bool status_line_initialized;
51 curlhelp_statusline *status_line;
52
53 bool put_buf_initialized;
54 curlhelp_read_curlbuf *put_buf;
55
56 CURL *curl;
57
58 struct curl_slist *header_list;
59 struct curl_slist *host;
60} check_curl_global_state;
61
62/* to know the underlying SSL library used by libcurl */
63typedef enum curlhelp_ssl_library {
64 CURLHELP_SSL_LIBRARY_UNKNOWN,
65 CURLHELP_SSL_LIBRARY_OPENSSL,
66 CURLHELP_SSL_LIBRARY_LIBRESSL,
67 CURLHELP_SSL_LIBRARY_GNUTLS,
68 CURLHELP_SSL_LIBRARY_NSS
69} curlhelp_ssl_library;
70
71#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch))
72
73typedef struct {
74 int errorcode;
75 check_curl_global_state curl_state;
76 check_curl_working_state working_state;
77} check_curl_configure_curl_wrapper;
78
79check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_curl_config config,
80 check_curl_working_state working_state,
81 bool check_cert,
82 bool on_redirect_dependent,
83 int follow_method, long max_depth);
84
85void handle_curl_option_return_code(CURLcode res, const char *option);
86
87int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf);
88size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/,
89 void * /*stream*/);
90void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/);
91
92int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char * /*data*/, size_t /*datalen*/);
93size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/,
94 void * /*stream*/);
95void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/);
96
97curlhelp_ssl_library curlhelp_get_ssl_library(void);
98const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/);
99
100typedef union {
101 struct curl_slist *to_info;
102 struct curl_certinfo *to_certinfo;
103} cert_ptr_union;
104int net_noopenssl_check_certificate(cert_ptr_union *, int, int);
105
106int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/);
107void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/);
108
109char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header);
110mp_subcheck check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/,
111 int /*maximum_age*/);
112size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
113 const curlhelp_write_curlbuf *body_buf);
114int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family);
115CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm);
116
117#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
118const char *strrstr2(const char *haystack, const char *needle);
119
120void cleanup(check_curl_global_state global_state);
121
122bool expected_statuscode(const char *reply, const char *statuscodes);
123char *string_statuscode(int major, int minor);
124
125void test_file(char *path);
126mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
127 int crit_days_till_exp);
128char *fmt_url(check_curl_working_state workingState);
129
130/* determine_hostname_resolver determines if the host or the proxy resolves the target hostname
131returns RESOLVE_LOCALLY if requester resolves the hostname locally, RESOLVE_REMOTELY if proxy
132resolves the hostname */
133bool hostname_gets_resolved_locally(const check_curl_working_state working_state);
134
135/* Checks if an IP is inside given CIDR region. Using /protocol_size or not specifying the prefix
136length performs an equality check. Supports both IPv4 and IPv6 returns 1 if the target_ip address is
137inside the given cidr_region_or_ip_addr, 0 if its out. return codes < 0 mean an error has occurred.
138*/
139typedef enum {
140 NO_ERROR,
141 FAILED_STRDUP,
142 COULD_NOT_PARSE_SUBNET_LENGTH,
143 CIDR_REGION_INVALID,
144 CIDR_REGION_INVALID_PREFIX,
145 IP_CONTAINS_INVALID_CHARACTERS,
146} ip_addr_inside_error_code;
147typedef struct {
148 bool inside;
149 ip_addr_inside_error_code error;
150} ip_addr_inside;
151ip_addr_inside ip_addr_inside_cidr(const char *cidr_region_or_ip_addr, const char *target_ip);
diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h
new file mode 100644
index 00000000..bcdf3010
--- /dev/null
+++ b/plugins/check_curl.d/config.h
@@ -0,0 +1,123 @@
1#pragma once
2
3#include "../../config.h"
4#include "../common.h"
5#include "../../lib/states.h"
6#include "../../lib/thresholds.h"
7#include <stddef.h>
8#include <string.h>
9#include <sys/socket.h>
10#include "curl/curl.h"
11#include "perfdata.h"
12#include "regex.h"
13
14enum {
15 MAX_RE_SIZE = 1024,
16 HTTP_PORT = 80,
17 HTTPS_PORT = 443,
18 MAX_PORT = 65535,
19 DEFAULT_MAX_REDIRS = 15
20};
21
22enum {
23 FOLLOW_HTTP_CURL = 0,
24 FOLLOW_LIBCURL = 1
25};
26
27enum {
28 STICKY_NONE = 0,
29 STICKY_HOST = 1,
30 STICKY_PORT = 2
31};
32
33#define HTTP_EXPECT "HTTP/"
34#define DEFAULT_BUFFER_SIZE 2048
35#define DEFAULT_SERVER_URL "/"
36
37typedef struct {
38 char *server_address;
39 char *server_url;
40 char *host_name;
41
42 char *http_method;
43
44 char *http_post_data;
45
46 unsigned short virtualPort;
47 unsigned short serverPort;
48
49 bool use_ssl;
50 bool no_body;
51
52 /* curl CURLOPT_PROXY option will be set to this value if not NULL */
53 char *curlopt_proxy;
54 /* curl CURLOPT_NOPROXY option will be set to this value if not NULL */
55 char *curlopt_noproxy;
56} check_curl_working_state;
57
58check_curl_working_state check_curl_working_state_init();
59
60typedef struct {
61 bool automatic_decompression;
62 bool haproxy_protocol;
63 long socket_timeout;
64 sa_family_t sin_family;
65 long curl_http_version;
66 char **http_opt_headers;
67 size_t http_opt_headers_count;
68 long ssl_version;
69 char *client_cert;
70 char *client_privkey;
71 char *ca_cert;
72 bool verify_peer_and_host;
73 char proxy[DEFAULT_BUFFER_SIZE];
74 char no_proxy[DEFAULT_BUFFER_SIZE];
75 char user_agent[DEFAULT_BUFFER_SIZE];
76 char proxy_auth[MAX_INPUT_BUFFER];
77 char user_auth[MAX_INPUT_BUFFER];
78 char *http_content_type;
79 char *cookie_jar_file;
80} check_curl_static_curl_config;
81
82typedef struct {
83 check_curl_working_state initial_config;
84
85 check_curl_static_curl_config curl_config;
86 long max_depth;
87 int followmethod;
88 int followsticky;
89
90 int maximum_age;
91
92 // the original regex string from the command line
93 char regexp[MAX_RE_SIZE];
94
95 // the compiled regex for usage later
96 regex_t compiled_regex;
97
98 mp_state_enum state_regex;
99 bool invert_regex;
100 bool check_cert;
101 bool continue_after_check_cert;
102 int days_till_exp_warn;
103 int days_till_exp_crit;
104 mp_thresholds thlds;
105 mp_range page_length_limits;
106 bool page_length_limits_is_set;
107 struct {
108 char string[MAX_INPUT_BUFFER];
109 bool is_present;
110 } server_expect;
111 char string_expect[MAX_INPUT_BUFFER];
112 char header_expect[MAX_INPUT_BUFFER];
113 mp_state_enum on_redirect_result_state;
114 bool on_redirect_dependent;
115
116 bool show_extended_perfdata;
117 bool show_body;
118
119 bool output_format_is_set;
120 mp_output_format output_format;
121} check_curl_config;
122
123check_curl_config check_curl_config_init();
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c
index 29c85206..dd466d00 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -1,38 +1,44 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dbi plugin 3 * Monitoring check_dbi plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2011 Monitoring Plugins Development Team 6 * Copyright (c) 2011-2024 Monitoring Plugins Development Team
7* Author: Sebastian 'tokkee' Harl <sh@teamix.net> 7 * Original Author: Sebastian 'tokkee' Harl <sh@teamix.net>
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_dbi plugin 11 * This file contains the check_dbi plugin
12* 12 *
13* Runs an arbitrary (SQL) command and checks the result. 13 * Runs an arbitrary (SQL) command and checks the result.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_dbi"; 32const char *progname = "check_dbi";
33const char *copyright = "2011"; 33const char *copyright = "2011-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "../lib/monitoringplug.h"
37#include "thresholds.h"
38#include "perfdata.h"
39#include "output.h"
40#include "states.h"
41#include "check_dbi.d/config.h"
36#include "common.h" 42#include "common.h"
37#include "utils.h" 43#include "utils.h"
38#include "utils_cmd.h" 44#include "utils_cmd.h"
@@ -43,7 +49,7 @@ const char *email = "devel@monitoring-plugins.org";
43 49
44/* required for NAN */ 50/* required for NAN */
45#ifndef _ISOC99_SOURCE 51#ifndef _ISOC99_SOURCE
46#define _ISOC99_SOURCE 52# define _ISOC99_SOURCE
47#endif 53#endif
48 54
49#include <assert.h> 55#include <assert.h>
@@ -53,774 +59,893 @@ const char *email = "devel@monitoring-plugins.org";
53 59
54#include <stdarg.h> 60#include <stdarg.h>
55 61
56typedef enum { 62static int verbose = 0;
57 METRIC_CONN_TIME,
58 METRIC_SERVER_VERSION,
59 METRIC_QUERY_RESULT,
60 METRIC_QUERY_TIME,
61} np_dbi_metric_t;
62
63typedef enum {
64 TYPE_NUMERIC,
65 TYPE_STRING,
66} np_dbi_type_t;
67 63
68typedef struct { 64typedef struct {
69 char *key; 65 int errorcode;
70 char *value; 66 check_dbi_config config;
71} driver_option_t; 67} check_dbi_config_wrapper;
72
73char *host = NULL;
74int verbose = 0;
75
76char *warning_range = NULL;
77char *critical_range = NULL;
78thresholds *dbi_thresholds = NULL;
79
80char *expect = NULL;
81
82regex_t expect_re;
83char *expect_re_str = NULL;
84int expect_re_cflags = 0;
85
86np_dbi_metric_t metric = METRIC_QUERY_RESULT;
87np_dbi_type_t type = TYPE_NUMERIC;
88
89char *np_dbi_driver = NULL;
90driver_option_t *np_dbi_options = NULL;
91int np_dbi_options_num = 0;
92char *np_dbi_database = NULL;
93char *np_dbi_query = NULL;
94
95int process_arguments (int, char **);
96int validate_arguments (void);
97void print_usage (void);
98void print_help (void);
99
100double timediff (struct timeval, struct timeval);
101 68
102void np_dbi_print_error (dbi_conn, char *, ...); 69static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
70void print_usage(void);
71static void print_help(void);
103 72
104int do_query (dbi_conn, const char **, double *, double *); 73static double timediff(struct timeval /*start*/, struct timeval /*end*/);
105 74
106int 75static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...);
107main (int argc, char **argv)
108{
109 int status = STATE_UNKNOWN;
110 76
111 dbi_driver driver; 77typedef struct {
112 dbi_conn conn; 78 char *result_string;
113 79 double result_number;
114 unsigned int server_version; 80 double query_duration;
115 81 int error_code;
116 struct timeval start_timeval, end_timeval; 82 const char *error_string;
117 double conn_time = 0.0; 83 mp_state_enum query_processing_status;
118 double query_time = 0.0; 84} do_query_result;
85static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type,
86 char *query);
87
88int main(int argc, char **argv) {
89 setlocale(LC_ALL, "");
90 bindtextdomain(PACKAGE, LOCALEDIR);
91 textdomain(PACKAGE);
119 92
120 const char *query_val_str = NULL; 93 /* Parse extra opts if any */
121 double query_val = 0.0; 94 argv = np_extra_opts(&argc, argv, progname);
122 95
123 int i; 96 check_dbi_config_wrapper tmp = process_arguments(argc, argv);
124 97
125 setlocale (LC_ALL, ""); 98 if (tmp.errorcode == ERROR) {
126 bindtextdomain (PACKAGE, LOCALEDIR); 99 usage4(_("Could not parse arguments"));
127 textdomain (PACKAGE); 100 }
128 101
129 /* Parse extra opts if any */ 102 const check_dbi_config config = tmp.config;
130 argv = np_extra_opts (&argc, argv, progname);
131 103
132 if (process_arguments (argc, argv) == ERROR) 104 if (config.output_format_is_set) {
133 usage4 (_("Could not parse arguments")); 105 mp_set_format(config.output_format);
106 }
134 107
135 /* Set signal handling and alarm */ 108 /* Set signal handling and alarm */
136 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 109 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
137 usage4 (_("Cannot catch SIGALRM")); 110 usage4(_("Cannot catch SIGALRM"));
138 } 111 }
139 alarm (timeout_interval); 112 alarm(timeout_interval);
140
141 if (verbose > 2)
142 printf ("Initializing DBI\n");
143 113
144 dbi_inst *instance_p = { 0 }; 114 if (verbose > 2) {
115 printf("Initializing DBI\n");
116 }
145 117
146 if (dbi_initialize_r(NULL, instance_p) < 0) { 118 dbi_inst instance_p = NULL;
147 printf ("UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); 119 if (dbi_initialize_r(NULL, &instance_p) < 0) {
148 return STATE_UNKNOWN; 120 printf("failed to initialize DBI; possibly you don't have any drivers installed.\n");
121 exit(STATE_UNKNOWN);
149 } 122 }
150 123
124 // Try to prevent libdbi from printing stuff on stderr
125 // Who thought that would be a good idea anyway?
126 dbi_set_verbosity_r(0, instance_p);
127
151 if (instance_p == NULL) { 128 if (instance_p == NULL) {
152 printf ("UNKNOWN - failed to initialize DBI.\n"); 129 printf("failed to initialize DBI.\n");
153 return STATE_UNKNOWN; 130 exit(STATE_UNKNOWN);
154 } 131 }
155 132
156 if (verbose) 133 if (verbose) {
157 printf ("Opening DBI driver '%s'\n", np_dbi_driver); 134 printf("Opening DBI driver '%s'\n", config.dbi_driver);
135 }
158 136
159 driver = dbi_driver_open_r(np_dbi_driver, instance_p); 137 dbi_driver driver = dbi_driver_open_r(config.dbi_driver, instance_p);
160 if (! driver) { 138 if (!driver) {
161 printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", 139 printf("failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver);
162 np_dbi_driver);
163 140
164 printf ("Known drivers:\n"); 141 printf("Known drivers:\n");
165 for (driver = dbi_driver_list_r(NULL, instance_p); driver; driver = dbi_driver_list_r(driver, instance_p)) { 142 for (driver = dbi_driver_list_r(NULL, instance_p); driver;
166 printf (" - %s\n", dbi_driver_get_name (driver)); 143 driver = dbi_driver_list_r(driver, instance_p)) {
144 printf(" - %s\n", dbi_driver_get_name(driver));
167 } 145 }
168 return STATE_UNKNOWN; 146 exit(STATE_UNKNOWN);
169 } 147 }
170 148
171 /* make a connection to the database */ 149 /* make a connection to the database */
172 gettimeofday (&start_timeval, NULL); 150 struct timeval start_timeval;
173 151 gettimeofday(&start_timeval, NULL);
174 conn = dbi_conn_open (driver); 152
175 if (! conn) { 153 dbi_conn conn = dbi_conn_open(driver);
176 printf ("UNKNOWN - failed top open connection object.\n"); 154 if (!conn) {
177 dbi_conn_close (conn); 155 printf("UNKNOWN - failed top open connection object.\n");
178 return STATE_UNKNOWN; 156 dbi_conn_close(conn);
157 exit(STATE_UNKNOWN);
179 } 158 }
180 159
181 for (i = 0; i < np_dbi_options_num; ++i) { 160 for (size_t i = 0; i < config.dbi_options_num; ++i) {
182 const char *opt; 161 const char *opt;
183 162
184 if (verbose > 1) 163 if (verbose > 1) {
185 printf ("Setting DBI driver option '%s' to '%s'\n", 164 printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key,
186 np_dbi_options[i].key, np_dbi_options[i].value); 165 config.dbi_options[i].value);
166 }
187 167
188 if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value)) 168 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) {
189 continue; 169 continue;
190 /* else: status != 0 */ 170 }
191 171
192 np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'", 172 // Failing to set option
193 np_dbi_options[i].key, np_dbi_options[i].value); 173 np_dbi_print_error(conn, "failed to set option '%s' to '%s'", config.dbi_options[i].key,
194 printf ("Known driver options:\n"); 174 config.dbi_options[i].value);
175 printf("Known driver options:\n");
195 176
196 for (opt = dbi_conn_get_option_list (conn, NULL); opt; 177 for (opt = dbi_conn_get_option_list(conn, NULL); opt;
197 opt = dbi_conn_get_option_list (conn, opt)) { 178 opt = dbi_conn_get_option_list(conn, opt)) {
198 printf (" - %s\n", opt); 179 printf(" - %s\n", opt);
199 } 180 }
200 dbi_conn_close (conn); 181 dbi_conn_close(conn);
201 return STATE_UNKNOWN; 182 exit(STATE_UNKNOWN);
202 } 183 }
203 184
204 if (host) { 185 if (config.host) {
205 if (verbose > 1) 186 if (verbose > 1) {
206 printf ("Setting DBI driver option 'host' to '%s'\n", host); 187 printf("Setting DBI driver option 'host' to '%s'\n", config.host);
207 dbi_conn_set_option (conn, "host", host); 188 }
189 dbi_conn_set_option(conn, "host", config.host);
208 } 190 }
209 191
210 if (verbose) { 192 if (verbose) {
211 const char *dbname, *host; 193 const char *dbname;
194 const char *host;
212 195
213 dbname = dbi_conn_get_option (conn, "dbname"); 196 dbname = dbi_conn_get_option(conn, "dbname");
214 host = dbi_conn_get_option (conn, "host"); 197 host = dbi_conn_get_option(conn, "host");
215 198
216 if (! dbname) 199 if (!dbname) {
217 dbname = "<unspecified>"; 200 dbname = "<unspecified>";
218 if (! host) 201 }
202 if (!host) {
219 host = "<unspecified>"; 203 host = "<unspecified>";
204 }
205
206 printf("Connecting to database '%s' at host '%s'\n", dbname, host);
207 }
220 208
221 printf ("Connecting to database '%s' at host '%s'\n", 209 if (dbi_conn_connect(conn) < 0) {
222 dbname, host); 210 np_dbi_print_error(conn, "failed to connect to database");
211 exit(STATE_UNKNOWN);
223 } 212 }
224 213
225 if (dbi_conn_connect (conn) < 0) { 214 struct timeval end_timeval;
226 np_dbi_print_error (conn, "UNKNOWN - failed to connect to database"); 215 gettimeofday(&end_timeval, NULL);
227 return STATE_UNKNOWN; 216 double conn_time = timediff(start_timeval, end_timeval);
217 if (verbose) {
218 printf("Time elapsed: %f\n", conn_time);
228 } 219 }
229 220
230 gettimeofday (&end_timeval, NULL); 221 mp_check overall = mp_check_init();
231 conn_time = timediff (start_timeval, end_timeval);
232 222
233 server_version = dbi_conn_get_engine_version (conn); 223 mp_subcheck sc_connection_time = mp_subcheck_init();
234 if (verbose) 224 sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_OK);
235 printf ("Connected to server version %u\n", server_version); 225 xasprintf(&sc_connection_time.output, "Connection time: %f", conn_time);
236 226
237 if (metric == METRIC_SERVER_VERSION) 227 mp_perfdata pd_conn_duration = perfdata_init();
238 status = get_status (server_version, dbi_thresholds); 228 pd_conn_duration.label = "conntime";
229 pd_conn_duration = mp_set_pd_value(pd_conn_duration, conn_time);
239 230
240 if (verbose) 231 if (config.metric == METRIC_CONN_TIME) {
241 printf ("Time elapsed: %f\n", conn_time); 232 pd_conn_duration = mp_pd_set_thresholds(pd_conn_duration, config.thresholds);
233 mp_state_enum status = mp_get_pd_status(pd_conn_duration);
234 sc_connection_time = mp_set_subcheck_state(sc_connection_time, status);
235 if (status != STATE_OK) {
236 xasprintf(&sc_connection_time.output, "%s violates thresholds",
237 sc_connection_time.output);
238 }
239 }
242 240
243 if (metric == METRIC_CONN_TIME) 241 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_conn_duration);
244 status = get_status (conn_time, dbi_thresholds); 242 mp_add_subcheck_to_check(&overall, sc_connection_time);
243
244 unsigned int server_version = dbi_conn_get_engine_version(conn);
245 if (verbose) {
246 printf("Connected to server version %u\n", server_version);
247 }
248
249 mp_subcheck sc_server_version = mp_subcheck_init();
250 sc_server_version = mp_set_subcheck_default_state(sc_server_version, STATE_OK);
251 xasprintf(&sc_server_version.output, "Connected to server version %u", server_version);
252
253 if (config.metric == METRIC_SERVER_VERSION) {
254 mp_perfdata pd_server_version = perfdata_init();
255 pd_server_version = mp_set_pd_value(pd_server_version, server_version);
256 pd_server_version = mp_pd_set_thresholds(pd_server_version, config.thresholds);
257 mp_state_enum status = mp_get_pd_status(pd_server_version);
258 mp_add_perfdata_to_subcheck(&sc_server_version, pd_server_version);
259
260 sc_server_version = mp_set_subcheck_state(sc_server_version, status);
261
262 if (status != STATE_OK) {
263 xasprintf(&sc_server_version.output, "%s violates thresholds",
264 sc_server_version.output);
265 }
266 };
267 mp_add_subcheck_to_check(&overall, sc_server_version);
245 268
246 /* select a database */ 269 /* select a database */
247 if (np_dbi_database) { 270 if (config.database) {
248 if (verbose > 1) 271 if (verbose > 1) {
249 printf ("Selecting database '%s'\n", np_dbi_database); 272 printf("Selecting database '%s'\n", config.database);
250 273 }
251 if (dbi_conn_select_db (conn, np_dbi_database)) { 274
252 np_dbi_print_error (conn, "UNKNOWN - failed to select database '%s'", 275 mp_subcheck sc_select_db = mp_subcheck_init();
253 np_dbi_database); 276 sc_select_db = mp_set_subcheck_default_state(sc_select_db, STATE_OK);
254 return STATE_UNKNOWN; 277
278 if (dbi_conn_select_db(conn, config.database)) {
279 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.database);
280 exit(STATE_UNKNOWN);
281 } else {
282 mp_add_subcheck_to_check(&overall, sc_select_db);
255 } 283 }
256 } 284 }
257 285
258 if (np_dbi_query) { 286 // Do a query (if configured)
287 if (config.query) {
288 mp_subcheck sc_query = mp_subcheck_init();
289 sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN);
290
259 /* execute query */ 291 /* execute query */
260 status = do_query (conn, &query_val_str, &query_val, &query_time); 292 do_query_result query_res = do_query(conn, config.metric, config.type, config.query);
261 if (status != STATE_OK) 293
262 /* do_query prints an error message in this case */ 294 if (query_res.error_code != 0) {
263 return status; 295 xasprintf(&sc_query.output, "Query failed: %s", query_res.error_string);
264 296 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
265 if (metric == METRIC_QUERY_RESULT) { 297 } else if (query_res.query_processing_status != STATE_OK) {
266 if (expect) { 298 if (query_res.error_string) {
267 if ((! query_val_str) || strcmp (query_val_str, expect)) 299 xasprintf(&sc_query.output, "Failed to process query: %s", query_res.error_string);
268 status = STATE_CRITICAL; 300 } else {
269 else 301 xasprintf(&sc_query.output, "Failed to process query");
270 status = STATE_OK;
271 } 302 }
272 else if (expect_re_str) { 303 sc_query = mp_set_subcheck_state(sc_query, query_res.query_processing_status);
273 int err; 304 } else {
274 305 // query succeeded in general
275 err = regexec (&expect_re, query_val_str, 0, NULL, /* flags = */ 0); 306 xasprintf(&sc_query.output, "Query '%s' succeeded", config.query);
276 if (! err) 307
277 status = STATE_OK; 308 // that's a OK by default now
278 else if (err == REG_NOMATCH) 309 sc_query = mp_set_subcheck_default_state(sc_query, STATE_OK);
279 status = STATE_CRITICAL; 310
280 else { 311 // query duration first
281 char errmsg[1024]; 312 mp_perfdata pd_query_duration = perfdata_init();
282 regerror (err, &expect_re, errmsg, sizeof (errmsg)); 313 pd_query_duration = mp_set_pd_value(pd_query_duration, query_res.query_duration);
283 printf ("ERROR - failed to execute regular expression: %s\n", 314 pd_query_duration.label = "querytime";
284 errmsg); 315 if (config.metric == METRIC_QUERY_TIME) {
285 status = STATE_CRITICAL; 316 pd_query_duration = mp_pd_set_thresholds(pd_query_duration, config.thresholds);
317 }
318
319 mp_add_perfdata_to_subcheck(&sc_query, pd_query_duration);
320
321 if (config.metric == METRIC_QUERY_RESULT) {
322 if (config.expect) {
323 if ((!query_res.result_string) ||
324 strcmp(query_res.result_string, config.expect)) {
325 xasprintf(&sc_query.output, "Found string '%s' in query result",
326 config.expect);
327 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
328 } else {
329 xasprintf(&sc_query.output, "Did not find string '%s' in query result",
330 config.expect);
331 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
332 }
333 } else if (config.expect_re_str) {
334 int comp_err;
335 regex_t expect_re = {};
336 comp_err = regcomp(&expect_re, config.expect_re_str, config.expect_re_cflags);
337 if (comp_err != 0) {
338 // TODO error, failed to compile regex
339 // TODO move this to config sanitatisation
340 printf("Failed to compile regex from string '%s'", config.expect_re_str);
341 exit(STATE_UNKNOWN);
342 }
343
344 int err =
345 regexec(&expect_re, query_res.result_string, 0, NULL, /* flags = */ 0);
346 if (!err) {
347 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
348 xasprintf(&sc_query.output, "Found regular expression '%s' in query result",
349 config.expect_re_str);
350 } else if (err == REG_NOMATCH) {
351 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
352 xasprintf(&sc_query.output,
353 "Did not find regular expression '%s' in query result",
354 config.expect_re_str);
355 } else {
356 char errmsg[1024];
357 regerror(err, &expect_re, errmsg, sizeof(errmsg));
358 xasprintf(&sc_query.output,
359 "ERROR - failed to execute regular expression: %s\n", errmsg);
360 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
361 }
362 } else {
363 // no string matching
364 if (isnan(query_res.result_number)) {
365 // The query result is not a number, but no string checking was configured
366 // so we expected a number
367 // this is a CRITICAL
368 xasprintf(&sc_query.output, "Query '%s' result is not numeric",
369 config.query);
370 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
371
372 } else {
373
374 mp_perfdata pd_query_val = perfdata_init();
375 pd_query_val = mp_set_pd_value(pd_query_val, query_res.result_number);
376 pd_query_val.label = "query";
377 pd_query_val = mp_pd_set_thresholds(pd_query_val, config.thresholds);
378
379 mp_add_perfdata_to_subcheck(&sc_query, pd_query_val);
380 mp_state_enum query_numerical_result = mp_get_pd_status(pd_query_val);
381
382 sc_query = mp_set_subcheck_state(sc_query, query_numerical_result);
383 // TODO set pd thresholds
384 // if (config.dbi_thresholds->warning) {
385 // pd_query_val.warn= config.dbi_thresholds->warning
386 // } else {
387 // }
388
389 if (query_numerical_result == STATE_OK) {
390 xasprintf(&sc_query.output,
391 "Query result '%f' is within given thresholds",
392 query_res.result_number);
393 } else {
394 xasprintf(&sc_query.output,
395 "Query result '%f' violates the given thresholds",
396 query_res.result_number);
397 }
398 }
286 } 399 }
400 } else if (config.metric == METRIC_QUERY_TIME) {
401 mp_state_enum query_time_status = mp_get_pd_status(pd_query_duration);
402 mp_set_subcheck_state(sc_query, query_time_status);
403
404 if (query_time_status == STATE_OK) {
405 xasprintf(&sc_query.output, "Query duration '%f' is within given thresholds",
406 query_res.query_duration);
407 } else {
408 xasprintf(&sc_query.output, "Query duration '%f' violates the given thresholds",
409 query_res.query_duration);
410 }
411 } else {
412 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error
413 * which should have been reported and handled (abort) before
414 * ... unless we expected a string to be returned */
415 assert((!isnan(query_res.result_number)) || (config.type == TYPE_STRING));
287 } 416 }
288 else
289 status = get_status (query_val, dbi_thresholds);
290 } 417 }
291 else if (metric == METRIC_QUERY_TIME) 418
292 status = get_status (query_time, dbi_thresholds); 419 mp_add_subcheck_to_check(&overall, sc_query);
293 } 420 }
294 421
295 if (verbose) 422 if (verbose) {
296 printf("Closing connection\n"); 423 printf("Closing connection\n");
297 dbi_conn_close (conn); 424 }
298 425 dbi_conn_close(conn);
299 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error 426
300 * which should have been reported and handled (abort) before 427 mp_exit(overall);
301 * ... unless we expected a string to be returned */
302 assert ((metric != METRIC_QUERY_RESULT) || (! isnan (query_val))
303 || (type == TYPE_STRING));
304
305 assert ((type != TYPE_STRING) || (expect || expect_re_str));
306
307 printf ("%s - connection time: %fs", state_text (status), conn_time);
308 if (np_dbi_query) {
309 if (type == TYPE_STRING) {
310 assert (expect || expect_re_str);
311 printf (", '%s' returned '%s' in %fs", np_dbi_query,
312 query_val_str ? query_val_str : "<nothing>", query_time);
313 if (status != STATE_OK) {
314 if (expect)
315 printf (" (expected '%s')", expect);
316 else if (expect_re_str)
317 printf (" (expected regex /%s/%s)", expect_re_str,
318 ((expect_re_cflags & REG_ICASE) ? "i" : ""));
319 }
320 }
321 else if (isnan (query_val))
322 printf (", '%s' query execution time: %fs", np_dbi_query, query_time);
323 else
324 printf (", '%s' returned %f in %fs", np_dbi_query, query_val, query_time);
325 }
326
327 printf (" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time,
328 ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "",
329 ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "",
330 server_version,
331 ((metric == METRIC_SERVER_VERSION) && warning_range) ? warning_range : "",
332 ((metric == METRIC_SERVER_VERSION) && critical_range) ? critical_range : "");
333 if (np_dbi_query) {
334 if (! isnan (query_val)) /* this is also true when -e is used */
335 printf (" query=%f;%s;%s;;", query_val,
336 ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "",
337 ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : "");
338 printf (" querytime=%fs;%s;%s;0;", query_time,
339 ((metric == METRIC_QUERY_TIME) && warning_range) ? warning_range : "",
340 ((metric == METRIC_QUERY_TIME) && critical_range) ? critical_range : "");
341 }
342 printf ("\n");
343 return status;
344} 428}
345 429
346/* process command-line arguments */ 430/* process command-line arguments */
347int 431check_dbi_config_wrapper process_arguments(int argc, char **argv) {
348process_arguments (int argc, char **argv) 432 enum {
349{ 433 output_format_index = CHAR_MAX + 1,
350 int c; 434 };
351 435
352 int option = 0; 436 int option = 0;
353 static struct option longopts[] = { 437 static struct option longopts[] = {STD_LONG_OPTS,
354 STD_LONG_OPTS, 438 {"expect", required_argument, 0, 'e'},
355 439 {"regex", required_argument, 0, 'r'},
356 {"expect", required_argument, 0, 'e'}, 440 {"regexi", required_argument, 0, 'R'},
357 {"regex", required_argument, 0, 'r'}, 441 {"metric", required_argument, 0, 'm'},
358 {"regexi", required_argument, 0, 'R'}, 442 {"driver", required_argument, 0, 'd'},
359 {"metric", required_argument, 0, 'm'}, 443 {"option", required_argument, 0, 'o'},
360 {"driver", required_argument, 0, 'd'}, 444 {"query", required_argument, 0, 'q'},
361 {"option", required_argument, 0, 'o'}, 445 {"database", required_argument, 0, 'D'},
362 {"query", required_argument, 0, 'q'}, 446 {"output-format", required_argument, 0, output_format_index},
363 {"database", required_argument, 0, 'D'}, 447 {0, 0, 0, 0}};
364 {0, 0, 0, 0} 448
449 check_dbi_config_wrapper result = {
450 .config = check_dbi_config_init(),
451 .errorcode = OK,
365 }; 452 };
453 int option_char;
454 while (true) {
455 option_char = getopt_long(argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:", longopts, &option);
366 456
367 while (1) { 457 if (option_char == EOF) {
368 c = getopt_long (argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:",
369 longopts, &option);
370
371 if (c == EOF)
372 break; 458 break;
459 }
373 460
374 switch (c) { 461 switch (option_char) {
375 case '?': /* usage */ 462 case '?': /* usage */
376 usage5 (); 463 usage5();
377 case 'h': /* help */ 464 case 'h': /* help */
378 print_help (); 465 print_help();
379 exit (STATE_UNKNOWN); 466 exit(STATE_UNKNOWN);
380 case 'V': /* version */ 467 case 'V': /* version */
381 print_revision (progname, NP_VERSION); 468 print_revision(progname, NP_VERSION);
382 exit (STATE_UNKNOWN); 469 exit(STATE_UNKNOWN);
383 470
384 case 'c': /* critical range */ 471 case 'c': /* critical range */ {
385 critical_range = optarg; 472 mp_range_parsed tmp = mp_parse_range_string(optarg);
386 type = TYPE_NUMERIC; 473 if (tmp.error != MP_PARSING_SUCCESS) {
387 break; 474 die(STATE_UNKNOWN, "failed to parse critical threshold");
388 case 'w': /* warning range */ 475 }
389 warning_range = optarg; 476 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range);
390 type = TYPE_NUMERIC; 477 result.config.type = TYPE_NUMERIC;
391 break; 478 } break;
479 case 'w': /* warning range */ {
480 mp_range_parsed tmp = mp_parse_range_string(optarg);
481 if (tmp.error != MP_PARSING_SUCCESS) {
482 die(STATE_UNKNOWN, "failed to parse warning threshold");
483 }
484 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range);
485 result.config.type = TYPE_NUMERIC;
486 } break;
392 case 'e': 487 case 'e':
393 expect = optarg; 488 result.config.expect = optarg;
394 type = TYPE_STRING; 489 result.config.type = TYPE_STRING;
395 break; 490 break;
396 case 'R': 491 case 'R':
397 expect_re_cflags = REG_ICASE; 492 result.config.expect_re_cflags = REG_ICASE;
398 /* fall through */ 493 /* fall through */
399 case 'r': 494 case 'r': {
400 { 495 int err;
401 int err; 496
402 497 result.config.expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
403 expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 498 result.config.expect_re_str = optarg;
404 expect_re_str = optarg; 499 result.config.type = TYPE_STRING;
405 type = TYPE_STRING; 500
406 501 regex_t expect_re = {};
407 err = regcomp (&expect_re, expect_re_str, expect_re_cflags); 502 err = regcomp(&expect_re, result.config.expect_re_str, result.config.expect_re_cflags);
408 if (err) { 503 if (err) {
409 char errmsg[1024]; 504 char errmsg[1024];
410 regerror (err, &expect_re, errmsg, sizeof (errmsg)); 505 regerror(err, &expect_re, errmsg, sizeof(errmsg));
411 printf ("ERROR - failed to compile regular expression: %s\n", 506 printf("ERROR - failed to compile regular expression: %s\n", errmsg);
412 errmsg); 507
413 return ERROR; 508 result.errorcode = ERROR;
414 } 509 return result;
415 break;
416 } 510 }
417 511 break;
512 }
418 case 'm': 513 case 'm':
419 if (! strcasecmp (optarg, "CONN_TIME")) 514 if (!strcasecmp(optarg, "CONN_TIME")) {
420 metric = METRIC_CONN_TIME; 515 result.config.metric = METRIC_CONN_TIME;
421 else if (! strcasecmp (optarg, "SERVER_VERSION")) 516 } else if (!strcasecmp(optarg, "SERVER_VERSION")) {
422 metric = METRIC_SERVER_VERSION; 517 result.config.metric = METRIC_SERVER_VERSION;
423 else if (! strcasecmp (optarg, "QUERY_RESULT")) 518 } else if (!strcasecmp(optarg, "QUERY_RESULT")) {
424 metric = METRIC_QUERY_RESULT; 519 result.config.metric = METRIC_QUERY_RESULT;
425 else if (! strcasecmp (optarg, "QUERY_TIME")) 520 } else if (!strcasecmp(optarg, "QUERY_TIME")) {
426 metric = METRIC_QUERY_TIME; 521 result.config.metric = METRIC_QUERY_TIME;
427 else 522 } else {
428 usage2 (_("Invalid metric"), optarg); 523 usage2(_("Invalid metric"), optarg);
524 }
429 break; 525 break;
430 case 't': /* timeout */ 526 case 't': /* timeout */
431 if (!is_intnonneg (optarg)) 527 if (!is_intnonneg(optarg)) {
432 usage2 (_("Timeout interval must be a positive integer"), optarg); 528 usage2(_("Timeout interval must be a positive integer"), optarg);
433 else 529 } else {
434 timeout_interval = atoi (optarg); 530 timeout_interval = atoi(optarg);
435 531 }
436 break; 532 break;
437 case 'H': /* host */ 533 case 'H': /* host */
438 if (!is_host (optarg)) 534 if (!is_host(optarg)) {
439 usage2 (_("Invalid hostname/address"), optarg); 535 usage2(_("Invalid hostname/address"), optarg);
440 else 536 } else {
441 host = optarg; 537 result.config.host = optarg;
538 }
442 break; 539 break;
443 case 'v': 540 case 'v':
444 verbose++; 541 verbose++;
445 break; 542 break;
446
447 case 'd': 543 case 'd':
448 np_dbi_driver = optarg; 544 result.config.dbi_driver = optarg;
449 break; 545 break;
450 case 'o': 546 case 'o': {
451 { 547 driver_option_t *new = NULL;
452 driver_option_t *new;
453 548
454 char *k, *v; 549 char *key = optarg;
550 char *value = strchr(key, '=');
455 551
456 k = optarg; 552 if (!value) {
457 v = strchr (k, (int)'='); 553 usage2(_("Option must be '<key>=<value>'"), optarg);
458 554 }
459 if (! v)
460 usage2 (_("Option must be '<key>=<value>'"), optarg);
461 555
462 *v = '\0'; 556 *value = '\0';
463 ++v; 557 ++value;
464 558
465 new = realloc (np_dbi_options, 559 new = realloc(result.config.dbi_options,
466 (np_dbi_options_num + 1) * sizeof (*new)); 560 (result.config.dbi_options_num + 1) * sizeof(*new));
467 if (! new) { 561 if (!new) {
468 printf ("UNKNOWN - failed to reallocate memory\n"); 562 printf("UNKNOWN - failed to reallocate memory\n");
469 exit (STATE_UNKNOWN); 563 exit(STATE_UNKNOWN);
470 } 564 }
471 565
472 np_dbi_options = new; 566 result.config.dbi_options = new;
473 new = np_dbi_options + np_dbi_options_num; 567 new = result.config.dbi_options + result.config.dbi_options_num;
474 ++np_dbi_options_num; 568 result.config.dbi_options_num++;
475 569
476 new->key = k; 570 new->key = key;
477 new->value = v; 571 new->value = value;
478 } 572 } break;
479 break;
480 case 'q': 573 case 'q':
481 np_dbi_query = optarg; 574 result.config.query = optarg;
482 break; 575 break;
483 case 'D': 576 case 'D':
484 np_dbi_database = optarg; 577 result.config.database = optarg;
485 break; 578 break;
579 case output_format_index: {
580 parsed_output_format parser = mp_parse_output_format(optarg);
581 if (!parser.parsing_success) {
582 // TODO List all available formats here, maybe add anothoer usage function
583 printf("Invalid output format: %s\n", optarg);
584 exit(STATE_UNKNOWN);
585 }
586
587 result.config.output_format_is_set = true;
588 result.config.output_format = parser.output_format;
589 break;
590 }
486 } 591 }
487 } 592 }
488 593
489 set_thresholds (&dbi_thresholds, warning_range, critical_range); 594 if (!result.config.dbi_driver) {
490 595 usage("Must specify a DBI driver");
491 return validate_arguments (); 596 }
492}
493 597
494int 598 if (((result.config.metric == METRIC_QUERY_RESULT) ||
495validate_arguments () 599 (result.config.metric == METRIC_QUERY_TIME)) &&
496{ 600 (!result.config.query)) {
497 if (! np_dbi_driver) 601 usage("Must specify a query to execute (metric == QUERY_RESULT)");
498 usage ("Must specify a DBI driver"); 602 }
499 603
500 if (((metric == METRIC_QUERY_RESULT) || (metric == METRIC_QUERY_TIME)) 604 if ((result.config.metric != METRIC_CONN_TIME) &&
501 && (! np_dbi_query)) 605 (result.config.metric != METRIC_SERVER_VERSION) &&
502 usage ("Must specify a query to execute (metric == QUERY_RESULT)"); 606 (result.config.metric != METRIC_QUERY_RESULT) &&
607 (result.config.metric != METRIC_QUERY_TIME)) {
608 usage("Invalid metric specified");
609 }
503 610
504 if ((metric != METRIC_CONN_TIME) 611 if (result.config.expect &&
505 && (metric != METRIC_SERVER_VERSION) 612 (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set ||
506 && (metric != METRIC_QUERY_RESULT) 613 result.config.expect_re_str)) {
507 && (metric != METRIC_QUERY_TIME)) 614 usage("Do not mix -e and -w/-c/-r/-R");
508 usage ("Invalid metric specified"); 615 }
509 616
510 if (expect && (warning_range || critical_range || expect_re_str)) 617 if (result.config.expect_re_str &&
511 usage ("Do not mix -e and -w/-c/-r/-R"); 618 (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set ||
619 result.config.expect)) {
620 usage("Do not mix -r/-R and -w/-c/-e");
621 }
512 622
513 if (expect_re_str && (warning_range || critical_range || expect)) 623 if (result.config.expect && (result.config.metric != METRIC_QUERY_RESULT)) {
514 usage ("Do not mix -r/-R and -w/-c/-e"); 624 usage("Option -e requires metric QUERY_RESULT");
625 }
515 626
516 if (expect && (metric != METRIC_QUERY_RESULT)) 627 if (result.config.expect_re_str && (result.config.metric != METRIC_QUERY_RESULT)) {
517 usage ("Option -e requires metric QUERY_RESULT"); 628 usage("Options -r/-R require metric QUERY_RESULT");
629 }
518 630
519 if (expect_re_str && (metric != METRIC_QUERY_RESULT)) 631 if (result.config.type == TYPE_STRING) {
520 usage ("Options -r/-R require metric QUERY_RESULT"); 632 assert(result.config.expect || result.config.expect_re_str);
633 }
521 634
522 return OK; 635 return result;
523} 636}
524 637
525void 638void print_help(void) {
526print_help (void) 639 print_revision(progname, NP_VERSION);
527{
528 print_revision (progname, NP_VERSION);
529 640
530 printf (COPYRIGHT, copyright, email); 641 printf(COPYRIGHT, copyright, email);
531 642
532 printf (_("This program connects to an (SQL) database using DBI and checks the\n" 643 printf(_("This program connects to an (SQL) database using DBI and checks the\n"
533 "specified metric against threshold levels. The default metric is\n" 644 "specified metric against threshold levels. The default metric is\n"
534 "the result of the specified query.\n")); 645 "the result of the specified query.\n"));
535 646
536 printf ("\n\n"); 647 printf("\n\n");
537 648
538 print_usage (); 649 print_usage();
539 650
540 printf (UT_HELP_VRSN); 651 printf(UT_HELP_VRSN);
541/* include this conditionally to avoid 'zero-length printf format string' 652/* include this conditionally to avoid 'zero-length printf format string'
542 * compiler warnings */ 653 * compiler warnings */
543#ifdef NP_EXTRA_OPTS 654#ifdef NP_EXTRA_OPTS
544 printf (UT_EXTRA_OPTS); 655 printf(UT_EXTRA_OPTS);
545#endif 656#endif
546 printf ("\n"); 657 printf("\n");
547 658
548 printf (" %s\n", "-d, --driver=STRING"); 659 printf(" %s\n", "-d, --driver=STRING");
549 printf (" %s\n", _("DBI driver to use")); 660 printf(" %s\n", _("DBI driver to use"));
550 printf (" %s\n", "-o, --option=STRING"); 661 printf(" %s\n", "-o, --option=STRING");
551 printf (" %s\n", _("DBI driver options")); 662 printf(" %s\n", _("DBI driver options"));
552 printf (" %s\n", "-q, --query=STRING"); 663 printf(" %s\n", "-q, --query=STRING");
553 printf (" %s\n", _("query to execute")); 664 printf(" %s\n", _("query to execute"));
554 printf ("\n"); 665 printf(" %s\n", "-H STRING");
555 666 printf(" %s\n", _("target database host"));
556 printf (UT_WARN_CRIT_RANGE); 667 printf("\n");
557 printf (" %s\n", "-e, --expect=STRING"); 668
558 printf (" %s\n", _("String to expect as query result")); 669 printf(UT_WARN_CRIT_RANGE);
559 printf (" %s\n", _("Do not mix with -w, -c, -r, or -R!")); 670 printf(" %s\n", "-e, --expect=STRING");
560 printf (" %s\n", "-r, --regex=REGEX"); 671 printf(" %s\n", _("String to expect as query result"));
561 printf (" %s\n", _("Extended POSIX regular expression to check query result against")); 672 printf(" %s\n", _("Do not mix with -w, -c, -r, or -R!"));
562 printf (" %s\n", _("Do not mix with -w, -c, -e, or -R!")); 673 printf(" %s\n", "-r, --regex=REGEX");
563 printf (" %s\n", "-R, --regexi=REGEX"); 674 printf(" %s\n", _("Extended POSIX regular expression to check query result against"));
564 printf (" %s\n", _("Case-insensitive extended POSIX regex to check query result against")); 675 printf(" %s\n", _("Do not mix with -w, -c, -e, or -R!"));
565 printf (" %s\n", _("Do not mix with -w, -c, -e, or -r!")); 676 printf(" %s\n", "-R, --regexi=REGEX");
566 printf (" %s\n", "-m, --metric=METRIC"); 677 printf(" %s\n", _("Case-insensitive extended POSIX regex to check query result against"));
567 printf (" %s\n", _("Metric to check thresholds against. Available metrics:")); 678 printf(" %s\n", _("Do not mix with -w, -c, -e, or -r!"));
568 printf (" CONN_TIME - %s\n", _("time used for setting up the database connection")); 679 printf(" %s\n", "-m, --metric=METRIC");
569 printf (" QUERY_RESULT - %s\n", _("result (first column of first row) of the query")); 680 printf(" %s\n", _("Metric to check thresholds against. Available metrics:"));
570 printf (" QUERY_TIME - %s\n", _("time used to execute the query")); 681 printf(" CONN_TIME - %s\n", _("time used for setting up the database connection"));
571 printf (" %s\n", _("(ignore the query result)")); 682 printf(" QUERY_RESULT - %s\n", _("result (first column of first row) of the query"));
572 printf ("\n"); 683 printf(" QUERY_TIME - %s\n", _("time used to execute the query"));
573 684 printf(" %s\n", _("(ignore the query result)"));
574 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 685 printf("\n");
575 686
576 printf (UT_VERBOSE); 687 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
577 688
578 printf ("\n"); 689 printf(UT_VERBOSE);
579 printf (" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates")); 690
580 printf (" %s\n\n", _("on a query, one has to be specified (-q option).")); 691 printf(UT_OUTPUT_FORMAT);
581 692
582 printf (" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,")); 693 printf("\n");
583 printf (" %s\n", _("executes the specified query. The first column of the first row of the")); 694 printf(" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates"));
584 printf (" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the")); 695 printf(" %s\n\n", _("on a query, one has to be specified (-q option)."));
585 printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric")); 696
586 printf (" %s\n\n", _("(strings representing numbers are fine).")); 697 printf(" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,"));
587 698 printf(" %s\n", _("executes the specified query. The first column of the first row of the"));
588 printf (" %s\n", _("The number and type of required DBI driver options depends on the actual")); 699 printf(" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the"));
589 printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/")); 700 printf(" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
590 printf (" %s\n\n", _("for details.")); 701 printf(" %s\n\n", _("(strings representing numbers are fine)."));
591 702
592 printf (" %s\n", _("Examples:")); 703 printf(" %s\n", _("The number and type of required DBI driver options depends on the actual"));
593 printf (" check_dbi -d pgsql -o username=postgres -m QUERY_RESULT \\\n"); 704 printf(" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
594 printf (" -q 'SELECT COUNT(*) FROM pg_stat_activity' -w 5 -c 10\n"); 705 printf(" %s\n\n", _("for details."));
595 printf (" Warning if more than five connections; critical if more than ten.\n\n"); 706
596 707 printf(" %s\n", _("Examples:"));
597 printf (" check_dbi -d mysql -H localhost -o username=user -o password=secret \\\n"); 708 printf(" check_dbi -d pgsql -o username=postgres -m QUERY_RESULT \\\n");
598 printf (" -q 'SELECT COUNT(*) FROM logged_in_users -w 5:20 -c 0:50\n"); 709 printf(" -q 'SELECT COUNT(*) FROM pg_stat_activity' -w 5 -c 10\n");
599 printf (" Warning if less than 5 or more than 20 users are logged in; critical\n"); 710 printf(" Warning if more than five connections; critical if more than ten.\n\n");
600 printf (" if more than 50 users.\n\n"); 711
601 712 printf(" check_dbi -d mysql -H localhost -o username=user -o password=secret \\\n");
602 printf (" check_dbi -d firebird -o username=user -o password=secret -o dbname=foo \\\n"); 713 printf(" -q 'SELECT COUNT(*) FROM logged_in_users -w 5:20 -c 0:50\n");
603 printf (" -m CONN_TIME -w 0.5 -c 2\n"); 714 printf(" Warning if less than 5 or more than 20 users are logged in; critical\n");
604 printf (" Warning if connecting to the database takes more than half of a second;\n"); 715 printf(" if more than 50 users.\n\n");
605 printf (" critical if it takes more than 2 seconds.\n\n"); 716
606 717 printf(" check_dbi -d firebird -o username=user -o password=secret -o dbname=foo \\\n");
607 printf (" check_dbi -d mysql -H localhost -o username=user \\\n"); 718 printf(" -m CONN_TIME -w 0.5 -c 2\n");
608 printf (" -q 'SELECT concat(@@version, \" \", @@version_comment)' \\\n"); 719 printf(" Warning if connecting to the database takes more than half of a second;\n");
609 printf (" -r '^5\\.[01].*MySQL Enterprise Server'\n"); 720 printf(" critical if it takes more than 2 seconds.\n\n");
610 printf (" Critical if the database server is not a MySQL enterprise server in either\n"); 721
611 printf (" version 5.0.x or 5.1.x.\n\n"); 722 printf(" check_dbi -d mysql -H localhost -o username=user \\\n");
612 723 printf(" -q 'SELECT concat(@@version, \" \", @@version_comment)' \\\n");
613 printf (" check_dbi -d pgsql -u username=user -m SERVER_VERSION \\\n"); 724 printf(" -r '^5\\.[01].*MySQL Enterprise Server'\n");
614 printf (" -w 090000:090099 -c 090000:090199\n"); 725 printf(" Critical if the database server is not a MySQL enterprise server in either\n");
615 printf (" Warn if the PostgreSQL server version is not 9.0.x; critical if the version\n"); 726 printf(" version 5.0.x or 5.1.x.\n\n");
616 printf (" is less than 9.x or higher than 9.1.x.\n"); 727
617 728 printf(" check_dbi -d pgsql -u username=user -m SERVER_VERSION \\\n");
618 printf (UT_SUPPORT); 729 printf(" -w 090000:090099 -c 090000:090199\n");
730 printf(" Warn if the PostgreSQL server version is not 9.0.x; critical if the version\n");
731 printf(" is less than 9.x or higher than 9.1.x.\n");
732
733 printf(UT_SUPPORT);
619} 734}
620 735
621void 736void print_usage(void) {
622print_usage (void) 737 printf("%s\n", _("Usage:"));
623{ 738 printf("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname);
624 printf ("%s\n", _("Usage:")); 739 printf(" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
625 printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname); 740 printf(" [-e <string>] [-r|-R <regex>]\n");
626 printf (" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
627 printf (" [-e <string>] [-r|-R <regex>]\n");
628} 741}
629 742
630#define CHECK_IGNORE_ERROR(s) \ 743const char *get_field_str(dbi_result res, check_dbi_metric metric, check_dbi_type type) {
631 do { \ 744 const char *str = dbi_result_get_string_idx(res, 1);
632 if (metric != METRIC_QUERY_RESULT) \ 745 if ((!str) || (strcmp(str, "ERROR") == 0)) {
633 return (s); \ 746 if (metric != METRIC_QUERY_RESULT) {
634 } while (0) 747 return NULL;
635 748 }
636const char *
637get_field_str (dbi_conn conn, dbi_result res, unsigned short field_type)
638{
639 const char *str;
640
641 if (field_type != DBI_TYPE_STRING) {
642 printf ("CRITICAL - result value is not a string\n");
643 return NULL; 749 return NULL;
644 } 750 }
645 751
646 str = dbi_result_get_string_idx (res, 1); 752 if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) {
647 if ((! str) || (strcmp (str, "ERROR") == 0)) { 753 printf("Query returned string '%s'\n", str);
648 CHECK_IGNORE_ERROR (NULL);
649 np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
650 return NULL;
651 } 754 }
652
653 if ((verbose && (type == TYPE_STRING)) || (verbose > 2))
654 printf ("Query returned string '%s'\n", str);
655 return str; 755 return str;
656} 756}
657 757
658double 758typedef struct {
659get_field (dbi_conn conn, dbi_result res, unsigned short *field_type) 759 double value;
660{ 760 int error_code;
661 double val = NAN; 761 int dbi_error_code; // not sure if useful
762} get_field_wrapper;
763get_field_wrapper get_field(dbi_result res, check_dbi_metric metric, check_dbi_type type) {
764
765 unsigned short field_type = dbi_result_get_field_type_idx(res, 1);
766 get_field_wrapper result = {
767 .value = NAN,
768 .error_code = OK,
769 };
662 770
663 if (*field_type == DBI_TYPE_INTEGER) { 771 if (field_type == DBI_TYPE_INTEGER) {
664 val = (double)dbi_result_get_longlong_idx (res, 1); 772 result.value = (double)dbi_result_get_longlong_idx(res, 1);
665 } 773 } else if (field_type == DBI_TYPE_DECIMAL) {
666 else if (*field_type == DBI_TYPE_DECIMAL) { 774 result.value = dbi_result_get_double_idx(res, 1);
667 val = dbi_result_get_double_idx (res, 1); 775 } else if (field_type == DBI_TYPE_STRING) {
668 }
669 else if (*field_type == DBI_TYPE_STRING) {
670 const char *val_str; 776 const char *val_str;
671 char *endptr = NULL; 777 char *endptr = NULL;
672 778
673 val_str = get_field_str (conn, res, *field_type); 779 val_str = get_field_str(res, metric, type);
674 if (! val_str) { 780 if (!val_str) {
675 CHECK_IGNORE_ERROR (NAN); 781 result.error_code = ERROR;
676 *field_type = DBI_TYPE_ERROR; 782 field_type = DBI_TYPE_ERROR;
677 return NAN; 783 return result;
678 } 784 }
679 785
680 val = strtod (val_str, &endptr); 786 result.value = strtod(val_str, &endptr);
681 if (endptr == val_str) { 787 if (endptr == val_str) {
682 CHECK_IGNORE_ERROR (NAN); 788 if (metric != METRIC_QUERY_RESULT) {
683 printf ("CRITICAL - result value is not a numeric: %s\n", val_str); 789 result.error_code = ERROR;
684 *field_type = DBI_TYPE_ERROR; 790 return result;
685 return NAN; 791 }
792
793 if (verbose) {
794 printf("CRITICAL - result value is not a numeric: %s\n", val_str);
795 }
796
797 field_type = DBI_TYPE_ERROR;
798 result.error_code = ERROR;
799 return result;
800 }
801
802 if ((endptr != NULL) && (*endptr != '\0')) {
803 if (verbose) {
804 printf("Garbage after value: %s\n", endptr);
805 }
686 } 806 }
687 else if ((endptr != NULL) && (*endptr != '\0')) { 807 } else {
688 if (verbose) 808 if (metric != METRIC_QUERY_RESULT) {
689 printf ("Garbage after value: %s\n", endptr); 809 result.error_code = ERROR;
810 return result;
690 } 811 }
812 // printf("CRITICAL - cannot parse value of type %s (%i)\n",
813 // (*field_type == DBI_TYPE_BINARY) ? "BINARY"
814 // : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME"
815 // : "<unknown>",
816 // *field_type);
817 field_type = DBI_TYPE_ERROR;
818 result.error_code = ERROR;
691 } 819 }
692 else { 820 return result;
693 CHECK_IGNORE_ERROR (NAN);
694 printf ("CRITICAL - cannot parse value of type %s (%i)\n",
695 (*field_type == DBI_TYPE_BINARY)
696 ? "BINARY"
697 : (*field_type == DBI_TYPE_DATETIME)
698 ? "DATETIME"
699 : "<unknown>",
700 *field_type);
701 *field_type = DBI_TYPE_ERROR;
702 return NAN;
703 }
704 return val;
705} 821}
706 822
707double 823static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type,
708get_query_result (dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val) 824 char *query) {
709{ 825 assert(query);
710 unsigned short field_type;
711 double val = NAN;
712
713 if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
714 CHECK_IGNORE_ERROR (STATE_OK);
715 np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
716 return STATE_CRITICAL;
717 }
718
719 if (dbi_result_get_numrows (res) < 1) {
720 CHECK_IGNORE_ERROR (STATE_OK);
721 printf ("WARNING - no rows returned\n");
722 return STATE_WARNING;
723 }
724 826
725 if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) { 827 if (verbose) {
726 CHECK_IGNORE_ERROR (STATE_OK); 828 printf("Executing query '%s'\n", query);
727 np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
728 return STATE_CRITICAL;
729 } 829 }
730 830
731 if (dbi_result_get_numfields (res) < 1) { 831 do_query_result result = {
732 CHECK_IGNORE_ERROR (STATE_OK); 832 .query_duration = 0,
733 printf ("WARNING - no fields returned\n"); 833 .result_string = NULL,
734 return STATE_WARNING; 834 .result_number = 0,
735 } 835 .error_code = 0,
836 .query_processing_status = STATE_UNKNOWN,
837 };
736 838
737 if (dbi_result_first_row (res) != 1) { 839 struct timeval timeval_start;
738 CHECK_IGNORE_ERROR (STATE_OK); 840 gettimeofday(&timeval_start, NULL);
739 np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
740 return STATE_CRITICAL;
741 }
742 841
743 field_type = dbi_result_get_field_type_idx (res, 1); 842 dbi_result res = dbi_conn_query(conn, query);
744 if (field_type != DBI_TYPE_ERROR) { 843 if (!res) {
745 if (type == TYPE_STRING) 844 dbi_conn_error(conn, &result.error_string);
746 /* the value will be freed in dbi_result_free */ 845 result.error_code = 1;
747 *res_val_str = strdup (get_field_str (conn, res, field_type)); 846 return result;
748 else
749 val = get_field (conn, res, &field_type);
750 } 847 }
751 848
752 *res_val = val; 849 struct timeval timeval_end;
850 gettimeofday(&timeval_end, NULL);
851 result.query_duration = timediff(timeval_start, timeval_end);
753 852
754 if (field_type == DBI_TYPE_ERROR) { 853 if (verbose) {
755 CHECK_IGNORE_ERROR (STATE_OK); 854 printf("Query duration: %f\n", result.query_duration);
756 np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
757 return STATE_CRITICAL;
758 } 855 }
759 856
760 dbi_result_free (res); 857 // Default state is OK, all error will be set explicitly
761 return STATE_OK; 858 mp_state_enum query_processing_state = STATE_OK;
762} 859 {
763
764#undef CHECK_IGNORE_ERROR
765
766int
767do_query (dbi_conn conn, const char **res_val_str, double *res_val, double *res_time)
768{
769 dbi_result res;
770 860
771 struct timeval timeval_start, timeval_end; 861 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
772 int status = STATE_OK; 862 if (metric != METRIC_QUERY_RESULT) {
773 863 query_processing_state = STATE_OK;
774 assert (np_dbi_query); 864 } else {
775 865 dbi_conn_error(conn, &result.error_string);
776 if (verbose) 866 query_processing_state = STATE_CRITICAL;
777 printf ("Executing query '%s'\n", np_dbi_query); 867 }
778 868 } else if (dbi_result_get_numrows(res) < 1) {
779 gettimeofday (&timeval_start, NULL); 869 if (metric != METRIC_QUERY_RESULT) {
780 870 query_processing_state = STATE_OK;
781 res = dbi_conn_query (conn, np_dbi_query); 871 } else {
782 if (! res) { 872 result.error_string = "no rows returned";
783 np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); 873 // printf("WARNING - no rows returned\n");
784 return STATE_CRITICAL; 874 query_processing_state = STATE_WARNING;
875 }
876 } else if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) {
877 if (metric != METRIC_QUERY_RESULT) {
878 query_processing_state = STATE_OK;
879 } else {
880 dbi_conn_error(conn, &result.error_string);
881 // np_dbi_print_error(conn, "CRITICAL - failed to fetch fields");
882 query_processing_state = STATE_CRITICAL;
883 }
884 } else if (dbi_result_get_numfields(res) < 1) {
885 if (metric != METRIC_QUERY_RESULT) {
886 query_processing_state = STATE_OK;
887 } else {
888 result.error_string = "no fields returned";
889 // printf("WARNING - no fields returned\n");
890 query_processing_state = STATE_WARNING;
891 }
892 } else if (dbi_result_first_row(res) != 1) {
893 if (metric != METRIC_QUERY_RESULT) {
894 query_processing_state = STATE_OK;
895 } else {
896 dbi_conn_error(conn, &result.error_string);
897 // np_dbi_print_error(conn, "CRITICAL - failed to fetch first row");
898 query_processing_state = STATE_CRITICAL;
899 }
900 } else {
901 unsigned short field_type = dbi_result_get_field_type_idx(res, 1);
902 if (field_type != DBI_TYPE_ERROR) {
903 if (type == TYPE_STRING) {
904 result.result_string = strdup(get_field_str(res, metric, type));
905 } else {
906 get_field_wrapper gfw = get_field(res, metric, type);
907 result.result_number = gfw.value;
908 }
909 } else {
910 // Error when retrieving the field, that is OK if the Query result is not of
911 // interest
912 if (metric != METRIC_QUERY_RESULT) {
913 query_processing_state = STATE_OK;
914 } else {
915 dbi_conn_error(conn, &result.error_string);
916 // np_dbi_print_error(conn, "CRITICAL - failed to fetch data");
917 query_processing_state = STATE_CRITICAL;
918 }
919 }
920 }
785 } 921 }
922 dbi_result_free(res);
786 923
787 status = get_query_result (conn, res, res_val_str, res_val); 924 result.query_processing_status = query_processing_state;
788
789 gettimeofday (&timeval_end, NULL);
790 *res_time = timediff (timeval_start, timeval_end);
791 925
792 if (verbose) 926 return result;
793 printf ("Time elapsed: %f\n", *res_time);
794
795 return status;
796} 927}
797 928
798double 929static double timediff(struct timeval start, struct timeval end) {
799timediff (struct timeval start, struct timeval end)
800{
801 double diff; 930 double diff;
802 931
803 while (start.tv_usec > end.tv_usec) { 932 while (start.tv_usec > end.tv_usec) {
804 --end.tv_sec; 933 --end.tv_sec;
805 end.tv_usec += 1000000; 934 end.tv_usec += 1000000;
806 } 935 }
807 diff = (double)(end.tv_sec - start.tv_sec) 936 diff = (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
808 + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
809 return diff; 937 return diff;
810} 938}
811 939
812void 940static void np_dbi_print_error(dbi_conn conn, char *fmt, ...) {
813np_dbi_print_error (dbi_conn conn, char *fmt, ...)
814{
815 const char *errmsg = NULL; 941 const char *errmsg = NULL;
816 va_list ap; 942 va_list ap;
817 943
818 va_start (ap, fmt); 944 va_start(ap, fmt);
819 945
820 dbi_conn_error (conn, &errmsg); 946 dbi_conn_error(conn, &errmsg);
821 vprintf (fmt, ap); 947 vprintf(fmt, ap);
822 printf (": %s\n", errmsg); 948 printf(": %s\n", errmsg);
823 949
824 va_end (ap); 950 va_end(ap);
825} 951}
826
diff --git a/plugins/check_dbi.d/config.h b/plugins/check_dbi.d/config.h
new file mode 100644
index 00000000..25d74a12
--- /dev/null
+++ b/plugins/check_dbi.d/config.h
@@ -0,0 +1,66 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include "../../lib/monitoringplug.h"
6#include "thresholds.h"
7
8typedef enum {
9 METRIC_CONN_TIME,
10 METRIC_SERVER_VERSION,
11 METRIC_QUERY_RESULT,
12 METRIC_QUERY_TIME,
13} check_dbi_metric;
14
15typedef enum {
16 TYPE_NUMERIC,
17 TYPE_STRING,
18} check_dbi_type;
19
20typedef struct {
21 char *key;
22 char *value;
23} driver_option_t;
24
25typedef struct {
26 char *dbi_driver;
27 char *host;
28
29 driver_option_t *dbi_options;
30 size_t dbi_options_num;
31
32 char *database;
33 char *query;
34
35 char *expect;
36 char *expect_re_str;
37 int expect_re_cflags;
38 check_dbi_metric metric;
39 check_dbi_type type;
40 mp_thresholds thresholds;
41
42 bool output_format_is_set;
43 mp_output_format output_format;
44} check_dbi_config;
45
46check_dbi_config check_dbi_config_init() {
47 check_dbi_config tmp = {
48 .dbi_driver = NULL,
49 .host = NULL,
50 .dbi_options = NULL,
51 .dbi_options_num = 0,
52 .database = NULL,
53 .query = NULL,
54
55 .expect = NULL,
56 .expect_re_str = NULL,
57 .expect_re_cflags = 0,
58 .metric = METRIC_QUERY_RESULT,
59 .type = TYPE_NUMERIC,
60
61 .thresholds = mp_thresholds_init(),
62
63 .output_format_is_set = false,
64 };
65 return tmp;
66}
diff --git a/plugins/check_dig.c b/plugins/check_dig.c
index be7a6101..9ec8028a 100644
--- a/plugins/check_dig.c
+++ b/plugins/check_dig.c
@@ -1,30 +1,30 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dig plugin 3 * Monitoring check_dig plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2002-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2002-2025 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_dig plugin 10 * This file contains the check_dig plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 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 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 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 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/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29/* Hackers note: 29/* Hackers note:
30 * There are typecasts to (char *) from _("foo bar") in this file. 30 * There are typecasts to (char *) from _("foo bar") in this file.
@@ -33,348 +33,591 @@
33 * because on some architectures those strings are in non-writable memory */ 33 * because on some architectures those strings are in non-writable memory */
34 34
35const char *progname = "check_dig"; 35const char *progname = "check_dig";
36const char *copyright = "2002-2008"; 36const char *copyright = "2002-2025";
37const char *email = "devel@monitoring-plugins.org"; 37const char *email = "devel@monitoring-plugins.org";
38 38
39#include <ctype.h>
39#include "common.h" 40#include "common.h"
40#include "netutils.h" 41#include "netutils.h"
41#include "utils.h" 42#include "utils.h"
42#include "runcmd.h" 43#include "runcmd.h"
43 44
44int process_arguments (int, char **); 45#include "check_dig.d/config.h"
45int validate_arguments (void); 46#include "states.h"
46void print_help (void); 47
47void print_usage (void); 48typedef struct {
48 49 int errorcode;
49#define UNDEFINED 0 50 check_dig_config config;
50#define DEFAULT_PORT 53 51} check_dig_config_wrapper;
51#define DEFAULT_TRIES 2 52static check_dig_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
52 53static check_dig_config_wrapper validate_arguments(check_dig_config_wrapper /*config_wrapper*/);
53char *query_address = NULL; 54
54char *record_type = "A"; 55static void print_help(void);
55char *expected_address = NULL; 56void print_usage(void);
56char *dns_server = NULL; 57
57char *dig_args = ""; 58static int verbose = 0;
58char *query_transport = ""; 59
59bool verbose = false; 60/* helpers for flag parsing */
60int server_port = DEFAULT_PORT; 61static flag_list parse_flags_line(const char *line);
61int number_tries = DEFAULT_TRIES; 62static flag_list split_csv_trim(const char *csv);
62double warning_interval = UNDEFINED; 63static bool flag_list_contains(const flag_list *list, const char *needle);
63double critical_interval = UNDEFINED; 64static void free_flag_list(flag_list *list);
64struct timeval tv; 65
65 66int main(int argc, char **argv) {
66int 67 setlocale(LC_ALL, "");
67main (int argc, char **argv) 68 bindtextdomain(PACKAGE, LOCALEDIR);
68{ 69 textdomain(PACKAGE);
69 char *command_line; 70
70 output chld_out, chld_err; 71 /* Set signal handling and alarm */
71 char *msg = NULL; 72 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
72 size_t i; 73 usage_va(_("Cannot catch SIGALRM"));
73 char *t; 74 }
74 long microsec; 75
75 double elapsed_time; 76 /* Parse extra opts if any */
76 int result = STATE_UNKNOWN; 77 argv = np_extra_opts(&argc, argv, progname);
77 int timeout_interval_dig; 78
78 79 check_dig_config_wrapper tmp_config = process_arguments(argc, argv);
79 setlocale (LC_ALL, ""); 80 if (tmp_config.errorcode == ERROR) {
80 bindtextdomain (PACKAGE, LOCALEDIR); 81 usage_va(_("Could not parse arguments"));
81 textdomain (PACKAGE); 82 }
82 83
83 /* Set signal handling and alarm */ 84 const check_dig_config config = tmp_config.config;
84 if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) 85
85 usage_va(_("Cannot catch SIGALRM")); 86 /* dig applies the timeout to each try, so we need to work around this */
86 87 int timeout_interval_dig = ((int)timeout_interval / config.number_tries) + config.number_tries;
87 /* Parse extra opts if any */ 88
88 argv=np_extra_opts (&argc, argv, progname); 89 char *command_line;
89 90 /* get the command to run */
90 if (process_arguments (argc, argv) == ERROR) 91 xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG,
91 usage_va(_("Could not parse arguments")); 92 config.dig_args, config.query_transport, config.server_port, config.dns_server,
92 93 config.query_address, config.record_type, config.number_tries, timeout_interval_dig);
93 /* dig applies the timeout to each try, so we need to work around this */ 94
94 timeout_interval_dig = timeout_interval / number_tries + number_tries; 95 alarm(timeout_interval);
95 96 struct timeval start_time;
96 /* get the command to run */ 97 gettimeofday(&start_time, NULL);
97 xasprintf (&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", 98
98 PATH_TO_DIG, dig_args, query_transport, server_port, dns_server, query_address, record_type, number_tries, timeout_interval_dig); 99 if (verbose) {
99 100 printf("%s\n", command_line);
100 alarm (timeout_interval); 101 if (config.expected_address != NULL) {
101 gettimeofday (&tv, NULL); 102 printf(_("Looking for: '%s'\n"), config.expected_address);
102 103 } else {
103 if (verbose) { 104 printf(_("Looking for: '%s'\n"), config.query_address);
104 printf ("%s\n", command_line); 105 }
105 if(expected_address != NULL) { 106 }
106 printf (_("Looking for: '%s'\n"), expected_address); 107
107 } else { 108 output chld_out;
108 printf (_("Looking for: '%s'\n"), query_address); 109 output chld_err;
109 } 110 char *msg = NULL;
110 } 111 flag_list dig_flags = {.items = NULL, .count = 0};
111 112 mp_state_enum result = STATE_UNKNOWN;
112 /* run the command */ 113
113 if(np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { 114 /* run the command */
114 result = STATE_WARNING; 115 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) {
115 msg = (char *)_("dig returned an error status"); 116 result = STATE_WARNING;
116 } 117 msg = (char *)_("dig returned an error status");
117 118 }
118 for(i = 0; i < chld_out.lines; i++) { 119
119 /* the server is responding, we just got the host name... */ 120 /* extract ';; flags: ...' from stdout (first occurrence) */
120 if (strstr (chld_out.line[i], ";; ANSWER SECTION:")) { 121 for (size_t i = 0; i < chld_out.lines; i++) {
121 122 if (strstr(chld_out.line[i], "flags:")) {
122 /* loop through the whole 'ANSWER SECTION' */ 123 if (verbose) {
123 for(; i < chld_out.lines; i++) { 124 printf("Raw flags line: %s\n", chld_out.line[i]);
124 /* get the host address */ 125 }
125 if (verbose) 126
126 printf ("%s\n", chld_out.line[i]); 127 dig_flags = parse_flags_line(chld_out.line[i]);
127 128
128 if (strcasestr (chld_out.line[i], (expected_address == NULL ? query_address : expected_address)) != NULL) { 129 if (verbose && dig_flags.count > 0) {
129 msg = chld_out.line[i]; 130 printf(_("Parsed flags:"));
130 result = STATE_OK; 131 for (size_t k = 0; k < dig_flags.count; k++) {
131 132 printf(" %s", dig_flags.items[k]);
132 /* Translate output TAB -> SPACE */ 133 }
133 t = msg; 134 printf("\n");
134 while ((t = strchr(t, '\t')) != NULL) *t = ' '; 135 }
135 break; 136 break;
136 } 137 }
137 } 138 }
138 139
139 if (result == STATE_UNKNOWN) { 140 for (size_t i = 0; i < chld_out.lines; i++) {
140 msg = (char *)_("Server not found in ANSWER SECTION"); 141 /* the server is responding, we just got the host name... */
141 result = STATE_WARNING; 142 if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) {
142 } 143
143 144 /* loop through the whole 'ANSWER SECTION' */
144 /* we found the answer section, so break out of the loop */ 145 for (; i < chld_out.lines; i++) {
145 break; 146 /* get the host address */
146 } 147 if (verbose) {
147 } 148 printf("%s\n", chld_out.line[i]);
148 149 }
149 if (result == STATE_UNKNOWN) { 150
150 msg = (char *)_("No ANSWER SECTION found"); 151 if (strcasestr(chld_out.line[i], (config.expected_address == NULL
151 result = STATE_CRITICAL; 152 ? config.query_address
152 } 153 : config.expected_address)) != NULL) {
153 154 msg = chld_out.line[i];
154 /* If we get anything on STDERR, at least set warning */ 155 result = STATE_OK;
155 if(chld_err.buflen > 0) { 156
156 result = max_state(result, STATE_WARNING); 157 /* Translate output TAB -> SPACE */
157 if(!msg) for(i = 0; i < chld_err.lines; i++) { 158 char *temp = msg;
158 msg = strchr(chld_err.line[0], ':'); 159 while ((temp = strchr(temp, '\t')) != NULL) {
159 if(msg) { 160 *temp = ' ';
160 msg++; 161 }
161 break; 162 break;
162 } 163 }
163 } 164 }
164 } 165
165 166 if (result == STATE_UNKNOWN) {
166 microsec = deltime (tv); 167 msg = (char *)_("Server not found in ANSWER SECTION");
167 elapsed_time = (double)microsec / 1.0e6; 168 result = STATE_WARNING;
168 169 }
169 if (critical_interval > UNDEFINED && elapsed_time > critical_interval) 170
170 result = STATE_CRITICAL; 171 /* we found the answer section, so break out of the loop */
171 172 break;
172 else if (warning_interval > UNDEFINED && elapsed_time > warning_interval) 173 }
173 result = STATE_WARNING; 174 }
174 175
175 printf ("DNS %s - %.3f seconds response time (%s)|%s\n", 176 if (result == STATE_UNKNOWN) {
176 state_text (result), elapsed_time, 177 msg = (char *)_("No ANSWER SECTION found");
177 msg ? msg : _("Probably a non-existent host/domain"), 178 result = STATE_CRITICAL;
178 fperfdata("time", elapsed_time, "s", 179 }
179 (warning_interval>UNDEFINED ? true:false), 180
180 warning_interval, 181 /* If we get anything on STDERR, at least set warning */
181 (critical_interval>UNDEFINED ? true:false), 182 if (chld_err.buflen > 0) {
182 critical_interval, 183 result = max_state(result, STATE_WARNING);
183 true, 0, false, 0)); 184 if (!msg) {
184 return result; 185 for (size_t i = 0; i < chld_err.lines; i++) {
186 msg = strchr(chld_err.line[0], ':');
187 if (msg) {
188 msg++;
189 break;
190 }
191 }
192 }
193 }
194
195 long microsec = deltime(start_time);
196 double elapsed_time = (double)microsec / 1.0e6;
197
198 if (config.critical_interval > UNDEFINED && elapsed_time > config.critical_interval) {
199 result = STATE_CRITICAL;
200 }
201
202 else if (config.warning_interval > UNDEFINED && elapsed_time > config.warning_interval) {
203 result = STATE_WARNING;
204 }
205
206 /* Optional: evaluate dig flags only if -E/-X were provided */
207 if ((config.require_flags.count > 0) || (config.forbid_flags.count > 0)) {
208 if (dig_flags.count > 0) {
209 for (size_t r = 0; r < config.require_flags.count; r++) {
210 if (!flag_list_contains(&dig_flags, config.require_flags.items[r])) {
211 result = STATE_CRITICAL;
212 if (!msg) {
213 xasprintf(&msg, _("Missing required DNS flag: %s"),
214 config.require_flags.items[r]);
215 } else {
216 char *newmsg = NULL;
217 xasprintf(&newmsg, _("%s; missing required DNS flag: %s"), msg,
218 config.require_flags.items[r]);
219 msg = newmsg;
220 }
221 }
222 }
223
224 for (size_t r = 0; r < config.forbid_flags.count; r++) {
225 if (flag_list_contains(&dig_flags, config.forbid_flags.items[r])) {
226 result = STATE_CRITICAL;
227 if (!msg) {
228 xasprintf(&msg, _("Forbidden DNS flag present: %s"),
229 config.forbid_flags.items[r]);
230 } else {
231 char *newmsg = NULL;
232 xasprintf(&newmsg, _("%s; forbidden DNS flag present: %s"), msg,
233 config.forbid_flags.items[r]);
234 msg = newmsg;
235 }
236 }
237 }
238 }
239 }
240
241 /* cleanup flags buffer */
242 free_flag_list(&dig_flags);
243
244 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time,
245 msg ? msg : _("Probably a non-existent host/domain"),
246 fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED),
247 config.warning_interval, (config.critical_interval > UNDEFINED),
248 config.critical_interval, true, 0, false, 0));
249 exit(result);
185} 250}
186 251
187
188
189/* process command-line arguments */ 252/* process command-line arguments */
190int 253check_dig_config_wrapper process_arguments(int argc, char **argv) {
191process_arguments (int argc, char **argv) 254 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
192{ 255 {"query_address", required_argument, 0, 'l'},
193 int c; 256 {"warning", required_argument, 0, 'w'},
194 257 {"critical", required_argument, 0, 'c'},
195 int option = 0; 258 {"timeout", required_argument, 0, 't'},
196 static struct option longopts[] = { 259 {"dig-arguments", required_argument, 0, 'A'},
197 {"hostname", required_argument, 0, 'H'}, 260 {"require-flags", required_argument, 0, 'E'},
198 {"query_address", required_argument, 0, 'l'}, 261 {"forbid-flags", required_argument, 0, 'X'},
199 {"warning", required_argument, 0, 'w'}, 262 {"verbose", no_argument, 0, 'v'},
200 {"critical", required_argument, 0, 'c'}, 263 {"version", no_argument, 0, 'V'},
201 {"timeout", required_argument, 0, 't'}, 264 {"help", no_argument, 0, 'h'},
202 {"dig-arguments", required_argument, 0, 'A'}, 265 {"record_type", required_argument, 0, 'T'},
203 {"verbose", no_argument, 0, 'v'}, 266 {"expected_address", required_argument, 0, 'a'},
204 {"version", no_argument, 0, 'V'}, 267 {"port", required_argument, 0, 'p'},
205 {"help", no_argument, 0, 'h'}, 268 {"use-ipv4", no_argument, 0, '4'},
206 {"record_type", required_argument, 0, 'T'}, 269 {"use-ipv6", no_argument, 0, '6'},
207 {"expected_address", required_argument, 0, 'a'}, 270 {0, 0, 0, 0}};
208 {"port", required_argument, 0, 'p'}, 271
209 {"use-ipv4", no_argument, 0, '4'}, 272 check_dig_config_wrapper result = {
210 {"use-ipv6", no_argument, 0, '6'}, 273 .errorcode = OK,
211 {0, 0, 0, 0} 274 .config = check_dig_config_init(),
212 }; 275 };
213 276
214 if (argc < 2) 277 if (argc < 2) {
215 return ERROR; 278 result.errorcode = ERROR;
216 279 return result;
217 while (1) { 280 }
218 c = getopt_long (argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option); 281
219 282 int option = 0;
220 if (c == -1 || c == EOF) 283 while (true) {
221 break; 284 int option_index =
222 285 getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:E:X:46", longopts, &option);
223 switch (c) { 286
224 case 'h': /* help */ 287 if (CHECK_EOF(option_index)) {
225 print_help (); 288 break;
226 exit (STATE_UNKNOWN); 289 }
227 case 'V': /* version */ 290
228 print_revision (progname, NP_VERSION); 291 switch (option_index) {
229 exit (STATE_UNKNOWN); 292 case 'h': /* help */
230 case 'H': /* hostname */ 293 print_help();
231 host_or_die(optarg); 294 exit(STATE_UNKNOWN);
232 dns_server = optarg; 295 case 'V': /* version */
233 break; 296 print_revision(progname, NP_VERSION);
234 case 'p': /* server port */ 297 exit(STATE_UNKNOWN);
235 if (is_intpos (optarg)) { 298 case 'H': /* hostname */
236 server_port = atoi (optarg); 299 host_or_die(optarg);
237 } 300 result.config.dns_server = optarg;
238 else { 301 break;
239 usage_va(_("Port must be a positive integer - %s"), optarg); 302 case 'p': /* server port */
240 } 303 if (is_intpos(optarg)) {
241 break; 304 result.config.server_port = atoi(optarg);
242 case 'l': /* address to lookup */ 305 } else {
243 query_address = optarg; 306 usage_va(_("Port must be a positive integer - %s"), optarg);
244 break; 307 }
245 case 'w': /* warning */ 308 break;
246 if (is_nonnegative (optarg)) { 309 case 'l': /* address to lookup */
247 warning_interval = strtod (optarg, NULL); 310 result.config.query_address = optarg;
248 } 311 break;
249 else { 312 case 'w': /* warning */
250 usage_va(_("Warning interval must be a positive integer - %s"), optarg); 313 if (is_nonnegative(optarg)) {
251 } 314 result.config.warning_interval = strtod(optarg, NULL);
252 break; 315 } else {
253 case 'c': /* critical */ 316 usage_va(_("Warning interval must be a positive integer - %s"), optarg);
254 if (is_nonnegative (optarg)) { 317 }
255 critical_interval = strtod (optarg, NULL); 318 break;
256 } 319 case 'c': /* critical */
257 else { 320 if (is_nonnegative(optarg)) {
258 usage_va(_("Critical interval must be a positive integer - %s"), optarg); 321 result.config.critical_interval = strtod(optarg, NULL);
259 } 322 } else {
260 break; 323 usage_va(_("Critical interval must be a positive integer - %s"), optarg);
261 case 't': /* timeout */ 324 }
262 if (is_intnonneg (optarg)) { 325 break;
263 timeout_interval = atoi (optarg); 326 case 't': /* timeout */
264 } 327 if (is_intnonneg(optarg)) {
265 else { 328 timeout_interval = atoi(optarg);
266 usage_va(_("Timeout interval must be a positive integer - %s"), optarg); 329 } else {
267 } 330 usage_va(_("Timeout interval must be a positive integer - %s"), optarg);
268 break; 331 }
269 case 'A': /* dig arguments */ 332 break;
270 dig_args = strdup(optarg); 333 case 'A': /* dig arguments */
271 break; 334 result.config.dig_args = strdup(optarg);
272 case 'v': /* verbose */ 335 break;
273 verbose = true; 336 case 'E': /* require flags */
274 break; 337 result.config.require_flags = split_csv_trim(optarg);
275 case 'T': 338 break;
276 record_type = optarg; 339 case 'X': /* forbid flags */
277 break; 340 result.config.forbid_flags = split_csv_trim(optarg);
278 case 'a': 341 break;
279 expected_address = optarg; 342 case 'v': /* verbose */
280 break; 343 verbose++;
281 case '4': 344 break;
282 query_transport = "-4"; 345 case 'T':
283 break; 346 result.config.record_type = optarg;
284 case '6': 347 break;
285 query_transport = "-6"; 348 case 'a':
286 break; 349 result.config.expected_address = optarg;
287 default: /* usage5 */ 350 break;
288 usage5(); 351 case '4':
289 } 352 result.config.query_transport = "-4";
290 } 353 break;
291 354 case '6':
292 c = optind; 355 result.config.query_transport = "-6";
293 if (dns_server == NULL) { 356 break;
294 if (c < argc) { 357 default: /* usage5 */
295 host_or_die(argv[c]); 358 usage5();
296 dns_server = argv[c]; 359 }
297 } 360 }
298 else { 361
299 if (strcmp(query_transport,"-6") == 0) 362 int index = optind;
300 dns_server = strdup("::1"); 363 if (result.config.dns_server == NULL) {
301 else 364 if (index < argc) {
302 dns_server = strdup ("127.0.0.1"); 365 host_or_die(argv[index]);
303 } 366 result.config.dns_server = argv[index];
304 } 367 } else {
305 368 if (strcmp(result.config.query_transport, "-6") == 0) {
306 return validate_arguments (); 369 result.config.dns_server = strdup("::1");
370 } else {
371 result.config.dns_server = strdup("127.0.0.1");
372 }
373 }
374 }
375
376 return validate_arguments(result);
307} 377}
308 378
309 379check_dig_config_wrapper validate_arguments(check_dig_config_wrapper config_wrapper) {
310 380 if (config_wrapper.config.query_address == NULL) {
311int 381 config_wrapper.errorcode = ERROR;
312validate_arguments (void) 382 }
313{ 383 return config_wrapper;
314 if (query_address != NULL)
315 return OK;
316 else
317 return ERROR;
318} 384}
319 385
386void print_help(void) {
387 char *myport;
320 388
389 xasprintf(&myport, "%d", DEFAULT_PORT);
321 390
322void 391 print_revision(progname, NP_VERSION);
323print_help (void)
324{
325 char *myport;
326 392
327 xasprintf (&myport, "%d", DEFAULT_PORT); 393 printf("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
394 printf(COPYRIGHT, copyright, email);
328 395
329 print_revision (progname, NP_VERSION); 396 printf(_("This plugin tests the DNS service on the specified host using dig"));
330 397
331 printf ("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"); 398 printf("\n\n");
332 printf (COPYRIGHT, copyright, email);
333 399
334 printf (_("This plugin tests the DNS service on the specified host using dig")); 400 print_usage();
335 401
336 printf ("\n\n"); 402 printf(UT_HELP_VRSN);
337 403
338 print_usage (); 404 printf(UT_EXTRA_OPTS);
339 405
340 printf (UT_HELP_VRSN); 406 printf(UT_HOST_PORT, 'p', myport);
341 407
342 printf (UT_EXTRA_OPTS); 408 printf(" %s\n", "-4, --use-ipv4");
409 printf(" %s\n", _("Force dig to only use IPv4 query transport"));
410 printf(" %s\n", "-6, --use-ipv6");
411 printf(" %s\n", _("Force dig to only use IPv6 query transport"));
412 printf(" %s\n", "-l, --query_address=STRING");
413 printf(" %s\n", _("Machine name to lookup"));
414 printf(" %s\n", "-T, --record_type=STRING");
415 printf(" %s\n", _("Record type to lookup (default: A)"));
416 printf(" %s\n", "-a, --expected_address=STRING");
417 printf(" %s\n",
418 _("An address expected to be in the answer section. If not set, uses whatever"));
419 printf(" %s\n", _("was in -l"));
420 printf(" %s\n", "-A, --dig-arguments=STRING");
421 printf(" %s\n", _("Pass STRING as argument(s) to dig"));
422 printf(" %s\n", "-E, --require-flags=LIST");
423 printf(" %s\n", _("Comma-separated dig flags that must be present (e.g. 'aa,qr')"));
424 printf(" %s\n", "-X, --forbid-flags=LIST");
425 printf(" %s\n", _("Comma-separated dig flags that must NOT be present"));
426 printf(UT_WARN_CRIT);
427 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
428 printf(UT_VERBOSE);
343 429
344 printf (UT_HOST_PORT, 'p', myport); 430 printf("\n");
431 printf("%s\n", _("Examples:"));
432 printf(" %s\n", "check_dig -H DNSSERVER -l www.example.com -A \"+tcp\"");
433 printf(" %s\n", "This will send a tcp query to DNSSERVER for www.example.com");
345 434
346 printf (" %s\n","-4, --use-ipv4"); 435 printf(UT_SUPPORT);
347 printf (" %s\n",_("Force dig to only use IPv4 query transport")); 436}
348 printf (" %s\n","-6, --use-ipv6");
349 printf (" %s\n",_("Force dig to only use IPv6 query transport"));
350 printf (" %s\n","-l, --query_address=STRING");
351 printf (" %s\n",_("Machine name to lookup"));
352 printf (" %s\n","-T, --record_type=STRING");
353 printf (" %s\n",_("Record type to lookup (default: A)"));
354 printf (" %s\n","-a, --expected_address=STRING");
355 printf (" %s\n",_("An address expected to be in the answer section. If not set, uses whatever"));
356 printf (" %s\n",_("was in -l"));
357 printf (" %s\n","-A, --dig-arguments=STRING");
358 printf (" %s\n",_("Pass STRING as argument(s) to dig"));
359 printf (UT_WARN_CRIT);
360 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
361 printf (UT_VERBOSE);
362 437
363 printf ("\n"); 438void print_usage(void) {
364 printf ("%s\n", _("Examples:")); 439 printf("%s\n", _("Usage:"));
365 printf (" %s\n", "check_dig -H DNSSERVER -l www.example.com -A \"+tcp\""); 440 printf("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname);
366 printf (" %s\n", "This will send a tcp query to DNSSERVER for www.example.com"); 441 printf(" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n");
442 printf(" [-t <timeout>] [-a <expected answer address>] [-E <flags>] [-X <flags>] [-v]\n");
443}
367 444
368 printf (UT_SUPPORT); 445/* helpers */
446
447/**
448 * parse_flags_line - Parse a dig output line and extract DNS header flags.
449 *
450 * Input:
451 * line - NUL terminated dig output line, e.g. ";; flags: qr rd ra; ..."
452 *
453 * Returns:
454 * flag_list where:
455 * - items: array of NUL terminated flag strings (heap allocated)
456 * - count: number of entries in items
457 * On parse failure or if no flags were found, count is 0 and items is NULL.
458 */
459static flag_list parse_flags_line(const char *line) {
460 flag_list result = {.items = NULL, .count = 0};
461
462 if (!line) {
463 return result;
464 }
465
466 /* Locate start of DNS header flags in dig output */
467 const char *p = strstr(line, "flags:");
468 if (!p) {
469 return result;
470 }
471 p += 6; /* skip literal "flags:" */
472
473 /* Skip whitespace after "flags:" */
474 while (*p && isspace((unsigned char)*p)) {
475 p++;
476 }
477
478 /* Flags are terminated by the next semicolon e.g. "qr rd ra;" */
479 const char *q = strchr(p, ';');
480 if (!q) {
481 return result;
482 }
483
484 /* Extract substring containing the flag block */
485 size_t len = (size_t)(q - p);
486 if (len == 0) {
487 return result;
488 }
489
490 char *buf = (char *)malloc(len + 1);
491 if (!buf) {
492 return result;
493 }
494 memcpy(buf, p, len);
495 buf[len] = '\0';
496
497 /* Tokenize flags separated by whitespace */
498 char **arr = NULL;
499 size_t cnt = 0;
500 char *saveptr = NULL;
501 char *tok = strtok_r(buf, " \t", &saveptr);
502
503 while (tok) {
504 /* Expand array for the next flag token */
505 char **tmp = (char **)realloc(arr, (cnt + 1) * sizeof(char *));
506 if (!tmp) {
507 /* On allocation failure keep what we have and return it */
508 break;
509 }
510 arr = tmp;
511 arr[cnt++] = strdup(tok);
512 tok = strtok_r(NULL, " \t", &saveptr);
513 }
514
515 free(buf);
516
517 result.items = arr;
518 result.count = cnt;
519 return result;
369} 520}
370 521
522/**
523 * split_csv_trim - Split a comma separated string into trimmed tokens.
524 *
525 * Input:
526 * csv - NUL terminated string, e.g. "aa, qr , rd"
527 *
528 * Returns:
529 * flag_list where:
530 * - items: array of NUL terminated tokens (heap allocated, whitespace trimmed)
531 * - count: number of tokens
532 * On empty input, count is 0 and items is NULL
533 */
534static flag_list split_csv_trim(const char *csv) {
535 flag_list result = {.items = NULL, .count = 0};
536
537 if (!csv || !*csv) {
538 return result;
539 }
540
541 char *tmp = strdup(csv);
542 if (!tmp) {
543 return result;
544 }
545
546 char *s = tmp;
547 char *token = NULL;
548
549 /* Split CSV by commas, trimming whitespace on each token */
550 while ((token = strsep(&s, ",")) != NULL) {
551 /* trim leading whitespace */
552 while (*token && isspace((unsigned char)*token)) {
553 token++;
554 }
555
556 /* trim trailing whitespace */
557 char *end = token + strlen(token);
558 while (end > token && isspace((unsigned char)end[-1])) {
559 *--end = '\0';
560 }
561
562 if (*token) {
563 /* Expand the items array and append the token */
564 char **arr = (char **)realloc(result.items, (result.count + 1) * sizeof(char *));
565 if (!arr) {
566 /* Allocation failed, stop and return what we have */
567 break;
568 }
569 result.items = arr;
570 result.items[result.count++] = strdup(token);
571 }
572 }
573
574 free(tmp);
575 return result;
576}
371 577
578/**
579 * flag_list_contains - Case-insensitive membership test in a flag_list.
580 *
581 * Input:
582 * list - pointer to a flag_list
583 * needle - NUL terminated string to search for
584 *
585 * Returns:
586 * true if needle is contained in list (strcasecmp)
587 * false otherwise
588 */
589static bool flag_list_contains(const flag_list *list, const char *needle) {
590 if (!list || !needle || !*needle) {
591 return false;
592 }
593
594 for (size_t i = 0; i < list->count; i++) {
595 if (strcasecmp(list->items[i], needle) == 0) {
596 return true;
597 }
598 }
599 return false;
600}
372 601
373void 602/**
374print_usage (void) 603 * free_flag_list - Release all heap allocations held by a flag_list.
375{ 604 *
376 printf ("%s\n", _("Usage:")); 605 * Input:
377 printf ("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname); 606 * list - pointer to a flag_list whose items were allocated by
378 printf (" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n"); 607 * parse_flags_line() or split_csv_trim().
379 printf (" [-t <timeout>] [-a <expected answer address>] [-v]\n"); 608 *
609 * After this call list->items is NULL and list->count is 0.
610 */
611static void free_flag_list(flag_list *list) {
612 if (!list || !list->items) {
613 return;
614 }
615
616 for (size_t i = 0; i < list->count; i++) {
617 free(list->items[i]);
618 }
619 free(list->items);
620
621 list->items = NULL;
622 list->count = 0;
380} 623}
diff --git a/plugins/check_dig.d/config.h b/plugins/check_dig.d/config.h
new file mode 100644
index 00000000..dd1f58b5
--- /dev/null
+++ b/plugins/check_dig.d/config.h
@@ -0,0 +1,48 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6#define UNDEFINED 0
7#define DEFAULT_PORT 53
8#define DEFAULT_TRIES 2
9
10typedef struct {
11 char **items;
12 size_t count;
13} flag_list;
14
15typedef struct {
16 char *query_address;
17 char *record_type;
18 char *expected_address;
19 char *dns_server;
20 char *query_transport;
21 int server_port;
22 char *dig_args;
23 int number_tries;
24
25 double warning_interval;
26 double critical_interval;
27 flag_list require_flags;
28 flag_list forbid_flags;
29} check_dig_config;
30
31check_dig_config check_dig_config_init() {
32 check_dig_config tmp = {
33 .query_address = NULL,
34 .record_type = "A",
35 .expected_address = NULL,
36 .dns_server = NULL,
37 .query_transport = "",
38 .server_port = DEFAULT_PORT,
39 .dig_args = "",
40 .number_tries = DEFAULT_TRIES,
41
42 .warning_interval = UNDEFINED,
43 .critical_interval = UNDEFINED,
44 .require_flags = {.count = 0, .items = NULL},
45 .forbid_flags = {.count = 0, .items = NULL},
46 };
47 return tmp;
48}
diff --git a/plugins/check_disk.c b/plugins/check_disk.c
index 24de2d45..e773e56c 100644
--- a/plugins/check_disk.c
+++ b/plugins/check_disk.c
@@ -1,1161 +1,1342 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_disk plugin 3 * Monitoring check_disk plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2008 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_disk plugin 10 * This file contains the check_disk plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 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 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 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 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/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29const char *progname = "check_disk"; 29const char *progname = "check_disk";
30const char *program_name = "check_disk"; /* Required for coreutils libs */ 30const char *program_name = "check_disk"; /* Required for coreutils libs */
31const char *copyright = "1999-2008"; 31const char *copyright = "1999-2024";
32const char *email = "devel@monitoring-plugins.org"; 32const char *email = "devel@monitoring-plugins.org";
33 33
34 34#include "states.h"
35#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
36#ifdef HAVE_SYS_STAT_H 41#ifdef HAVE_SYS_STAT_H
37# include <sys/stat.h> 42# include <sys/stat.h>
38#endif 43#endif
44
39#if HAVE_INTTYPES_H 45#if HAVE_INTTYPES_H
40# include <inttypes.h> 46# include <inttypes.h>
41#endif 47#endif
48
42#include <assert.h> 49#include <assert.h>
43#include "popen.h"
44#include "utils.h"
45#include "utils_disk.h"
46#include <stdarg.h> 50#include <stdarg.h>
47#include "fsusage.h" 51#include <stdint.h>
48#include "mountlist.h"
49#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
50#if HAVE_LIMITS_H 59#if HAVE_LIMITS_H
51# include <limits.h> 60# include <limits.h>
52#endif 61#endif
62
53#include "regex.h" 63#include "regex.h"
54 64
55#ifdef __CYGWIN__ 65#ifdef __CYGWIN__
56# include <windows.h> 66# include <windows.h>
57# undef ERROR 67# undef ERROR
58# define ERROR -1 68# define ERROR -1
59#endif 69#endif
60 70
61/* If nonzero, show even filesystems with zero size or 71#ifdef _AIX
62 uninteresting types. */ 72# pragma alloca
63static int show_all_fs = 1; 73#endif
64
65/* If nonzero, show only local filesystems. */
66static int show_local_fs = 0;
67
68/* If nonzero, show only local filesystems but call stat() on remote ones. */
69static int stat_remote_fs = 0;
70
71/* If positive, the units to use when printing sizes;
72 if negative, the human-readable base. */
73/* static int output_block_size; */
74
75/* If nonzero, invoke the `sync' system call before getting any usage data.
76 Using this option can make df very slow, especially with many or very
77 busy disks. Note that this may make a difference on some systems --
78 SunOs4.1.3, for one. It is *not* necessary on Linux. */
79/* static int require_sync = 0; */
80
81/* Linked list of filesystem types to display.
82 If `fs_select_list' is NULL, list all types.
83 This table is generated dynamically from command-line options,
84 rather than hardcoding into the program what it thinks are the
85 valid filesystem types; let the user specify any filesystem type
86 they want to, and if there are any filesystems of that type, they
87 will be shown.
88
89 Some filesystem types:
90 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
91
92/* static struct parameter_list *fs_select_list; */
93
94/* Linked list of filesystem types to omit.
95 If the list is empty, don't exclude any types. */
96static struct regex_list *fs_exclude_list = NULL;
97 74
98/* Linked list of filesystem types to check. 75typedef struct {
99 If the list is empty, include all types. */ 76 int errorcode;
100static struct regex_list *fs_include_list; 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,
82 char *crit_freespace_units, char *warn_freespace_percent,
83 char *crit_freespace_percent, char *warn_freeinodes_percent,
84 char *crit_freeinodes_percent);
85static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/);
86static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/);
87
88/*
89 * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control
90 * how reserved and inodes should be judged (ignored or not)
91 */
92static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp,
93 bool freespace_ignore_reserved);
94static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit,
95 bool display_inodes_perfdata, byte_unit unit);
96
97void print_usage(void);
98static void print_help(void);
99
100static int verbose = 0;
101
102// This would not be necessary in C23!!
103const byte_unit Bytes_Factor = 1;
104const byte_unit KibiBytes_factor = 1024;
105const byte_unit MebiBytes_factor = 1048576;
106const byte_unit GibiBytes_factor = 1073741824;
107const byte_unit TebiBytes_factor = 1099511627776;
108const byte_unit PebiBytes_factor = 1125899906842624;
109const byte_unit ExbiBytes_factor = 1152921504606846976;
110const byte_unit KiloBytes_factor = 1000;
111const byte_unit MegaBytes_factor = 1000000;
112const byte_unit GigaBytes_factor = 1000000000;
113const byte_unit TeraBytes_factor = 1000000000000;
114const byte_unit PetaBytes_factor = 1000000000000000;
115const byte_unit ExaBytes_factor = 1000000000000000000;
116
117int main(int argc, char **argv) {
118 setlocale(LC_ALL, "");
119 bindtextdomain(PACKAGE, LOCALEDIR);
120 textdomain(PACKAGE);
101 121
102static struct name_list *dp_exclude_list; 122#ifdef __CYGWIN__
123 char mountdir[32];
124#endif
103 125
104static struct parameter_list *path_select_list = NULL; 126 // Parse extra opts if any
127 argv = np_extra_opts(&argc, argv, progname);
128
129 check_disk_config_wrapper tmp_config = process_arguments(argc, argv);
130 if (tmp_config.errorcode == ERROR) {
131 usage4(_("Could not parse arguments"));
132 }
133
134 check_disk_config config = tmp_config.config;
135
136 if (config.output_format_is_set) {
137 mp_set_format(config.output_format);
138 }
139
140 if (config.erronly) {
141 mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY);
142 }
143
144 if (!config.path_ignored) {
145 mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list,
146 config.exact_match);
147 }
148
149 // Error if no match found for specified paths
150 for (parameter_list_elem *elem = config.path_select_list.first; elem;) {
151 if (!elem->best_match && config.ignore_missing) {
152 /* Delete the path from the list so that it is not stat-checked later in the code. */
153 elem = mp_int_fs_list_del(&config.path_select_list, elem);
154 continue;
155 }
156 if (!elem->best_match) {
157 /* Without --ignore-missing option, exit with Critical state. */
158 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name);
159 }
160
161 elem = mp_int_fs_list_get_next(elem);
162 }
163
164 mp_check overall = mp_check_init();
165 if (config.path_select_list.length == 0) {
166 mp_subcheck none_sc = mp_subcheck_init();
167 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
168 if (config.ignore_missing) {
169 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
170 } else {
171 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
172 if (verbose >= 2) {
173 printf("None of the provided paths were found\n");
174 }
175 }
176 mp_add_subcheck_to_check(&overall, none_sc);
177 mp_exit(overall);
178 }
105 179
106/* Linked list of mounted filesystems. */ 180 // Filter list first
107static struct mount_entry *mount_list; 181 for (parameter_list_elem *path = config.path_select_list.first; path;) {
182 if (!path->best_match) {
183 path = mp_int_fs_list_del(&config.path_select_list, path);
184 continue;
185 }
108 186
109/* For long options that have no equivalent short option, use a 187 struct mount_entry *mount_entry = path->best_match;
110 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
111enum
112{
113 SYNC_OPTION = CHAR_MAX + 1,
114 NO_SYNC_OPTION,
115 BLOCK_SIZE_OPTION
116};
117 188
118#ifdef _AIX 189#ifdef __CYGWIN__
119#pragma alloca 190 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) {
191 path = mp_int_fs_list_del(&config.path_select_list, path);
192 continue;
193 }
194
195 char *mountdir = NULL;
196 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10);
197 if (GetDriveType(mountdir) != DRIVE_FIXED) {
198 mount_entry->me_remote = 1;
199 }
120#endif 200#endif
121 201
122int process_arguments (int, char **); 202 /* Remove filesystems already seen */
123void print_path (const char *mypath); 203 if (np_seen_name(config.seen, mount_entry->me_mountdir)) {
124void set_all_thresholds (struct parameter_list *path); 204 path = mp_int_fs_list_del(&config.path_select_list, path);
125int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *); 205 continue;
126void print_help (void); 206 }
127void print_usage (void); 207
128double calculate_percent(uintmax_t, uintmax_t); 208 if (path->group == NULL) {
129bool stat_path (struct parameter_list *p); 209 if (config.fs_exclude_list &&
130void get_stats (struct parameter_list *p, struct fs_usage *fsp); 210 np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) {
131void get_path_stats (struct parameter_list *p, struct fs_usage *fsp); 211 // Skip excluded fs's
132 212 path = mp_int_fs_list_del(&config.path_select_list, path);
133char *exclude_device; 213 continue;
134char *units; 214 }
135uintmax_t mult = 1024 * 1024;
136int verbose = 0;
137bool erronly = false;
138bool display_mntp = false;
139bool exact_match = false;
140bool ignore_missing = false;
141bool freespace_ignore_reserved = false;
142bool display_inodes_perfdata = false;
143char *warn_freespace_units = NULL;
144char *crit_freespace_units = NULL;
145char *warn_freespace_percent = NULL;
146char *crit_freespace_percent = NULL;
147char *warn_usedspace_units = NULL;
148char *crit_usedspace_units = NULL;
149char *warn_usedspace_percent = NULL;
150char *crit_usedspace_percent = NULL;
151char *warn_usedinodes_percent = NULL;
152char *crit_usedinodes_percent = NULL;
153char *warn_freeinodes_percent = NULL;
154char *crit_freeinodes_percent = NULL;
155bool path_selected = false;
156bool path_ignored = false;
157char *group = NULL;
158struct stat *stat_buf;
159struct name_list *seen = NULL;
160
161
162int
163main (int argc, char **argv)
164{
165 int result = STATE_UNKNOWN;
166 int disk_result = STATE_UNKNOWN;
167 char *output;
168 char *ignored;
169 char *details;
170 char *perf;
171 char *perf_ilabel;
172 char *preamble = " - free space:";
173 char *ignored_preamble = " - ignored paths:";
174 char *flag_header;
175 int temp_result;
176
177 struct mount_entry *me;
178 struct fs_usage fsp;
179 struct parameter_list *temp_list, *path;
180 215
181#ifdef __CYGWIN__ 216 if (config.device_path_exclude_list &&
182 char mountdir[32]; 217 (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) ||
183#endif 218 np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) {
219 // Skip excluded device or mount paths
220 path = mp_int_fs_list_del(&config.path_select_list, path);
221 continue;
222 }
184 223
185 output = strdup (""); 224 if (config.fs_include_list &&
186 ignored = strdup (""); 225 !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) {
187 details = strdup (""); 226 // Skip not included fstypes
188 perf = strdup (""); 227 path = mp_int_fs_list_del(&config.path_select_list, path);
189 perf_ilabel = strdup (""); 228 continue;
190 stat_buf = malloc(sizeof *stat_buf); 229 }
191
192 setlocale (LC_ALL, "");
193 bindtextdomain (PACKAGE, LOCALEDIR);
194 textdomain (PACKAGE);
195
196 mount_list = read_file_system_list (0);
197
198 /* Parse extra opts if any */
199 argv = np_extra_opts (&argc, argv, progname);
200
201 if (process_arguments (argc, argv) == ERROR)
202 usage4 (_("Could not parse arguments"));
203
204 /* If a list of paths has not been selected, find entire
205 mount list and create list of paths
206 */
207 if (path_selected == false && path_ignored == false) {
208 for (me = mount_list; me; me = me->me_next) {
209 if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) {
210 path = np_add_parameter(&path_select_list, me->me_mountdir);
211 }
212 path->best_match = me;
213 path->group = group;
214 set_all_thresholds(path);
215 }
216 }
217
218 if (path_ignored == false) {
219 np_set_best_match(path_select_list, mount_list, exact_match);
220 }
221
222 /* Error if no match found for specified paths */
223 temp_list = path_select_list;
224
225 while (path_select_list) {
226 if (! path_select_list->best_match && ignore_missing == true) {
227 /* If the first element will be deleted, the temp_list must be updated with the new start address as well */
228 if (path_select_list == temp_list) {
229 temp_list = path_select_list->name_next;
230 }
231 /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */
232 xasprintf (&ignored, "%s %s;", ignored, path_select_list->name);
233 /* Delete the path from the list so that it is not stat-checked later in the code. */
234 path_select_list = np_del_parameter(path_select_list, path_select_list->name_prev);
235 } else if (! path_select_list->best_match) {
236 /* Without --ignore-missing option, exit with Critical state. */
237 die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list->name);
238 } else {
239 /* Continue jumping through the list */
240 path_select_list = path_select_list->name_next;
241 }
242 }
243
244 path_select_list = temp_list;
245
246 if (! path_select_list && ignore_missing == true) {
247 result = STATE_OK;
248 if (verbose >= 2) {
249 printf ("None of the provided paths were found\n");
250 }
251 }
252
253 /* Process for every path in list */
254 for (path = path_select_list; path; path=path->name_next) {
255 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL)
256 printf("Thresholds(pct) for %s warn: %f crit %f\n",
257 path->name,
258 path->freespace_percent->warning->end,
259 path->freespace_percent->critical->end);
260
261 if (verbose >= 3 && path->group != NULL)
262 printf("Group of %s: %s\n",path->name,path->group);
263
264 /* reset disk result */
265 disk_result = STATE_UNKNOWN;
266
267 me = path->best_match;
268
269 if (!me) {
270 continue;
271 }
272 230
273#ifdef __CYGWIN__ 231 /* Skip remote filesystems if we're not interested in them */
274 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) 232 if (mount_entry->me_remote && config.show_local_fs) {
275 continue; 233 if (config.stat_remote_fs) {
276 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); 234 // TODO Stat here
277 if (GetDriveType(mountdir) != DRIVE_FIXED) 235 if (!stat_path(path, config.ignore_missing) && config.ignore_missing) {
278 me->me_remote = 1; 236 }
279#endif 237 }
280 /* Filters */ 238 continue;
281 239 }
282 /* Remove filesystems already seen */
283 if (np_seen_name(seen, me->me_mountdir)) {
284 continue;
285 }
286 np_add_name(&seen, me->me_mountdir);
287
288 if (path->group == NULL) {
289 /* Skip remote filesystems if we're not interested in them */
290 if (me->me_remote && show_local_fs) {
291 if (stat_remote_fs) {
292 if (!stat_path(path) && ignore_missing == true) {
293 result = STATE_OK;
294 xasprintf (&ignored, "%s %s;", ignored, path->name);
295 }
296 }
297 continue;
298 /* Skip pseudo fs's if we haven't asked for all fs's */
299 } else if (me->me_dummy && !show_all_fs) {
300 continue;
301 /* Skip excluded fstypes */
302 } else if (fs_exclude_list && np_find_regmatch (fs_exclude_list, me->me_type)) {
303 continue;
304 /* Skip excluded fs's */
305 } else if (dp_exclude_list &&
306 (np_find_name (dp_exclude_list, me->me_devname) ||
307 np_find_name (dp_exclude_list, me->me_mountdir))) {
308 continue;
309 /* Skip not included fstypes */
310 } else if (fs_include_list && !np_find_regmatch(fs_include_list, me->me_type)) {
311 continue;
312 }
313 }
314
315 if (!stat_path(path)) {
316 if (ignore_missing == true) {
317 result = STATE_OK;
318 xasprintf (&ignored, "%s %s;", ignored, path->name);
319 }
320 continue;
321 }
322 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
323
324 if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
325 get_stats (path, &fsp);
326
327 if (verbose >= 3) {
328 printf ("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n",
329 me->me_mountdir,
330 path->dused_pct,
331 path->dfree_pct,
332 path->dused_units,
333 path->dfree_units,
334 path->dtotal_units,
335 path->dused_inodes_percent,
336 path->dfree_inodes_percent,
337 fsp.fsu_blocksize,
338 mult);
339 }
340
341 /* Threshold comparisons */
342
343 temp_result = get_status(path->dfree_units, path->freespace_units);
344 if (verbose >=3) printf("Freespace_units result=%d\n", temp_result);
345 disk_result = max_state( disk_result, temp_result );
346
347 temp_result = get_status(path->dfree_pct, path->freespace_percent);
348 if (verbose >=3) printf("Freespace%% result=%d\n", temp_result);
349 disk_result = max_state( disk_result, temp_result );
350
351 temp_result = get_status(path->dused_units, path->usedspace_units);
352 if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result);
353 disk_result = max_state( disk_result, temp_result );
354
355 temp_result = get_status(path->dused_pct, path->usedspace_percent);
356 if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result);
357 disk_result = max_state( disk_result, temp_result );
358
359 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
360 if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result);
361 disk_result = max_state( disk_result, temp_result );
362
363 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
364 if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result);
365 disk_result = max_state( disk_result, temp_result );
366
367 result = max_state(result, disk_result);
368
369 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
370 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
371 data. Assumption that start=0. Roll on new syntax...
372 */
373
374 /* *_high_tide must be reinitialized at each run */
375 uint64_t warning_high_tide = UINT64_MAX;
376
377 if (path->freespace_units->warning != NULL) {
378 warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * mult;
379 }
380 if (path->freespace_percent->warning != NULL) {
381 warning_high_tide = min( warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end/100) * (path->dtotal_units * mult)) );
382 }
383
384 uint64_t critical_high_tide = UINT64_MAX;
385
386 if (path->freespace_units->critical != NULL) {
387 critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * mult;
388 }
389 if (path->freespace_percent->critical != NULL) {
390 critical_high_tide = min( critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end/100) * (path->dtotal_units * mult)) );
391 }
392
393 /* Nb: *_high_tide are unset when == UINT64_MAX */
394 xasprintf (&perf, "%s %s", perf,
395 perfdata_uint64 (
396 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
397 path->dused_units * mult, "B",
398 (warning_high_tide == UINT64_MAX ? false : true), warning_high_tide,
399 (critical_high_tide == UINT64_MAX ? false : true), critical_high_tide,
400 true, 0,
401 true, path->dtotal_units * mult));
402
403 if (display_inodes_perfdata) {
404 /* *_high_tide must be reinitialized at each run */
405 warning_high_tide = UINT64_MAX;
406 critical_high_tide = UINT64_MAX;
407
408 if (path->freeinodes_percent->warning != NULL) {
409 warning_high_tide = (uint64_t) fabs( min( (double) warning_high_tide, (double) (1.0 - path->freeinodes_percent->warning->end/100)*path->inodes_total ));
410 }
411 if (path->freeinodes_percent->critical != NULL) {
412 critical_high_tide = (uint64_t) fabs( min( (double) critical_high_tide, (double) (1.0 - path->freeinodes_percent->critical->end/100)*path->inodes_total ));
413 }
414
415 xasprintf (&perf_ilabel, "%s (inodes)", (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir);
416 /* Nb: *_high_tide are unset when == UINT64_MAX */
417 xasprintf (&perf, "%s %s", perf,
418 perfdata_uint64 (perf_ilabel,
419 path->inodes_used, "",
420 (warning_high_tide != UINT64_MAX ? true : false), warning_high_tide,
421 (critical_high_tide != UINT64_MAX ? true : false), critical_high_tide,
422 true, 0,
423 true, path->inodes_total));
424 }
425
426 if (disk_result==STATE_OK && erronly && !verbose)
427 continue;
428
429 if(disk_result && verbose >= 1) {
430 xasprintf(&flag_header, " %s [", state_text (disk_result));
431 } else {
432 xasprintf(&flag_header, "");
433 }
434 xasprintf (&output, "%s%s %s %llu%s (%.1f%%",
435 output, flag_header,
436 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
437 path->dfree_units,
438 units,
439 path->dfree_pct);
440 if (path->dused_inodes_percent < 0) {
441 xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : ""));
442 } else {
443 xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : ""));
444 }
445 free(flag_header);
446 }
447 }
448
449 if (verbose >= 2)
450 xasprintf (&output, "%s%s", output, details);
451
452 if (strcmp(output, "") == 0 && ! erronly) {
453 preamble = "";
454 xasprintf (&output, " - No disks were found for provided parameters");
455 }
456
457 printf ("DISK %s%s%s%s%s|%s\n", state_text (result), ((erronly && result==STATE_OK)) ? "" : preamble, output, (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf);
458 return result;
459}
460 240
241 // TODO why stat here? remove unstatable fs?
242 if (!stat_path(path, config.ignore_missing)) {
243 // if (config.ignore_missing) {
244 // xasprintf(&ignored, "%s %s;", ignored, path->name);
245 // }
246 // not accessible, remove from list
247 path = mp_int_fs_list_del(&config.path_select_list, path);
248 continue;
249 }
250 }
251
252 path = mp_int_fs_list_get_next(path);
253 }
254
255 // now get the actual measurements
256 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) {
257 // Get actual metrics here
258 struct mount_entry *mount_entry = filesystem->best_match;
259 struct fs_usage fsp = {0};
260 get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp);
261
262 if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) {
263 *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved);
264
265 if (verbose >= 3) {
266 printf("For %s, used_units=%lu free_units=%lu total_units=%lu "
267 "fsp.fsu_blocksize=%lu\n",
268 mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes,
269 filesystem->total_bytes, fsp.fsu_blocksize);
270 }
271 } else {
272 // failed to retrieve file system data or not mounted?
273 filesystem = mp_int_fs_list_del(&config.path_select_list, filesystem);
274 continue;
275 }
276 filesystem = mp_int_fs_list_get_next(filesystem);
277 }
278
279 if (verbose > 2) {
280 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
281 filesystem = mp_int_fs_list_get_next(filesystem)) {
282 assert(filesystem->best_match != NULL);
283 if (filesystem->best_match == NULL) {
284 printf("Filesystem path %s has no mount_entry!\n", filesystem->name);
285 } else {
286 // printf("Filesystem path %s has a mount_entry!\n", filesystem->name);
287 }
288 }
289 }
290
291 measurement_unit_list *measurements = NULL;
292 measurement_unit_list *current = NULL;
293 // create measuring units, because of groups
294 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
295 filesystem = mp_int_fs_list_get_next(filesystem)) {
296 assert(filesystem->best_match != NULL);
297
298 if (filesystem->group == NULL) {
299 // create a measurement unit for the fs
300 measurement_unit unit =
301 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
302 if (measurements == NULL) {
303 measurements = current = add_measurement_list(NULL, unit);
304 } else {
305 current = add_measurement_list(measurements, unit);
306 }
307 } else {
308 // Grouped elements are consecutive
309 if (measurements == NULL) {
310 // first entry
311 measurement_unit unit =
312 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
313 unit.name = strdup(filesystem->group);
314 measurements = current = add_measurement_list(NULL, unit);
315 } else {
316 // if this is the first element of a group, the name of the previous entry is
317 // different
318 if (strcmp(filesystem->group, current->unit.name) != 0) {
319 // so, this must be the first element of a group
320 measurement_unit unit =
321 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
322 unit.name = filesystem->group;
323 current = add_measurement_list(measurements, unit);
324
325 } else {
326 // NOT the first entry of a group, add info to the other one
327 current->unit = add_filesystem_to_measurement_unit(current->unit, *filesystem);
328 }
329 }
330 }
331 }
332
333 /* Process for every path in list */
334 if (measurements != NULL) {
335 for (measurement_unit_list *unit = measurements; unit; unit = unit->next) {
336 mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata,
337 config.display_unit);
338 mp_add_subcheck_to_check(&overall, unit_sc);
339 }
340 } else {
341 // Apparently no machting fs found
342 mp_subcheck none_sc = mp_subcheck_init();
343 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
344
345 if (config.ignore_missing) {
346 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
347 } else {
348 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
349 }
350 mp_add_subcheck_to_check(&overall, none_sc);
351 }
352
353 mp_exit(overall);
354}
461 355
462double calculate_percent(uintmax_t value, uintmax_t total) { 356double calculate_percent(uintmax_t value, uintmax_t total) {
463 double pct = -1; 357 double pct = -1;
464 if(value <= DBL_MAX && total != 0) { 358 if (value <= DBL_MAX && total != 0) {
465 pct = (double)value / total * 100.0; 359 pct = (double)value / (double)total * 100.0;
466 } 360 }
467 return pct; 361
362 return pct;
468} 363}
469 364
470/* process command-line arguments */ 365/* process command-line arguments */
471int 366check_disk_config_wrapper process_arguments(int argc, char **argv) {
472process_arguments (int argc, char **argv) 367
473{ 368 check_disk_config_wrapper result = {
474 int c, err; 369 .errorcode = OK,
475 struct parameter_list *se; 370 .config = check_disk_config_init(),
476 struct parameter_list *temp_list = NULL, *previous = NULL; 371 };
477 struct mount_entry *me; 372
478 regex_t re; 373 if (argc < 2) {
479 int cflags = REG_NOSUB | REG_EXTENDED; 374 result.errorcode = ERROR;
480 int default_cflags = cflags; 375 return result;
481 char errbuf[MAX_INPUT_BUFFER]; 376 }
482 int fnd = 0; 377
483 378 enum {
484 int option = 0; 379 output_format_index = CHAR_MAX + 1,
485 static struct option longopts[] = { 380 display_unit_index,
486 {"timeout", required_argument, 0, 't'}, 381 };
487 {"warning", required_argument, 0, 'w'}, 382
488 {"critical", required_argument, 0, 'c'}, 383 static struct option longopts[] = {{"timeout", required_argument, 0, 't'},
489 {"iwarning", required_argument, 0, 'W'}, 384 {"warning", required_argument, 0, 'w'},
490 /* Dang, -C is taken. We might want to reshuffle this. */ 385 {"critical", required_argument, 0, 'c'},
491 {"icritical", required_argument, 0, 'K'}, 386 {"iwarning", required_argument, 0, 'W'},
492 {"kilobytes", no_argument, 0, 'k'}, 387 {"icritical", required_argument, 0, 'K'},
493 {"megabytes", no_argument, 0, 'm'}, 388 {"kilobytes", no_argument, 0, 'k'},
494 {"units", required_argument, 0, 'u'}, 389 {"megabytes", no_argument, 0, 'm'},
495 {"path", required_argument, 0, 'p'}, 390 {"units", required_argument, 0, 'u'},
496 {"partition", required_argument, 0, 'p'}, 391 {"path", required_argument, 0, 'p'},
497 {"exclude_device", required_argument, 0, 'x'}, 392 {"partition", required_argument, 0, 'p'},
498 {"exclude-type", required_argument, 0, 'X'}, 393 {"exclude_device", required_argument, 0, 'x'},
499 {"include-type", required_argument, 0, 'N'}, 394 {"exclude-type", required_argument, 0, 'X'},
500 {"group", required_argument, 0, 'g'}, 395 {"include-type", required_argument, 0, 'N'},
501 {"eregi-path", required_argument, 0, 'R'}, 396 {"group", required_argument, 0, 'g'},
502 {"eregi-partition", required_argument, 0, 'R'}, 397 {"eregi-path", required_argument, 0, 'R'},
503 {"ereg-path", required_argument, 0, 'r'}, 398 {"eregi-partition", required_argument, 0, 'R'},
504 {"ereg-partition", required_argument, 0, 'r'}, 399 {"ereg-path", required_argument, 0, 'r'},
505 {"freespace-ignore-reserved", no_argument, 0, 'f'}, 400 {"ereg-partition", required_argument, 0, 'r'},
506 {"ignore-ereg-path", required_argument, 0, 'i'}, 401 {"freespace-ignore-reserved", no_argument, 0, 'f'},
507 {"ignore-ereg-partition", required_argument, 0, 'i'}, 402 {"ignore-ereg-path", required_argument, 0, 'i'},
508 {"ignore-eregi-path", required_argument, 0, 'I'}, 403 {"ignore-ereg-partition", required_argument, 0, 'i'},
509 {"ignore-eregi-partition", required_argument, 0, 'I'}, 404 {"ignore-eregi-path", required_argument, 0, 'I'},
510 {"ignore-missing", no_argument, 0, 'n'}, 405 {"ignore-eregi-partition", required_argument, 0, 'I'},
511 {"local", no_argument, 0, 'l'}, 406 {"ignore-missing", no_argument, 0, 'n'},
512 {"stat-remote-fs", no_argument, 0, 'L'}, 407 {"local", no_argument, 0, 'l'},
513 {"iperfdata", no_argument, 0, 'P'}, 408 {"stat-remote-fs", no_argument, 0, 'L'},
514 {"mountpoint", no_argument, 0, 'M'}, 409 {"iperfdata", no_argument, 0, 'P'},
515 {"errors-only", no_argument, 0, 'e'}, 410 {"mountpoint", no_argument, 0, 'M'},
516 {"exact-match", no_argument, 0, 'E'}, 411 {"errors-only", no_argument, 0, 'e'},
517 {"all", no_argument, 0, 'A'}, 412 {"exact-match", no_argument, 0, 'E'},
518 {"verbose", no_argument, 0, 'v'}, 413 {"all", no_argument, 0, 'A'},
519 {"quiet", no_argument, 0, 'q'}, 414 {"verbose", no_argument, 0, 'v'},
520 {"clear", no_argument, 0, 'C'}, 415 {"quiet", no_argument, 0, 'q'},
521 {"version", no_argument, 0, 'V'}, 416 {"clear", no_argument, 0, 'C'},
522 {"help", no_argument, 0, 'h'}, 417 {"version", no_argument, 0, 'V'},
523 {0, 0, 0, 0} 418 {"help", no_argument, 0, 'h'},
524 }; 419 {"output-format", required_argument, 0, output_format_index},
525 420 {"display-unit", required_argument, 0, display_unit_index},
526 if (argc < 2) 421 {0, 0, 0, 0}};
527 return ERROR; 422
528 423 for (int index = 1; index < argc; index++) {
529 np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); 424 if (strcmp("-to", argv[index]) == 0) {
530 425 strcpy(argv[index], "-t");
531 for (c = 1; c < argc; c++) 426 }
532 if (strcmp ("-to", argv[c]) == 0) 427 }
533 strcpy (argv[c], "-t"); 428
534 429 int cflags = REG_NOSUB | REG_EXTENDED;
535 while (1) { 430 int default_cflags = cflags;
536 c = getopt_long (argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); 431 char *warn_freespace_units = NULL;
537 432 char *crit_freespace_units = NULL;
538 if (c == -1 || c == EOF) 433 char *warn_freespace_percent = NULL;
539 break; 434 char *crit_freespace_percent = NULL;
540 435 char *warn_freeinodes_percent = NULL;
541 switch (c) { 436 char *crit_freeinodes_percent = NULL;
542 case 't': /* timeout period */ 437
543 if (is_integer (optarg)) { 438 bool path_selected = false;
544 timeout_interval = atoi (optarg); 439 char *group = NULL;
545 break; 440 byte_unit unit = MebiBytes_factor;
546 } 441
547 else { 442 result.config.mount_list = read_file_system_list(false);
548 usage2 (_("Timeout interval must be a positive integer"), optarg); 443
549 } 444 np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED);
550 445
551 /* See comments for 'c' */ 446 while (true) {
552 case 'w': /* warning threshold */ 447 int option = 0;
448 int option_index = getopt_long(
449 argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
450
451 if (CHECK_EOF(option_index)) {
452 break;
453 }
454
455 switch (option_index) {
456 case 't': /* timeout period */
457 if (is_integer(optarg)) {
458 timeout_interval = atoi(optarg);
459 break;
460 } else {
461 usage2(_("Timeout interval must be a positive integer"), optarg);
462 }
463
464 /* See comments for 'c' */
465 case 'w': /* warning threshold */
553 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { 466 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
554 die(STATE_UNKNOWN, "Argument for --warning invalid or missing: %s\n", optarg); 467 die(STATE_UNKNOWN, "Argument for --warning invalid or missing: %s\n", optarg);
555 } 468 }
556 469
557 if (strstr(optarg, "%")) { 470 if (strstr(optarg, "%")) {
558 if (*optarg == '@') { 471 if (*optarg == '@') {
559 warn_freespace_percent = optarg; 472 warn_freespace_percent = optarg;
560 } else { 473 } else {
561 xasprintf(&warn_freespace_percent, "@%s", optarg); 474 xasprintf(&warn_freespace_percent, "@%s", optarg);
562 } 475 }
563 } else { 476 } else {
564 if (*optarg == '@') { 477 if (*optarg == '@') {
565 warn_freespace_units = optarg; 478 warn_freespace_units = optarg;
566 } else { 479 } else {
567 xasprintf(&warn_freespace_units, "@%s", optarg); 480 xasprintf(&warn_freespace_units, "@%s", optarg);
568 } 481 }
569 } 482 }
570 break; 483 break;
571 484
572 /* Awful mistake where the range values do not make sense. Normally, 485 /* Awful mistake where the range values do not make sense. Normally,
573 you alert if the value is within the range, but since we are using 486 * you alert if the value is within the range, but since we are using
574 freespace, we have to alert if outside the range. Thus we artificially 487 * freespace, we have to alert if outside the range. Thus we artificially
575 force @ at the beginning of the range, so that it is backwards compatible 488 * force @ at the beginning of the range, so that it is backwards compatible
576 */ 489 */
577 case 'c': /* critical threshold */ 490 case 'c': /* critical threshold */
578 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { 491 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
579 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); 492 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg);
580 } 493 }
581 494
582 if (strstr(optarg, "%")) { 495 if (strstr(optarg, "%")) {
583 if (*optarg == '@') { 496 if (*optarg == '@') {
584 crit_freespace_percent = optarg; 497 crit_freespace_percent = optarg;
585 } else { 498 } else {
586 xasprintf(&crit_freespace_percent, "@%s", optarg); 499 xasprintf(&crit_freespace_percent, "@%s", optarg);
587 } 500 }
588 } else { 501 } else {
589 if (*optarg == '@') { 502 if (*optarg == '@') {
590 crit_freespace_units = optarg; 503 crit_freespace_units = optarg;
591 } else { 504 } else {
592 xasprintf(&crit_freespace_units, "@%s", optarg); 505 xasprintf(&crit_freespace_units, "@%s", optarg);
593 } 506 }
594 } 507 }
595 break; 508 break;
596
597 case 'W': /* warning inode threshold */
598 if (*optarg == '@') {
599 warn_freeinodes_percent = optarg;
600 } else {
601 xasprintf(&warn_freeinodes_percent, "@%s", optarg);
602 }
603 break;
604 case 'K': /* critical inode threshold */
605 if (*optarg == '@') {
606 crit_freeinodes_percent = optarg;
607 } else {
608 xasprintf(&crit_freeinodes_percent, "@%s", optarg);
609 }
610 break;
611 case 'u':
612 if (units)
613 free(units);
614 if (! strcasecmp (optarg, "bytes")) {
615 mult = (uintmax_t)1;
616 units = strdup ("B");
617 } else if (!strcmp(optarg, "KiB")) {
618 mult = (uintmax_t)1024;
619 units = strdup ("KiB");
620 } else if (! strcmp (optarg, "kB")) {
621 mult = (uintmax_t)1000;
622 units = strdup ("kB");
623 } else if (!strcmp(optarg, "MiB")) {
624 mult = (uintmax_t)1024 * 1024;
625 units = strdup ("MiB");
626 } else if (! strcmp (optarg, "MB")) {
627 mult = (uintmax_t)1000 * 1000;
628 units = strdup ("MB");
629 } else if (!strcmp(optarg, "GiB")) {
630 mult = (uintmax_t)1024 * 1024 * 1024;
631 units = strdup ("GiB");
632 } else if (! strcmp (optarg, "GB")){
633 mult = (uintmax_t)1000 * 1000 * 1000;
634 units = strdup ("GB");
635 } else if (!strcmp(optarg, "TiB")) {
636 mult = (uintmax_t)1024 * 1024 * 1024 * 1024;
637 units = strdup ("TiB");
638 } else if (! strcmp (optarg, "TB")) {
639 mult = (uintmax_t)1000 * 1000 * 1000 * 1000;
640 units = strdup ("TB");
641 } else if (!strcmp(optarg, "PiB")) {
642 mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024;
643 units = strdup ("PiB");
644 } else if (! strcmp (optarg, "PB")){
645 mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000;
646 units = strdup ("PB");
647 } else {
648 die (STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
649 }
650 if (units == NULL)
651 die (STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
652 break;
653 case 'k': /* display mountpoint */
654 mult = 1024;
655 if (units)
656 free(units);
657 units = strdup ("kiB");
658 break;
659 case 'm': /* display mountpoint */
660 mult = 1024 * 1024;
661 if (units)
662 free(units);
663 units = strdup ("MiB");
664 break;
665 case 'L':
666 stat_remote_fs = 1;
667 /* fallthrough */
668 case 'l':
669 show_local_fs = 1;
670 break;
671 case 'P':
672 display_inodes_perfdata = 1;
673 break;
674 case 'p': /* select path */
675 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
676 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
677 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
678 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
679 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
680 }
681
682 /* add parameter if not found. overwrite thresholds if path has already been added */
683 if (! (se = np_find_parameter(path_select_list, optarg))) {
684 se = np_add_parameter(&path_select_list, optarg);
685
686 if (stat(optarg, &stat_buf[0]) && ignore_missing == true) {
687 path_ignored = true;
688 break;
689 }
690 }
691 se->group = group;
692 set_all_thresholds(se);
693
694 /* With autofs, it is required to stat() the path before re-populating the mount_list */
695 if (!stat_path(se)) {
696 break;
697 }
698 /* NB: We can't free the old mount_list "just like that": both list pointers and struct
699 * pointers are copied around. One of the reason it wasn't done yet is that other parts
700 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
701 mount_list = read_file_system_list (0);
702 np_set_best_match(se, mount_list, exact_match);
703
704 path_selected = true;
705 break;
706 case 'x': /* exclude path or partition */
707 np_add_name(&dp_exclude_list, optarg);
708 break;
709 case 'X': /* exclude file system type */
710 err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED);
711 if (err != 0) {
712 regerror (err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
713 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
714 }
715 break;
716 case 'N': /* include file system type */
717 err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED);
718 if (err != 0) {
719 regerror (err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
720 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
721 }
722 break;
723 case 'v': /* verbose */
724 verbose++;
725 break;
726 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
727 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
728 erronly = true;
729 break;
730 case 'e':
731 erronly = true;
732 break;
733 case 'E':
734 if (path_selected)
735 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
736 exact_match = true;
737 break;
738 case 'f':
739 freespace_ignore_reserved = true;
740 break;
741 case 'g':
742 if (path_selected)
743 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
744 group = optarg;
745 break;
746 case 'I':
747 cflags |= REG_ICASE;
748 // Intentional fallthrough
749 case 'i':
750 if (!path_selected)
751 die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
752 err = regcomp(&re, optarg, cflags);
753 if (err != 0) {
754 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
755 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
756 }
757
758 temp_list = path_select_list;
759
760 previous = NULL;
761 while (temp_list) {
762 if (temp_list->best_match) {
763 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
764
765 if (verbose >=3)
766 printf("ignoring %s matching regex\n", temp_list->name);
767
768 temp_list = np_del_parameter(temp_list, previous);
769 /* pointer to first element needs to be updated if first item gets deleted */
770 if (previous == NULL)
771 path_select_list = temp_list;
772 } else {
773 previous = temp_list;
774 temp_list = temp_list->name_next;
775 }
776 } else {
777 previous = temp_list;
778 temp_list = temp_list->name_next;
779 }
780 }
781
782
783 cflags = default_cflags;
784 break;
785
786 case 'n':
787 ignore_missing = true;
788 break;
789 case 'A':
790 optarg = strdup(".*");
791 // Intentional fallthrough
792 case 'R':
793 cflags |= REG_ICASE;
794 // Intentional fallthrough
795 case 'r':
796 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
797 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
798 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
799 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
800 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n"));
801 }
802
803 err = regcomp(&re, optarg, cflags);
804 if (err != 0) {
805 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
806 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
807 }
808
809 for (me = mount_list; me; me = me->me_next) {
810 if (np_regex_match_mount_entry(me, &re)) {
811 fnd = true;
812 if (verbose >= 3)
813 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
814
815 /* add parameter if not found. overwrite thresholds if path has already been added */
816 if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
817 se = np_add_parameter(&path_select_list, me->me_mountdir);
818 }
819 se->group = group;
820 set_all_thresholds(se);
821 }
822 }
823
824 if (!fnd && ignore_missing == true) {
825 path_ignored = true;
826 path_selected = true;
827 break;
828 } else if (!fnd)
829 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
830 _("Regular expression did not match any path or disk"), optarg);
831
832 fnd = false;
833 path_selected = true;
834 np_set_best_match(path_select_list, mount_list, exact_match);
835 cflags = default_cflags;
836
837 break;
838 case 'M': /* display mountpoint */
839 display_mntp = true;
840 break;
841 case 'C':
842 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
843 if (path_selected == false) {
844 struct parameter_list *path;
845 for (me = mount_list; me; me = me->me_next) {
846 if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
847 path = np_add_parameter(&path_select_list, me->me_mountdir);
848 path->best_match = me;
849 path->group = group;
850 set_all_thresholds(path);
851 }
852 }
853 warn_freespace_units = NULL;
854 crit_freespace_units = NULL;
855 warn_usedspace_units = NULL;
856 crit_usedspace_units = NULL;
857 warn_freespace_percent = NULL;
858 crit_freespace_percent = NULL;
859 warn_usedspace_percent = NULL;
860 crit_usedspace_percent = NULL;
861 warn_usedinodes_percent = NULL;
862 crit_usedinodes_percent = NULL;
863 warn_freeinodes_percent = NULL;
864 crit_freeinodes_percent = NULL;
865
866 path_selected = false;
867 group = NULL;
868 break;
869 case 'V': /* version */
870 print_revision (progname, NP_VERSION);
871 exit (STATE_UNKNOWN);
872 case 'h': /* help */
873 print_help ();
874 exit (STATE_UNKNOWN);
875 case '?': /* help */
876 usage (_("Unknown argument"));
877 }
878 }
879
880 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
881 c = optind;
882 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
883 warn_usedspace_percent = argv[c++];
884
885 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
886 crit_usedspace_percent = argv[c++];
887
888 if (argc > c) {
889 se = np_add_parameter(&path_select_list, strdup(argv[c++]));
890 path_selected = true;
891 set_all_thresholds(se);
892 }
893
894 if (units == NULL) {
895 units = strdup ("MiB");
896 mult = (uintmax_t)1024 * 1024;
897 }
898
899 return true;
900}
901 509
510 case 'W': /* warning inode threshold */
511 if (*optarg == '@') {
512 warn_freeinodes_percent = optarg;
513 } else {
514 xasprintf(&warn_freeinodes_percent, "@%s", optarg);
515 }
516 break;
517 case 'K': /* critical inode threshold */
518 if (*optarg == '@') {
519 crit_freeinodes_percent = optarg;
520 } else {
521 xasprintf(&crit_freeinodes_percent, "@%s", optarg);
522 }
523 break;
524 case 'u':
525 if (!strcasecmp(optarg, "bytes")) {
526 unit = Bytes_Factor;
527 } else if (!strcmp(optarg, "KiB")) {
528 unit = KibiBytes_factor;
529 } else if (!strcmp(optarg, "kB")) {
530 unit = KiloBytes_factor;
531 } else if (!strcmp(optarg, "MiB")) {
532 unit = MebiBytes_factor;
533 } else if (!strcmp(optarg, "MB")) {
534 unit = MegaBytes_factor;
535 } else if (!strcmp(optarg, "GiB")) {
536 unit = GibiBytes_factor;
537 } else if (!strcmp(optarg, "GB")) {
538 unit = GigaBytes_factor;
539 } else if (!strcmp(optarg, "TiB")) {
540 unit = TebiBytes_factor;
541 } else if (!strcmp(optarg, "TB")) {
542 unit = TeraBytes_factor;
543 } else if (!strcmp(optarg, "PiB")) {
544 unit = PebiBytes_factor;
545 } else if (!strcmp(optarg, "PB")) {
546 unit = PetaBytes_factor;
547 } else {
548 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
549 }
550 break;
551 case 'k':
552 unit = KibiBytes_factor;
553 break;
554 case 'm':
555 unit = MebiBytes_factor;
556 break;
557 case display_unit_index:
558 if (!strcasecmp(optarg, "bytes")) {
559 result.config.display_unit = Bytes;
560 } else if (!strcmp(optarg, "KiB")) {
561 result.config.display_unit = KibiBytes;
562 } else if (!strcmp(optarg, "kB")) {
563 result.config.display_unit = KiloBytes;
564 } else if (!strcmp(optarg, "MiB")) {
565 result.config.display_unit = MebiBytes;
566 } else if (!strcmp(optarg, "MB")) {
567 result.config.display_unit = MegaBytes;
568 } else if (!strcmp(optarg, "GiB")) {
569 result.config.display_unit = GibiBytes;
570 } else if (!strcmp(optarg, "GB")) {
571 result.config.display_unit = GigaBytes;
572 } else if (!strcmp(optarg, "TiB")) {
573 result.config.display_unit = TebiBytes;
574 } else if (!strcmp(optarg, "TB")) {
575 result.config.display_unit = TeraBytes;
576 } else if (!strcmp(optarg, "PiB")) {
577 result.config.display_unit = PebiBytes;
578 } else if (!strcmp(optarg, "PB")) {
579 result.config.display_unit = PetaBytes;
580 } else {
581 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
582 }
583 break;
584 case 'L':
585 result.config.stat_remote_fs = true;
586 /* fallthrough */
587 case 'l':
588 result.config.show_local_fs = true;
589 break;
590 case 'P':
591 result.config.display_inodes_perfdata = true;
592 break;
593 case 'p': /* select path */ {
594 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
595 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
596 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
597 _("Must set a threshold value before using -p\n"));
598 }
902 599
600 /* add parameter if not found. overwrite thresholds if path has already been added */
601 parameter_list_elem *search_entry;
602 if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) {
603 search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg);
903 604
904void 605 // struct stat stat_buf = {};
905print_path (const char *mypath) 606 // if (stat(optarg, &stat_buf) && result.config.ignore_missing) {
906{ 607 // result.config.path_ignored = true;
907 if (mypath == NULL) 608 // break;
908 printf ("\n"); 609 // }
909 else 610 }
910 printf (_(" for %s\n"), mypath); 611 search_entry->group = group;
911} 612 set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units,
613 warn_freespace_percent, crit_freespace_percent,
614
615 warn_freeinodes_percent, crit_freeinodes_percent);
616
617 /* With autofs, it is required to stat() the path before re-populating the mount_list */
618 // if (!stat_path(se, result.config.ignore_missing)) {
619 // break;
620 // }
621 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
622 result.config.exact_match);
623
624 path_selected = true;
625 } break;
626 case 'x': /* exclude path or partition */
627 np_add_name(&result.config.device_path_exclude_list, optarg);
628 break;
629 case 'X': /* exclude file system type */ {
630 int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED);
631 if (err != 0) {
632 char errbuf[MAX_INPUT_BUFFER];
633 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
634 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
635 _("Could not compile regular expression"), errbuf);
636 }
637 break;
638 case 'N': /* include file system type */
639 err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED);
640 if (err != 0) {
641 char errbuf[MAX_INPUT_BUFFER];
642 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
643 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
644 _("Could not compile regular expression"), errbuf);
645 }
646 } break;
647 case 'v': /* verbose */
648 verbose++;
649 break;
650 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
651 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
652 result.config.erronly = true;
653 break;
654 case 'e':
655 result.config.erronly = true;
656 break;
657 case 'E':
658 if (path_selected) {
659 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
660 _("Must set -E before selecting paths\n"));
661 }
662 result.config.exact_match = true;
663 break;
664 case 'f':
665 result.config.freespace_ignore_reserved = true;
666 break;
667 case 'g':
668 if (path_selected) {
669 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
670 _("Must set group value before selecting paths\n"));
671 }
672 group = optarg;
673 break;
674 case 'I':
675 cflags |= REG_ICASE;
676 // Intentional fallthrough
677 case 'i': {
678 if (!path_selected) {
679 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"),
680 _("Paths need to be selected before using -i/-I. Use -A to select all paths "
681 "explicitly"));
682 }
683 regex_t regex;
684 int err = regcomp(&regex, optarg, cflags);
685 if (err != 0) {
686 char errbuf[MAX_INPUT_BUFFER];
687 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
688 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
689 _("Could not compile regular expression"), errbuf);
690 }
912 691
692 for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) {
693 if (elem->best_match) {
694 if (np_regex_match_mount_entry(elem->best_match, &regex)) {
913 695
914void 696 if (verbose >= 3) {
915set_all_thresholds (struct parameter_list *path) 697 printf("ignoring %s matching regex\n", elem->name);
916{ 698 }
917 if (path->freespace_units != NULL) free(path->freespace_units);
918 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
919 if (path->freespace_percent != NULL) free (path->freespace_percent);
920 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
921 if (path->usedspace_units != NULL) free (path->usedspace_units);
922 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
923 if (path->usedspace_percent != NULL) free (path->usedspace_percent);
924 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
925 if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
926 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
927 if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
928 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
929}
930 699
931void 700 elem = mp_int_fs_list_del(&result.config.path_select_list, elem);
932print_help (void) 701 continue;
933{ 702 }
934 print_revision (progname, NP_VERSION); 703 }
935
936 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
937 printf (COPYRIGHT, copyright, email);
938
939 printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
940 printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
941
942 printf ("\n\n");
943
944 print_usage ();
945
946 printf (UT_HELP_VRSN);
947 printf (UT_EXTRA_OPTS);
948
949 printf (" %s\n", "-w, --warning=INTEGER");
950 printf (" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
951 printf (" %s\n", "-w, --warning=PERCENT%");
952 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
953 printf (" %s\n", "-c, --critical=INTEGER");
954 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
955 printf (" %s\n", "-c, --critical=PERCENT%");
956 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of disk space is free"));
957 printf (" %s\n", "-W, --iwarning=PERCENT%");
958 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
959 printf (" %s\n", "-K, --icritical=PERCENT%");
960 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
961 printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
962 printf (" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
963 printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
964 printf (" %s\n", _("Ignore device (only works if -p unspecified)"));
965 printf (" %s\n", "-C, --clear");
966 printf (" %s\n", _("Clear thresholds"));
967 printf (" %s\n", "-E, --exact-match");
968 printf (" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
969 printf (" %s\n", "-e, --errors-only");
970 printf (" %s\n", _("Display only devices/mountpoints with errors"));
971 printf (" %s\n", "-f, --freespace-ignore-reserved");
972 printf (" %s\n", _("Don't account root-reserved blocks into freespace in perfdata"));
973 printf (" %s\n", "-P, --iperfdata");
974 printf (" %s\n", _("Display inode usage in perfdata"));
975 printf (" %s\n", "-g, --group=NAME");
976 printf (" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
977 printf (" %s\n", "-k, --kilobytes");
978 printf (" %s\n", _("Same as '--units kB'"));
979 printf (" %s\n", "-l, --local");
980 printf (" %s\n", _("Only check local filesystems"));
981 printf (" %s\n", "-L, --stat-remote-fs");
982 printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
983 printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
984 printf (" %s\n", "-M, --mountpoint");
985 printf (" %s\n", _("Display the (block) device instead of the mount point"));
986 printf (" %s\n", "-m, --megabytes");
987 printf (" %s\n", _("Same as '--units MB'"));
988 printf (" %s\n", "-A, --all");
989 printf (" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
990 printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
991 printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
992 printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
993 printf (" %s\n", _("Regular expression for path or partition (may be repeated)"));
994 printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
995 printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
996 printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
997 printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
998 printf (" %s\n", "-n, --ignore-missing");
999 printf (" %s\n", _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
1000 printf (" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
1001 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1002 printf (" %s\n", "-u, --units=STRING");
1003 printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
1004 printf (UT_VERBOSE);
1005 printf (" %s\n", "-X, --exclude-type=TYPE_REGEX");
1006 printf (" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
1007 printf (" %s\n", "-N, --include-type=TYPE_REGEX");
1008 printf (" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)"));
1009
1010 printf ("\n");
1011 printf ("%s\n", _("General usage hints:"));
1012 printf (" %s\n", _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
1013 printf (" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
1014 printf (" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} {thresholds b} ...\""));
1015
1016
1017
1018 printf ("\n");
1019 printf ("%s\n", _("Examples:"));
1020 printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
1021 printf (" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
1022 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
1023 printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
1024 printf (" %s\n\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
1025 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
1026 printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
1027
1028 printf (UT_SUPPORT);
1029}
1030 704
705 elem = mp_int_fs_list_get_next(elem);
706 }
1031 707
708 cflags = default_cflags;
709 } break;
710 case 'n':
711 result.config.ignore_missing = true;
712 break;
713 case 'A':
714 optarg = strdup(".*");
715 // Intentional fallthrough
716 case 'R':
717 cflags |= REG_ICASE;
718 // Intentional fallthrough
719 case 'r': {
720 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
721 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
722 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
723 _("Must set a threshold value before using -r/-R/-A "
724 "(--ereg-path/--eregi-path/--all)\n"));
725 }
1032 726
1033void 727 regex_t regex;
1034print_usage (void) 728 int err = regcomp(&regex, optarg, cflags);
1035{ 729 if (err != 0) {
1036 printf ("%s\n", _("Usage:")); 730 char errbuf[MAX_INPUT_BUFFER];
1037 printf (" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c absolute_limit|-c percentage_limit%% | -K inode_percentage_limit } {-p path | -x device}\n", progname); 731 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
1038 printf ("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); 732 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
1039 printf ("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); 733 _("Could not compile regular expression"), errbuf);
734 }
735
736 bool found = false;
737 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
738 if (np_regex_match_mount_entry(me, &regex)) {
739 found = true;
740 if (verbose >= 3) {
741 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir,
742 optarg);
743 }
744
745 /* add parameter if not found. overwrite thresholds if path has already been
746 * added */
747 parameter_list_elem *se = NULL;
748 if (!(se = mp_int_fs_list_find(result.config.path_select_list,
749 me->me_mountdir))) {
750 se =
751 mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
752 }
753 se->group = group;
754 set_all_thresholds(se, warn_freespace_units, crit_freespace_units,
755 warn_freespace_percent, crit_freespace_percent,
756 warn_freeinodes_percent, crit_freeinodes_percent);
757 }
758 }
759
760 if (!found) {
761 if (result.config.ignore_missing) {
762 result.config.path_ignored = true;
763 path_selected = true;
764 break;
765 }
766
767 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
768 _("Regular expression did not match any path or disk"), optarg);
769 }
770
771 path_selected = true;
772 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
773 result.config.exact_match);
774 cflags = default_cflags;
775
776 } break;
777 case 'M': /* display mountpoint */
778 result.config.display_mntp = true;
779 break;
780 case 'C': {
781 /* add all mount entries to path_select list if no partitions have been explicitly
782 * defined using -p */
783 if (!path_selected) {
784 parameter_list_elem *path;
785 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
786 if (!(path = mp_int_fs_list_find(result.config.path_select_list,
787 me->me_mountdir))) {
788 path =
789 mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
790 }
791 path->best_match = me;
792 path->group = group;
793 set_all_thresholds(path, warn_freespace_units, crit_freespace_units,
794 warn_freespace_percent, crit_freespace_percent,
795 warn_freeinodes_percent, crit_freeinodes_percent);
796 }
797 }
798
799 warn_freespace_units = NULL;
800 crit_freespace_units = NULL;
801 warn_freespace_percent = NULL;
802 crit_freespace_percent = NULL;
803 warn_freeinodes_percent = NULL;
804 crit_freeinodes_percent = NULL;
805
806 path_selected = false;
807 group = NULL;
808 } break;
809 case 'V': /* version */
810 print_revision(progname, NP_VERSION);
811 exit(STATE_UNKNOWN);
812 case 'h': /* help */
813 print_help();
814 exit(STATE_UNKNOWN);
815 case '?': /* help */
816 usage(_("Unknown argument"));
817 case output_format_index: {
818 parsed_output_format parser = mp_parse_output_format(optarg);
819 if (!parser.parsing_success) {
820 // TODO List all available formats here, maybe add anothoer usage function
821 printf("Invalid output format: %s\n", optarg);
822 exit(STATE_UNKNOWN);
823 }
824
825 result.config.output_format_is_set = true;
826 result.config.output_format = parser.output_format;
827 break;
828 }
829 }
830 }
831
832 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
833 int index = optind;
834
835 if (argc > index && is_intnonneg(argv[index])) {
836 if (verbose > 0) {
837 printf("Got an positional warn threshold: %s\n", argv[index]);
838 }
839 char *range = argv[index++];
840 mp_range_parsed tmp = mp_parse_range_string(range);
841 if (tmp.error != MP_PARSING_SUCCESS) {
842 die(STATE_UNKNOWN, "failed to parse warning threshold");
843 }
844
845 mp_range tmp_range = tmp.range;
846 // Invert range to use it for free instead of used
847 // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range;
848
849 warn_freespace_percent = mp_range_to_string(tmp_range);
850
851 if (verbose > 0) {
852 printf("Positional warning threshold transformed to: %s\n", warn_freespace_percent);
853 }
854 }
855
856 if (argc > index && is_intnonneg(argv[index])) {
857 if (verbose > 0) {
858 printf("Got an positional crit threshold: %s\n", argv[index]);
859 }
860 char *range = argv[index++];
861 mp_range_parsed tmp = mp_parse_range_string(range);
862 if (tmp.error != MP_PARSING_SUCCESS) {
863 die(STATE_UNKNOWN, "failed to parse warning threshold");
864 }
865
866 mp_range tmp_range = tmp.range;
867 // Invert range to use it for free instead of used
868 // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range;
869
870 crit_freespace_percent = mp_range_to_string(tmp_range);
871
872 if (verbose > 0) {
873 printf("Positional critical threshold transformed to: %s\n", crit_freespace_percent);
874 }
875 }
876
877 if (argc > index) {
878 if (verbose > 0) {
879 printf("Got an positional filesystem: %s\n", argv[index]);
880 }
881 struct parameter_list *se =
882 mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++]));
883 path_selected = true;
884 set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent,
885 crit_freespace_percent, warn_freeinodes_percent,
886 crit_freeinodes_percent);
887 }
888
889 // If a list of paths has not been explicitly selected, find entire
890 // mount list and create list of paths
891 if (!path_selected && !result.config.path_ignored) {
892 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
893 if (me->me_dummy != 0) {
894 // just do not add dummy filesystems
895 continue;
896 }
897
898 parameter_list_elem *path = NULL;
899 if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) {
900 path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
901 }
902 path->best_match = me;
903 path->group = group;
904 set_all_thresholds(path, warn_freespace_units, crit_freespace_units,
905 warn_freespace_percent, crit_freespace_percent,
906 warn_freeinodes_percent, crit_freeinodes_percent);
907 }
908 }
909
910 // Set thresholds to the appropriate unit
911 for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp;
912 tmp = mp_int_fs_list_get_next(tmp)) {
913
914 mp_perfdata_value factor = mp_create_pd_value(unit);
915
916 if (tmp->freespace_units.critical_is_set) {
917 tmp->freespace_units.critical =
918 mp_range_multiply(tmp->freespace_units.critical, factor);
919 }
920 if (tmp->freespace_units.warning_is_set) {
921 tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor);
922 }
923 }
924
925 return result;
1040} 926}
1041 927
1042bool 928void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
1043stat_path (struct parameter_list *p) 929 char *crit_freespace_units, char *warn_freespace_percent,
1044{ 930 char *crit_freespace_percent, char *warn_freeinodes_percent,
1045 /* Stat entry to check that dir exists and is accessible */ 931 char *crit_freeinodes_percent) {
1046 if (verbose >= 3) 932 mp_range_parsed tmp;
1047 printf("calling stat on %s\n", p->name); 933
1048 if (stat (p->name, &stat_buf[0])) { 934 if (warn_freespace_units) {
1049 if (verbose >= 3) 935 tmp = mp_parse_range_string(warn_freespace_units);
1050 printf("stat failed on %s\n", p->name); 936 tmp.range.start = mp_create_pd_value(0);
1051 if (ignore_missing == true) { 937 tmp.range.start_infinity = false;
1052 return false; 938 path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range);
1053 } else { 939 }
1054 printf("DISK %s - ", _("CRITICAL")); 940
1055 die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); 941 if (crit_freespace_units) {
1056 } 942 tmp = mp_parse_range_string(crit_freespace_units);
1057 } 943 tmp.range.start = mp_create_pd_value(0);
1058 return true; 944 tmp.range.start_infinity = false;
945 path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range);
946 }
947
948 if (warn_freespace_percent) {
949 tmp = mp_parse_range_string(warn_freespace_percent);
950 tmp.range.start = mp_create_pd_value(0);
951 tmp.range.start_infinity = false;
952 path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range);
953 }
954
955 if (crit_freespace_percent) {
956 tmp = mp_parse_range_string(crit_freespace_percent);
957 tmp.range.start = mp_create_pd_value(0);
958 tmp.range.start_infinity = false;
959 path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range);
960 }
961
962 if (warn_freeinodes_percent) {
963 tmp = mp_parse_range_string(warn_freeinodes_percent);
964 tmp.range.start = mp_create_pd_value(0);
965 tmp.range.start_infinity = false;
966 path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range);
967 }
968
969 if (crit_freeinodes_percent) {
970 tmp = mp_parse_range_string(crit_freeinodes_percent);
971 tmp.range.start = mp_create_pd_value(0);
972 tmp.range.start_infinity = false;
973 path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range);
974 }
1059} 975}
1060 976
977void print_help(void) {
978 print_revision(progname, NP_VERSION);
979
980 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
981 printf(COPYRIGHT, copyright, email);
982
983 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
984 printf("%s\n",
985 _("and generates an alert if free space is less than one of the threshold values"));
986
987 printf("\n\n");
988
989 print_usage();
990
991 printf(UT_HELP_VRSN);
992 printf(UT_EXTRA_OPTS);
993
994 printf(" %s\n", "-w, --warning=INTEGER");
995 printf(" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
996 printf(" %s\n", "-w, --warning=PERCENT%");
997 printf(" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
998 printf(" %s\n", "-c, --critical=INTEGER");
999 printf(" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
1000 printf(" %s\n", "-c, --critical=PERCENT%");
1001 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of disk space is free"));
1002 printf(" %s\n", "-W, --iwarning=PERCENT%");
1003 printf(" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
1004 printf(" %s\n", "-K, --icritical=PERCENT%");
1005 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
1006 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION");
1007 printf(" %s\n",
1008 _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
1009 printf(" %s\n", "-x, --exclude_device=PATH <STRING>");
1010 printf(" %s\n", _("Ignore device (only works if -p unspecified)"));
1011 printf(" %s\n", "-C, --clear");
1012 printf(" %s\n", _("Clear thresholds"));
1013 printf(" %s\n", "-E, --exact-match");
1014 printf(" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
1015 printf(" %s\n", "-e, --errors-only");
1016 printf(" %s\n", _("Display only devices/mountpoints with errors"));
1017 printf(" %s\n", "-f, --freespace-ignore-reserved");
1018 printf(" %s\n", _("Don't account root-reserved blocks into freespace in perfdata"));
1019 printf(" %s\n", "-P, --iperfdata");
1020 printf(" %s\n", _("Display inode usage in perfdata"));
1021 printf(" %s\n", "-g, --group=NAME");
1022 printf(" %s\n",
1023 _("Group paths. Thresholds apply to (free-)space of all partitions together"));
1024 printf(" %s\n", "-l, --local");
1025 printf(" %s\n", _("Only check local filesystems"));
1026 printf(" %s\n", "-L, --stat-remote-fs");
1027 printf(
1028 " %s\n",
1029 _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
1030 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
1031 printf(" %s\n", "-M, --mountpoint");
1032 printf(" %s\n", _("Display the (block) device instead of the mount point"));
1033 printf(" %s\n", "-A, --all");
1034 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
1035 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
1036 printf(" %s\n",
1037 _("Case insensitive regular expression for path/partition (may be repeated)"));
1038 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
1039 printf(" %s\n", _("Regular expression for path or partition (may be repeated)"));
1040 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
1041 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) "
1042 "(may be repeated)"));
1043 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
1044 printf(" %s\n",
1045 _("Regular expression to ignore selected path or partition (may be repeated)"));
1046 printf(" %s\n", "-n, --ignore-missing");
1047 printf(" %s\n",
1048 _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
1049 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
1050 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1051 printf(" %s\n", "-u, --units=STRING");
1052 printf(" %s\n", _("Select the unit used for the absolute value thresholds"));
1053 printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", "
1054 "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1055 printf(" %s\n", "-k, --kilobytes");
1056 printf(" %s\n", _("Same as '--units kB'"));
1057 printf(" %s\n", "--display-unit");
1058 printf(" %s\n", _("Select the unit used for in the output"));
1059 printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", "
1060 "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1061 printf(" %s\n", "-m, --megabytes");
1062 printf(" %s\n", _("Same as '--units MB'"));
1063 printf(UT_VERBOSE);
1064 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX");
1065 printf(" %s\n",
1066 _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
1067 printf(" %s\n", "-N, --include-type=TYPE_REGEX");
1068 printf(
1069 " %s\n",
1070 _("Check only filesystems where the type matches this given regex(7) (may be repeated)"));
1071 printf(UT_OUTPUT_FORMAT);
1072
1073 printf("\n");
1074 printf("%s\n", _("General usage hints:"));
1075 printf(
1076 " %s\n",
1077 _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
1078 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
1079 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} "
1080 "{thresholds b} ...\""));
1081
1082 printf("\n");
1083 printf("%s\n", _("Examples:"));
1084 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
1085 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
1086 printf(" %s\n",
1087 "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
1088 printf(
1089 " %s\n",
1090 _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
1091 printf(" %s\n\n",
1092 _("are grouped which means the freespace thresholds are applied to all disks together"));
1093 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
1094 printf(" %s\n",
1095 _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
1096
1097 printf(UT_SUPPORT);
1098}
1061 1099
1062void 1100void print_usage(void) {
1063get_stats (struct parameter_list *p, struct fs_usage *fsp) { 1101 printf("%s\n", _("Usage:"));
1064 struct parameter_list *p_list; 1102 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c "
1065 struct fs_usage tmpfsp; 1103 "absolute_limit|-c percentage_limit%% | -K "
1066 int first = 1; 1104 "inode_percentage_limit } {-p path | -x device}\n",
1105 progname);
1106 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
1107 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n");
1108}
1067 1109
1068 if (p->group == NULL) { 1110bool stat_path(parameter_list_elem *parameters, bool ignore_missing) {
1069 get_path_stats(p,fsp); 1111 /* Stat entry to check that dir exists and is accessible */
1070 } else { 1112 if (verbose >= 3) {
1071 /* find all group members */ 1113 printf("calling stat on %s\n", parameters->name);
1072 for (p_list = path_select_list; p_list; p_list=p_list->name_next) { 1114 }
1073#ifdef __CYGWIN__ 1115
1074 if (strncmp(p_list->name, "/cygdrive/", 10) != 0) 1116 struct stat stat_buf = {0};
1075 continue; 1117 if (stat(parameters->name, &stat_buf)) {
1076#endif 1118 if (verbose >= 3) {
1077 if (p_list->group && ! (strcmp(p_list->group, p->group))) { 1119 printf("stat failed on %s\n", parameters->name);
1078 if (! stat_path(p_list)) 1120 }
1079 continue; 1121 if (ignore_missing) {
1080 get_fs_usage (p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp); 1122 return false;
1081 get_path_stats(p_list, &tmpfsp); 1123 }
1082 if (verbose >= 3) 1124 printf("DISK %s - ", _("CRITICAL"));
1083 printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n", 1125 die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"),
1084 p_list->group, 1126 strerror(errno));
1085 tmpfsp.fsu_blocks, 1127 }
1086 tmpfsp.fsu_blocksize, 1128
1087 p_list->best_match->me_mountdir, 1129 return true;
1088 p_list->dused_units, 1130}
1089 p_list->dfree_units,
1090 p_list->dtotal_units,
1091 mult);
1092
1093 /* prevent counting the first FS of a group twice since its parameter_list entry
1094 * is used to carry the information of all file systems of the entire group */
1095 if (! first) {
1096 p->total += p_list->total;
1097 p->available += p_list->available;
1098 p->available_to_root += p_list->available_to_root;
1099 p->used += p_list->used;
1100
1101 p->dused_units += p_list->dused_units;
1102 p->dfree_units += p_list->dfree_units;
1103 p->dtotal_units += p_list->dtotal_units;
1104 p->inodes_total += p_list->inodes_total;
1105 p->inodes_free += p_list->inodes_free;
1106 p->inodes_free_to_root += p_list->inodes_free_to_root;
1107 p->inodes_used += p_list->inodes_used;
1108 }
1109 first = 0;
1110 }
1111 if (verbose >= 3)
1112 printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n",
1113 p->group,
1114 p->dused_units,
1115 p->dfree_units,
1116 p->dtotal_units,
1117 tmpfsp.fsu_blocksize,
1118 mult);
1119 }
1120 /* modify devname and mountdir for output */
1121 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
1122 }
1123 /* finally calculate percentages for either plain FS or summed up group */
1124 p->dused_pct = calculate_percent( p->used, p->used + p->available ); /* used + available can never be > uintmax */
1125 p->dfree_pct = 100.0 - p->dused_pct;
1126 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1127 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1128 1131
1132static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp,
1133 bool freespace_ignore_reserved) {
1134 uintmax_t available = fsp.fsu_bavail;
1135 uintmax_t available_to_root = fsp.fsu_bfree;
1136 uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree;
1137 uintmax_t total;
1138
1139 if (freespace_ignore_reserved) {
1140 /* option activated : we subtract the root-reserved space from the total */
1141 total = fsp.fsu_blocks - available_to_root + available;
1142 } else {
1143 /* default behaviour : take all the blocks into account */
1144 total = fsp.fsu_blocks;
1145 }
1146
1147 parameters.used_bytes = used * fsp.fsu_blocksize;
1148 parameters.free_bytes = available * fsp.fsu_blocksize;
1149 parameters.total_bytes = total * fsp.fsu_blocksize;
1150
1151 /* Free file nodes. Not sure the workaround is required, but in case...*/
1152 parameters.inodes_free = fsp.fsu_ffree;
1153 parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */
1154 parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree;
1155
1156 if (freespace_ignore_reserved) {
1157 /* option activated : we subtract the root-reserved inodes from the total */
1158 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */
1159 /* for others, fsp->fsu_ffree == fsp->fsu_favail */
1160 parameters.inodes_total =
1161 fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free;
1162 } else {
1163 /* default behaviour : take all the inodes into account */
1164 parameters.inodes_total = fsp.fsu_files;
1165 }
1166
1167 return parameters;
1129} 1168}
1130 1169
1131void 1170mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata,
1132get_path_stats (struct parameter_list *p, struct fs_usage *fsp) { 1171 byte_unit unit) {
1133 p->available = fsp->fsu_bavail; 1172 mp_subcheck result = mp_subcheck_init();
1134 p->available_to_root = fsp->fsu_bfree; 1173 result = mp_set_subcheck_default_state(result, STATE_UNKNOWN);
1135 p->used = fsp->fsu_blocks - fsp->fsu_bfree; 1174 xasprintf(&result.output, "%s", measurement_unit.name);
1136 if (freespace_ignore_reserved) { 1175
1137 /* option activated : we subtract the root-reserved space from the total */ 1176 if (!measurement_unit.is_group && measurement_unit.filesystem_type) {
1138 p->total = fsp->fsu_blocks - p->available_to_root + p->available; 1177 xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type);
1139 } else { 1178 }
1140 /* default behaviour : take all the blocks into account */ 1179
1141 p->total = fsp->fsu_blocks; 1180 /* Threshold comparisons */
1142 } 1181
1143 1182 // ===============================
1144 p->dused_units = p->used*fsp->fsu_blocksize/mult; 1183 // Free space absolute values test
1145 p->dfree_units = p->available*fsp->fsu_blocksize/mult; 1184 mp_subcheck freespace_bytes_sc = mp_subcheck_init();
1146 p->dtotal_units = p->total*fsp->fsu_blocksize/mult; 1185 freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK);
1147 /* Free file nodes. Not sure the workaround is required, but in case...*/ 1186
1148 p->inodes_free = fsp->fsu_ffree; 1187 if (unit != Humanized) {
1149 p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ 1188 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)",
1150 p->inodes_used = fsp->fsu_files - fsp->fsu_ffree; 1189 (uintmax_t)(measurement_unit.free_bytes / unit), get_unit_string(unit),
1151 if (freespace_ignore_reserved) { 1190 (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit));
1152 /* option activated : we subtract the root-reserved inodes from the total */ 1191 } else {
1153 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ 1192 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)",
1154 /* for others, fsp->fsu_ffree == fsp->fsu_favail */ 1193 humanize_byte_value(measurement_unit.free_bytes, false),
1155 p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free; 1194 humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false));
1156 } else { 1195 }
1157 /* default behaviour : take all the inodes into account */ 1196
1158 p->inodes_total = fsp->fsu_files; 1197 // Free space just internally for computation
1159 } 1198 mp_perfdata free_space_pd = perfdata_init();
1160 np_add_name(&seen, p->best_match->me_mountdir); 1199 free_space_pd.label = measurement_unit.name;
1200 free_space_pd.value = mp_create_pd_value(measurement_unit.free_bytes);
1201 free_space_pd =
1202 mp_pd_set_thresholds(free_space_pd, measurement_unit.freespace_bytes_thresholds);
1203 freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(free_space_pd));
1204
1205 // Used space for display
1206 mp_perfdata used_space_pd = perfdata_init();
1207 used_space_pd.label = measurement_unit.name;
1208 used_space_pd.value = mp_create_pd_value(measurement_unit.used_bytes);
1209 used_space_pd =
1210 mp_set_pd_max_value(used_space_pd, mp_create_pd_value(measurement_unit.total_bytes));
1211 used_space_pd = mp_set_pd_min_value(used_space_pd, mp_create_pd_value(0));
1212 used_space_pd.uom = "B";
1213
1214 // special case for absolute space thresholds here:
1215 // if absolute values are not set, compute the thresholds from percentage thresholds
1216 mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds;
1217 if (!temp_thlds.critical_is_set &&
1218 measurement_unit.freespace_percent_thresholds.critical_is_set) {
1219 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical;
1220
1221 if (!tmp_range.end_infinity) {
1222 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 *
1223 measurement_unit.total_bytes);
1224 }
1225
1226 if (!tmp_range.start_infinity) {
1227 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 *
1228 measurement_unit.total_bytes);
1229 }
1230 measurement_unit.freespace_bytes_thresholds =
1231 mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range);
1232 free_space_pd =
1233 mp_pd_set_thresholds(free_space_pd, measurement_unit.freespace_bytes_thresholds);
1234 }
1235
1236 if (!temp_thlds.warning_is_set &&
1237 measurement_unit.freespace_percent_thresholds.warning_is_set) {
1238 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning;
1239 if (!tmp_range.end_infinity) {
1240 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 *
1241 measurement_unit.total_bytes);
1242 }
1243 if (!tmp_range.start_infinity) {
1244 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 *
1245 measurement_unit.total_bytes);
1246 }
1247 measurement_unit.freespace_bytes_thresholds =
1248 mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range);
1249 free_space_pd =
1250 mp_pd_set_thresholds(free_space_pd, measurement_unit.freespace_bytes_thresholds);
1251 }
1252
1253 mp_thresholds thr_used_space = measurement_unit.freespace_bytes_thresholds;
1254 if (thr_used_space.critical_is_set) {
1255 thr_used_space.critical.alert_on_inside_range =
1256 !thr_used_space.critical.alert_on_inside_range;
1257 }
1258 if (thr_used_space.warning_is_set) {
1259 thr_used_space.warning.alert_on_inside_range =
1260 !thr_used_space.warning.alert_on_inside_range;
1261 }
1262 used_space_pd = mp_pd_set_thresholds(used_space_pd, thr_used_space);
1263
1264 mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space_pd);
1265 mp_add_subcheck_to_subcheck(&result, freespace_bytes_sc);
1266
1267 // ==========================
1268 // Free space percentage test
1269 mp_subcheck freespace_percent_sc = mp_subcheck_init();
1270 freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK);
1271
1272 double free_percentage =
1273 calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes);
1274 xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage);
1275
1276 // Using perfdata here just to get to the test result
1277 mp_perfdata free_space_percent_pd = perfdata_init();
1278 free_space_percent_pd.value = mp_create_pd_value(free_percentage);
1279 free_space_percent_pd =
1280 mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds);
1281
1282 freespace_percent_sc =
1283 mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd));
1284 mp_add_subcheck_to_subcheck(&result, freespace_percent_sc);
1285
1286 // ================
1287 // Free inodes test
1288 // Only ever useful if the number of inodes is static (e.g. ext4),
1289 // not when it is dynamic (e.g btrfs)
1290 // Assumption: if the total number of inodes == 0, we have such a case and just skip the test
1291 if (measurement_unit.inodes_total > 0) {
1292 mp_subcheck freeindodes_percent_sc = mp_subcheck_init();
1293 freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK);
1294
1295 double free_inode_percentage =
1296 calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total);
1297
1298 mp_perfdata inode_percentage_pd = perfdata_init();
1299 inode_percentage_pd = mp_set_pd_value(inode_percentage_pd, free_inode_percentage);
1300 inode_percentage_pd = mp_pd_set_thresholds(inode_percentage_pd,
1301 measurement_unit.freeinodes_percent_thresholds);
1302
1303 if (verbose > 0) {
1304 printf("free inode percentage computed: %g\n", free_inode_percentage);
1305 }
1306
1307 xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)",
1308 free_inode_percentage, measurement_unit.inodes_free,
1309 measurement_unit.inodes_total);
1310
1311 mp_perfdata inodes_pd = perfdata_init();
1312 xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name);
1313 inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used);
1314 inodes_pd =
1315 mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total));
1316 inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0));
1317
1318 mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds;
1319
1320 if (absolut_inode_thresholds.critical_is_set) {
1321 absolut_inode_thresholds.critical =
1322 mp_range_multiply(absolut_inode_thresholds.critical,
1323 mp_create_pd_value(measurement_unit.inodes_total / 100));
1324 }
1325 if (absolut_inode_thresholds.warning_is_set) {
1326 absolut_inode_thresholds.warning =
1327 mp_range_multiply(absolut_inode_thresholds.warning,
1328 mp_create_pd_value(measurement_unit.inodes_total / 100));
1329 }
1330
1331 inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds);
1332
1333 freeindodes_percent_sc =
1334 mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inode_percentage_pd));
1335 if (display_inodes_perfdata) {
1336 mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd);
1337 }
1338 mp_add_subcheck_to_subcheck(&result, freeindodes_percent_sc);
1339 }
1340
1341 return result;
1161} 1342}
diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c
new file mode 100644
index 00000000..0b89018d
--- /dev/null
+++ b/plugins/check_disk.d/utils_disk.c
@@ -0,0 +1,528 @@
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 &&
130 dummy_match.rm_eo == len) {
131 return true;
132 }
133 }
134
135 return false;
136}
137
138bool np_seen_name(struct name_list *list, const char *name) {
139 for (struct name_list *iterator = list; iterator; iterator = iterator->next) {
140 if (!strcmp(iterator->name, name)) {
141 return true;
142 }
143 }
144 return false;
145}
146
147bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) {
148 return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) ||
149 (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0));
150}
151
152check_disk_config check_disk_config_init() {
153 check_disk_config tmp = {
154 .erronly = false,
155 .display_mntp = false,
156 .show_local_fs = false,
157 .stat_remote_fs = false,
158 .display_inodes_perfdata = false,
159
160 .exact_match = false,
161 .freespace_ignore_reserved = false,
162
163 .ignore_missing = false,
164 .path_ignored = false,
165
166 // FS Filters
167 .fs_exclude_list = NULL,
168 .fs_include_list = NULL,
169 .device_path_exclude_list = NULL,
170
171 // Actual filesystems paths to investigate
172 .path_select_list = filesystem_list_init(),
173
174 .mount_list = NULL,
175 .seen = NULL,
176
177 .display_unit = Humanized,
178 // .unit = MebiBytes,
179
180 .output_format_is_set = false,
181 };
182 return tmp;
183}
184
185char *get_unit_string(byte_unit_enum unit) {
186 switch (unit) {
187 case Bytes:
188 return "Bytes";
189 case KibiBytes:
190 return "KiB";
191 case MebiBytes:
192 return "MiB";
193 case GibiBytes:
194 return "GiB";
195 case TebiBytes:
196 return "TiB";
197 case PebiBytes:
198 return "PiB";
199 case ExbiBytes:
200 return "EiB";
201 case KiloBytes:
202 return "KB";
203 case MegaBytes:
204 return "MB";
205 case GigaBytes:
206 return "GB";
207 case TeraBytes:
208 return "TB";
209 case PetaBytes:
210 return "PB";
211 case ExaBytes:
212 return "EB";
213 default:
214 assert(false);
215 }
216}
217
218measurement_unit measurement_unit_init() {
219 measurement_unit tmp = {
220 .name = NULL,
221 .filesystem_type = NULL,
222 .is_group = false,
223
224 .freeinodes_percent_thresholds = mp_thresholds_init(),
225 .freespace_percent_thresholds = mp_thresholds_init(),
226 .freespace_bytes_thresholds = mp_thresholds_init(),
227
228 .free_bytes = 0,
229 .used_bytes = 0,
230 .total_bytes = 0,
231
232 .inodes_total = 0,
233 .inodes_free = 0,
234 .inodes_free_to_root = 0,
235 .inodes_used = 0,
236 };
237 return tmp;
238}
239
240// Add a given element to the list, memory for the new element is freshly allocated
241// Returns a pointer to new element
242measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem) {
243 // find last element
244 measurement_unit_list *new = NULL;
245 if (list == NULL) {
246 new = calloc(1, sizeof(measurement_unit_list));
247 if (new == NULL) {
248 die(STATE_UNKNOWN, _("allocation failed"));
249 }
250 } else {
251 measurement_unit_list *list_elem = list;
252 while (list_elem->next != NULL) {
253 list_elem = list_elem->next;
254 }
255
256 new = calloc(1, sizeof(measurement_unit_list));
257 if (new == NULL) {
258 die(STATE_UNKNOWN, _("allocation failed"));
259 }
260
261 list_elem->next = new;
262 }
263
264 new->unit = elem;
265 new->next = NULL;
266 return new;
267}
268
269measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit,
270 parameter_list_elem filesystem) {
271
272 unit.free_bytes += filesystem.free_bytes;
273 unit.used_bytes += filesystem.used_bytes;
274 unit.total_bytes += filesystem.total_bytes;
275
276 unit.inodes_total += filesystem.inodes_total;
277 unit.inodes_free += filesystem.inodes_free;
278 unit.inodes_free_to_root += filesystem.inodes_free_to_root;
279 unit.inodes_used += filesystem.inodes_used;
280 return unit;
281}
282
283measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem,
284 bool display_mntp) {
285 measurement_unit result = measurement_unit_init();
286 if (!display_mntp) {
287 result.name = strdup(filesystem.best_match->me_mountdir);
288 } else {
289 result.name = strdup(filesystem.best_match->me_devname);
290 }
291
292 if (filesystem.group) {
293 result.is_group = true;
294 } else {
295 result.is_group = false;
296 if (filesystem.best_match) {
297 result.filesystem_type = filesystem.best_match->me_type;
298 }
299 }
300
301 result.freeinodes_percent_thresholds = filesystem.freeinodes_percent;
302 result.freespace_percent_thresholds = filesystem.freespace_percent;
303 result.freespace_bytes_thresholds = filesystem.freespace_units;
304 result.free_bytes = filesystem.free_bytes;
305 result.total_bytes = filesystem.total_bytes;
306 result.used_bytes = filesystem.used_bytes;
307 result.inodes_total = filesystem.inodes_total;
308 result.inodes_used = filesystem.inodes_used;
309 result.inodes_free = filesystem.inodes_free;
310 result.inodes_free_to_root = filesystem.inodes_free_to_root;
311 return result;
312}
313
314#define RANDOM_STRING_LENGTH 64
315
316char *humanize_byte_value(unsigned long long value, bool use_si_units) {
317 // Idea: A reasonable output should have at most 3 orders of magnitude
318 // before the decimal separator
319 // 353GiB is ok, 2444 GiB should be 2.386 TiB
320 char *result = calloc(RANDOM_STRING_LENGTH, sizeof(char));
321 if (result == NULL) {
322 die(STATE_UNKNOWN, _("allocation failed"));
323 }
324 const byte_unit KibiBytes_factor = 1024;
325 const byte_unit MebiBytes_factor = 1048576;
326 const byte_unit GibiBytes_factor = 1073741824;
327 const byte_unit TebiBytes_factor = 1099511627776;
328 const byte_unit PebiBytes_factor = 1125899906842624;
329 const byte_unit ExbiBytes_factor = 1152921504606846976;
330 const byte_unit KiloBytes_factor = 1000;
331 const byte_unit MegaBytes_factor = 1000000;
332 const byte_unit GigaBytes_factor = 1000000000;
333 const byte_unit TeraBytes_factor = 1000000000000;
334 const byte_unit PetaBytes_factor = 1000000000000000;
335 const byte_unit ExaBytes_factor = 1000000000000000000;
336
337 if (use_si_units) {
338 // SI units, powers of 10
339 if (value < KiloBytes_factor) {
340 snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value);
341 } else if (value < MegaBytes_factor) {
342 snprintf(result, RANDOM_STRING_LENGTH, "%llu KB", value / KiloBytes_factor);
343 } else if (value < GigaBytes_factor) {
344 snprintf(result, RANDOM_STRING_LENGTH, "%llu MB", value / MegaBytes_factor);
345 } else if (value < TeraBytes_factor) {
346 snprintf(result, RANDOM_STRING_LENGTH, "%llu GB", value / GigaBytes_factor);
347 } else if (value < PetaBytes_factor) {
348 snprintf(result, RANDOM_STRING_LENGTH, "%llu TB", value / TeraBytes_factor);
349 } else if (value < ExaBytes_factor) {
350 snprintf(result, RANDOM_STRING_LENGTH, "%llu PB", value / PetaBytes_factor);
351 } else {
352 snprintf(result, RANDOM_STRING_LENGTH, "%llu EB", value / ExaBytes_factor);
353 }
354 } else {
355 // IEC units, powers of 2 ^ 10
356 if (value < KibiBytes_factor) {
357 snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value);
358 } else if (value < MebiBytes_factor) {
359 snprintf(result, RANDOM_STRING_LENGTH, "%llu KiB", value / KibiBytes_factor);
360 } else if (value < GibiBytes_factor) {
361 snprintf(result, RANDOM_STRING_LENGTH, "%llu MiB", value / MebiBytes_factor);
362 } else if (value < TebiBytes_factor) {
363 snprintf(result, RANDOM_STRING_LENGTH, "%llu GiB", value / GibiBytes_factor);
364 } else if (value < PebiBytes_factor) {
365 snprintf(result, RANDOM_STRING_LENGTH, "%llu TiB", value / TebiBytes_factor);
366 } else if (value < ExbiBytes_factor) {
367 snprintf(result, RANDOM_STRING_LENGTH, "%llu PiB", value / PebiBytes_factor);
368 } else {
369 snprintf(result, RANDOM_STRING_LENGTH, "%llu EiB", value / ExbiBytes_factor);
370 }
371 }
372
373 return result;
374}
375
376filesystem_list filesystem_list_init() {
377 filesystem_list tmp = {
378 .length = 0,
379 .first = NULL,
380 };
381 return tmp;
382}
383
384parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name) {
385 parameter_list_elem *current = list->first;
386 parameter_list_elem *new_path = (struct parameter_list *)malloc(sizeof *new_path);
387 *new_path = parameter_list_init(name);
388
389 if (current == NULL) {
390 list->first = new_path;
391 new_path->prev = NULL;
392 list->length = 1;
393 } else {
394 while (current->next) {
395 current = current->next;
396 }
397 current->next = new_path;
398 new_path->prev = current;
399 list->length++;
400 }
401 return new_path;
402}
403
404parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name) {
405 if (list.length == 0) {
406 return NULL;
407 }
408
409 for (parameter_list_elem *temp_list = list.first; temp_list; temp_list = temp_list->next) {
410 if (!strcmp(temp_list->name, name)) {
411 return temp_list;
412 }
413 }
414
415 return NULL;
416}
417
418parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item) {
419 if (list->length == 0) {
420 return NULL;
421 }
422
423 if (item == NULL) {
424 // Got NULL for item, interpret this as "delete first element"
425 // as a kind of compatibility to the old function
426 item = list->first;
427 }
428
429 if (list->first == item) {
430 list->length--;
431
432 list->first = item->next;
433 if (list->first) {
434 list->first->prev = NULL;
435 }
436 return list->first;
437 }
438
439 // Was not the first element, continue
440 parameter_list_elem *prev = list->first;
441 parameter_list_elem *current = list->first->next;
442
443 while (current != item && current != NULL) {
444 prev = current;
445 current = current->next;
446 }
447
448 if (current == NULL) {
449 // didn't find that element ....
450 return NULL;
451 }
452
453 // remove the element
454 parameter_list_elem *next = current->next;
455 prev->next = next;
456 list->length--;
457 if (next) {
458 next->prev = prev;
459 }
460
461 if (item->name) {
462 free(item->name);
463 }
464 free(item);
465
466 return next;
467}
468
469parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current) {
470 if (!current) {
471 return NULL;
472 }
473 return current->next;
474}
475
476void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list,
477 bool exact) {
478 for (parameter_list_elem *elem = list.first; elem; elem = mp_int_fs_list_get_next(elem)) {
479 if (!elem->best_match) {
480 size_t name_len = strlen(elem->name);
481 struct mount_entry *best_match = NULL;
482
483 /* set best match if path name exactly matches a mounted device name */
484 for (struct mount_entry *mount_entry = mount_list; mount_entry;
485 mount_entry = mount_entry->me_next) {
486 if (strcmp(mount_entry->me_devname, elem->name) == 0) {
487 struct fs_usage fsp;
488 if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >=
489 0) {
490 best_match = mount_entry;
491 }
492 }
493 }
494
495 /* set best match by directory name if no match was found by devname */
496 if (!best_match) {
497 size_t best_match_len = 0;
498 for (struct mount_entry *mount_entry = mount_list; mount_entry;
499 mount_entry = mount_entry->me_next) {
500 size_t len = strlen(mount_entry->me_mountdir);
501
502 if ((!exact &&
503 (best_match_len <= len && len <= name_len &&
504 (len == 1 || strncmp(mount_entry->me_mountdir, elem->name, len) == 0))) ||
505 (exact && strcmp(mount_entry->me_mountdir, elem->name) == 0)) {
506 struct fs_usage fsp;
507
508 if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >=
509 0) {
510 best_match = mount_entry;
511 best_match_len = len;
512 }
513 }
514 }
515 }
516
517 if (best_match) {
518 elem->best_match = best_match;
519 } else {
520 elem->best_match =
521 NULL; /* Not sure why this is needed as it should be null on initialisation */
522 }
523
524 // No filesystem without a mount_entry!
525 // assert(elem->best_match != NULL);
526 }
527 }
528}
diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h
new file mode 100644
index 00000000..c96d4296
--- /dev/null
+++ b/plugins/check_disk.d/utils_disk.h
@@ -0,0 +1,160 @@
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,
145 bool exact);
146
147measurement_unit measurement_unit_init();
148measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem);
149measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit,
150 parameter_list_elem filesystem);
151measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem,
152 bool display_mntp);
153
154int search_parameter_list(parameter_list_elem *list, const char *name);
155bool np_regex_match_mount_entry(struct mount_entry *, regex_t *);
156
157char *get_unit_string(byte_unit_enum);
158check_disk_config check_disk_config_init();
159
160char *humanize_byte_value(unsigned long long value, bool use_si_units);
diff --git a/plugins/check_dns.c b/plugins/check_dns.c
index 468bc958..56f91dae 100644
--- a/plugins/check_dns.c
+++ b/plugins/check_dns.c
@@ -1,36 +1,36 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dns plugin 3 * Monitoring check_dns plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2008 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_dns plugin 10 * This file contains the check_dns plugin
11* 11 *
12* LIMITATION: nslookup on Solaris 7 can return output over 2 lines, which 12 * LIMITATION: nslookup on Solaris 7 can return output over 2 lines, which
13* will not be picked up by this plugin 13 * will not be picked up by this plugin
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_dns"; 32const char *progname = "check_dns";
33const char *copyright = "2000-2008"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
@@ -39,579 +39,634 @@ const char *email = "devel@monitoring-plugins.org";
39#include "netutils.h" 39#include "netutils.h"
40#include "runcmd.h" 40#include "runcmd.h"
41 41
42int process_arguments (int, char **); 42#include "states.h"
43int validate_arguments (void); 43#include "check_dns.d/config.h"
44int error_scan (char *, bool *); 44
45bool ip_match_cidr(const char *, const char *); 45typedef struct {
46unsigned long ip2long(const char *); 46 int errorcode;
47void print_help (void); 47 check_dns_config config;
48void print_usage (void); 48} check_dns_config_wrapper;
49 49static check_dns_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50#define ADDRESS_LENGTH 256 50static check_dns_config_wrapper validate_arguments(check_dns_config_wrapper /*config_wrapper*/);
51char query_address[ADDRESS_LENGTH] = ""; 51static mp_state_enum error_scan(char * /*input_buffer*/, bool * /*is_nxdomain*/,
52char dns_server[ADDRESS_LENGTH] = ""; 52 const char /*dns_server*/[ADDRESS_LENGTH]);
53char ptr_server[ADDRESS_LENGTH] = ""; 53static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/);
54bool verbose = false; 54static unsigned long ip2long(const char * /*src*/);
55char **expected_address = NULL; 55static void print_help(void);
56int expected_address_cnt = 0; 56void print_usage(void);
57bool expect_nxdomain = false; 57
58 58static bool verbose = false;
59bool expect_authority = false; 59
60bool all_match = false; 60static int qstrcmp(const void *p1, const void *p2) {
61thresholds *time_thresholds = NULL;
62
63static int
64qstrcmp(const void *p1, const void *p2)
65{
66 /* The actual arguments to this function are "pointers to 61 /* The actual arguments to this function are "pointers to
67 pointers to char", but strcmp() arguments are "pointers 62 pointers to char", but strcmp() arguments are "pointers
68 to char", hence the following cast plus dereference */ 63 to char", hence the following cast plus dereference */
69 return strcmp(* (char * const *) p1, * (char * const *) p2); 64 return strcmp(*(char *const *)p1, *(char *const *)p2);
70} 65}
71 66
67int main(int argc, char **argv) {
68 setlocale(LC_ALL, "");
69 bindtextdomain(PACKAGE, LOCALEDIR);
70 textdomain(PACKAGE);
72 71
73int 72 /* Set signal handling and alarm */
74main (int argc, char **argv) 73 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
75{ 74 usage_va(_("Cannot catch SIGALRM"));
76 char *command_line = NULL; 75 }
77 char input_buffer[MAX_INPUT_BUFFER]; 76
78 char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */ 77 /* Parse extra opts if any */
79 char **addresses = NULL; 78 argv = np_extra_opts(&argc, argv, progname);
80 int n_addresses = 0; 79
81 char *msg = NULL; 80 check_dns_config_wrapper tmp = process_arguments(argc, argv);
82 char *temp_buffer = NULL; 81
83 bool non_authoritative = false; 82 if (tmp.errorcode == ERROR) {
84 int result = STATE_UNKNOWN; 83 usage_va(_("Could not parse arguments"));
85 double elapsed_time; 84 }
86 long microsec; 85
87 struct timeval tv; 86 const check_dns_config config = tmp.config;
88 bool parse_address = false; /* This flag scans for Address: but only after Name: */ 87
89 output chld_out, chld_err; 88 char *command_line = NULL;
90 bool is_nxdomain = false; 89 /* get the command to run */
91 90 xasprintf(&command_line, "%s %s %s", NSLOOKUP_COMMAND, config.query_address, config.dns_server);
92 setlocale (LC_ALL, ""); 91
93 bindtextdomain (PACKAGE, LOCALEDIR); 92 struct timeval tv;
94 textdomain (PACKAGE); 93 alarm(timeout_interval);
95 94 gettimeofday(&tv, NULL);
96 /* Set signal handling and alarm */ 95
97 if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 96 if (verbose) {
98 usage_va(_("Cannot catch SIGALRM")); 97 printf("%s\n", command_line);
99 } 98 }
100 99
101 /* Parse extra opts if any */ 100 output chld_out;
102 argv=np_extra_opts (&argc, argv, progname); 101 output chld_err;
103 102 char *msg = NULL;
104 if (process_arguments (argc, argv) == ERROR) { 103 mp_state_enum result = STATE_UNKNOWN;
105 usage_va(_("Could not parse arguments")); 104 /* run the command */
106 } 105 if ((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) {
107 106 msg = (char *)_("nslookup returned an error status");
108 /* get the command to run */ 107 result = STATE_WARNING;
109 xasprintf (&command_line, "%s %s %s", NSLOOKUP_COMMAND, query_address, dns_server); 108 }
110 109
111 alarm (timeout_interval); 110 /* =====
112 gettimeofday (&tv, NULL); 111 * scan stdout, main results get retrieved here
113 112 * =====
114 if (verbose) 113 */
115 printf ("%s\n", command_line); 114 char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */
116 115 char **addresses = NULL; // All addresses parsed from stdout
117 /* run the command */ 116 size_t n_addresses = 0; // counter for retrieved addresses
118 if((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) { 117 bool non_authoritative = false;
119 msg = (char *)_("nslookup returned an error status"); 118 bool is_nxdomain = false;
120 result = STATE_WARNING; 119 bool parse_address = false; /* This flag scans for Address: but only after Name: */
121 } 120 for (size_t i = 0; i < chld_out.lines; i++) {
122 121 if (addresses == NULL) {
123 /* scan stdout */ 122 addresses = malloc(sizeof(*addresses) * 10);
124 for(size_t i = 0; i < chld_out.lines; i++) { 123 } else if (!(n_addresses % 10)) {
125 if (addresses == NULL) 124 addresses = realloc(addresses, sizeof(*addresses) * (n_addresses + 10));
126 addresses = malloc(sizeof(*addresses)*10); 125 }
127 else if (!(n_addresses % 10)) 126
128 addresses = realloc(addresses,sizeof(*addresses) * (n_addresses + 10)); 127 if (verbose) {
129 128 puts(chld_out.line[i]);
130 if (verbose) 129 }
131 puts(chld_out.line[i]); 130
132 131 if (strcasestr(chld_out.line[i], ".in-addr.arpa") ||
133 if (strcasestr (chld_out.line[i], ".in-addr.arpa") || strcasestr (chld_out.line[i], ".ip6.arpa")) { 132 strcasestr(chld_out.line[i], ".ip6.arpa")) {
134 if ((temp_buffer = strstr (chld_out.line[i], "name = "))) 133 if ((strstr(chld_out.line[i], "canonical name = ") != NULL)) {
135 addresses[n_addresses++] = strdup (temp_buffer + 7); 134 continue;
136 else { 135 }
137 msg = (char *)_("Warning plugin error"); 136 char *temp_buffer = NULL;
138 result = STATE_WARNING; 137 if ((temp_buffer = strstr(chld_out.line[i], "name = "))) {
139 } 138 addresses[n_addresses++] = strdup(temp_buffer + 7);
140 } 139 } else {
141 140 msg = (char *)_("Warning plugin error");
142 /* bug ID: 2946553 - Older versions of bind will use all available dns 141 result = STATE_WARNING;
143 servers, we have to match the one specified */ 142 }
144 if (strstr (chld_out.line[i], "Server:") && strlen(dns_server) > 0) { 143 }
145 temp_buffer = strchr (chld_out.line[i], ':'); 144
146 temp_buffer++; 145 /* bug ID: 2946553 - Older versions of bind will use all available dns
147 146 servers, we have to match the one specified */
148 /* Strip leading tabs */ 147 if (strstr(chld_out.line[i], "Server:") && strlen(config.dns_server) > 0) {
149 for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) 148 char *temp_buffer = strchr(chld_out.line[i], ':');
150 /* NOOP */; 149 if (temp_buffer == NULL) {
151 150 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Server line\n"),
152 strip(temp_buffer); 151 NSLOOKUP_COMMAND);
153 if (temp_buffer==NULL || strlen(temp_buffer)==0) { 152 }
154 die (STATE_CRITICAL, 153
155 _("DNS CRITICAL - '%s' returned empty server string\n"), 154 temp_buffer++;
156 NSLOOKUP_COMMAND); 155
157 } 156 /* Strip leading tabs */
158 157 for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) {
159 if (strcmp(temp_buffer, dns_server) != 0) { 158 /* NOOP */;
160 die (STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), dns_server); 159 }
161 } 160
162 } 161 strip(temp_buffer);
163 162 if (strlen(temp_buffer) == 0) {
164 /* the server is responding, we just got the host name... */ 163 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"),
165 if (strstr (chld_out.line[i], "Name:")) 164 NSLOOKUP_COMMAND);
166 parse_address = true; 165 }
167 else if (parse_address && (strstr (chld_out.line[i], "Address:") || 166
168 strstr (chld_out.line[i], "Addresses:"))) { 167 if (strcmp(temp_buffer, config.dns_server) != 0) {
169 temp_buffer = index (chld_out.line[i], ':'); 168 die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"),
170 temp_buffer++; 169 config.dns_server);
171 170 }
172 /* Strip leading spaces */ 171 }
173 while (*temp_buffer == ' ') 172
174 temp_buffer++; 173 /* the server is responding, we just got the host name... */
175 174 if (strstr(chld_out.line[i], "Name:")) {
176 strip(temp_buffer); 175 parse_address = true;
177 if (temp_buffer==NULL || strlen(temp_buffer)==0) { 176 } else if (parse_address && (strstr(chld_out.line[i], "Address:") ||
178 die (STATE_CRITICAL, 177 strstr(chld_out.line[i], "Addresses:"))) {
179 _("DNS CRITICAL - '%s' returned empty host name string\n"), 178 char *temp_buffer = strchr(chld_out.line[i], ':');
180 NSLOOKUP_COMMAND); 179 if (temp_buffer == NULL) {
181 } 180 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Address line\n"),
182 181 NSLOOKUP_COMMAND);
183 addresses[n_addresses++] = strdup(temp_buffer); 182 }
184 } 183
185 else if (strstr (chld_out.line[i], _("Non-authoritative answer:"))) { 184 temp_buffer++;
186 non_authoritative = true; 185
187 } 186 /* Strip leading spaces */
188 187 while (*temp_buffer == ' ') {
189 188 temp_buffer++;
190 result = error_scan (chld_out.line[i], &is_nxdomain); 189 }
191 if (result != STATE_OK) { 190
192 msg = strchr (chld_out.line[i], ':'); 191 strip(temp_buffer);
193 if(msg) msg++; 192 if (strlen(temp_buffer) == 0) {
194 break; 193 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"),
195 } 194 NSLOOKUP_COMMAND);
196 } 195 }
197 196
198 /* scan stderr */ 197 addresses[n_addresses++] = strdup(temp_buffer);
199 for(size_t i = 0; i < chld_err.lines; i++) { 198 } else if (strstr(chld_out.line[i], _("Non-authoritative answer:"))) {
200 if (verbose) 199 non_authoritative = true;
201 puts(chld_err.line[i]); 200 }
202 201
203 if (error_scan (chld_err.line[i], &is_nxdomain) != STATE_OK) { 202 result = error_scan(chld_out.line[i], &is_nxdomain, config.dns_server);
204 result = max_state (result, error_scan (chld_err.line[i], &is_nxdomain)); 203 if (result != STATE_OK) {
205 msg = strchr(input_buffer, ':'); 204 msg = strchr(chld_out.line[i], ':');
206 if(msg) 205 if (msg) {
207 msg++; 206 msg++;
208 else 207 }
209 msg = input_buffer; 208 break;
210 } 209 }
211 } 210 }
212 211
213 if (is_nxdomain && !expect_nxdomain) { 212 char input_buffer[MAX_INPUT_BUFFER];
214 die (STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), query_address); 213 /* scan stderr */
215 } 214 for (size_t i = 0; i < chld_err.lines; i++) {
216 215 if (verbose) {
217 if (addresses) { 216 puts(chld_err.line[i]);
218 int i,slen; 217 }
219 char *adrp; 218
220 qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp); 219 if (error_scan(chld_err.line[i], &is_nxdomain, config.dns_server) != STATE_OK) {
221 for(i=0, slen=1; i < n_addresses; i++) { 220 result =
222 slen += strlen(addresses[i])+1; 221 max_state(result, error_scan(chld_err.line[i], &is_nxdomain, config.dns_server));
223 } 222 msg = strchr(input_buffer, ':');
224 adrp = address = malloc(slen); 223 if (msg) {
225 for(i=0; i < n_addresses; i++) { 224 msg++;
226 if (i) *adrp++ = ','; 225 } else {
227 strcpy(adrp, addresses[i]); 226 msg = input_buffer;
228 adrp += strlen(addresses[i]); 227 }
229 } 228 }
230 *adrp = 0; 229 }
231 } else 230
232 die (STATE_CRITICAL, 231 if (is_nxdomain && !config.expect_nxdomain) {
233 _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), 232 die(STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), config.query_address);
234 NSLOOKUP_COMMAND); 233 }
235 234
236 /* compare to expected address */ 235 if (addresses) {
237 if (result == STATE_OK && expected_address_cnt > 0) { 236 size_t slen = 1;
238 result = STATE_CRITICAL; 237 char *adrp = NULL;
239 temp_buffer = ""; 238 qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp);
240 unsigned long expect_match = (1 << expected_address_cnt) - 1; 239 for (size_t i = 0; i < n_addresses; i++) {
241 unsigned long addr_match = (1 << n_addresses) - 1; 240 slen += strlen(addresses[i]) + 1;
242 241 }
243 for (int i=0; i<expected_address_cnt; i++) { 242
244 int j; 243 // Temporary pointer adrp gets moved, address stays on the beginning
245 /* check if we get a match on 'raw' ip or cidr */ 244 adrp = address = malloc(slen);
246 for (j=0; j<n_addresses; j++) { 245 for (size_t i = 0; i < n_addresses; i++) {
247 if ( strcmp(addresses[j], expected_address[i]) == 0 246 if (i) {
248 || ip_match_cidr(addresses[j], expected_address[i]) ) { 247 *adrp++ = ',';
249 result = STATE_OK; 248 }
250 addr_match &= ~(1 << j); 249 strcpy(adrp, addresses[i]);
251 expect_match &= ~(1 << i); 250 adrp += strlen(addresses[i]);
252 } 251 }
253 } 252 *adrp = 0;
254 253 } else {
255 /* prepare an error string */ 254 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"),
256 xasprintf(&temp_buffer, "%s%s; ", temp_buffer, expected_address[i]); 255 NSLOOKUP_COMMAND);
257 } 256 }
258 /* check if expected_address must cover all in addresses and none may be missing */ 257
259 if (all_match && (expect_match != 0 || addr_match != 0)) 258 /* compare to expected address */
260 result = STATE_CRITICAL; 259 if (result == STATE_OK && config.expected_address_cnt > 0) {
261 if (result == STATE_CRITICAL) { 260 result = STATE_CRITICAL;
262 /* Strip off last semicolon... */ 261 char *temp_buffer = "";
263 temp_buffer[strlen(temp_buffer)-2] = '\0'; 262 unsigned long expect_match = (1 << config.expected_address_cnt) - 1;
264 xasprintf(&msg, _("expected '%s' but got '%s'"), temp_buffer, address); 263 unsigned long addr_match = (1 << n_addresses) - 1;
265 } 264
266 } 265 for (size_t i = 0; i < config.expected_address_cnt; i++) {
267 266 /* check if we get a match on 'raw' ip or cidr */
268 if (expect_nxdomain) { 267 for (size_t j = 0; j < n_addresses; j++) {
269 if (!is_nxdomain) { 268 if (strcmp(addresses[j], config.expected_address[i]) == 0 ||
270 result = STATE_CRITICAL; 269 ip_match_cidr(addresses[j], config.expected_address[i])) {
271 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), query_address, address); 270 result = STATE_OK;
272 } else { 271 addr_match &= ~(1 << j);
273 if (address != NULL) free(address); 272 expect_match &= ~(1 << i);
274 address = "NXDOMAIN"; 273 }
275 } 274 }
276 } 275
277 276 /* prepare an error string */
278 /* check if authoritative */ 277 xasprintf(&temp_buffer, "%s%s; ", temp_buffer, config.expected_address[i]);
279 if (result == STATE_OK && expect_authority && non_authoritative) { 278 }
280 result = STATE_CRITICAL; 279 /* check if expected_address must cover all in addresses and none may be missing */
281 xasprintf(&msg, _("server %s is not authoritative for %s"), dns_server, query_address); 280 if (config.all_match && (expect_match != 0 || addr_match != 0)) {
282 } 281 result = STATE_CRITICAL;
283 282 }
284 microsec = deltime (tv); 283 if (result == STATE_CRITICAL) {
285 elapsed_time = (double)microsec / 1.0e6; 284 /* Strip off last semicolon... */
286 285 temp_buffer[strlen(temp_buffer) - 2] = '\0';
287 if (result == STATE_OK) { 286 xasprintf(&msg, _("expected '%s' but got '%s'"), temp_buffer, address);
288 result = get_status(elapsed_time, time_thresholds); 287 }
289 if (result == STATE_OK) { 288 }
290 printf ("DNS %s: ", _("OK")); 289
291 } else if (result == STATE_WARNING) { 290 if (config.expect_nxdomain) {
292 printf ("DNS %s: ", _("WARNING")); 291 if (!is_nxdomain) {
293 } else if (result == STATE_CRITICAL) { 292 result = STATE_CRITICAL;
294 printf ("DNS %s: ", _("CRITICAL")); 293 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), config.query_address,
295 } 294 address);
296 printf (ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), elapsed_time); 295 } else {
297 printf (_(". %s returns %s"), query_address, address); 296 if (address != NULL) {
298 if ((time_thresholds->warning != NULL) && (time_thresholds->critical != NULL)) { 297 free(address);
299 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", 298 }
300 true, time_thresholds->warning->end, 299 address = "NXDOMAIN";
301 true, time_thresholds->critical->end, 300 }
302 true, 0, false, 0)); 301 }
303 } else if ((time_thresholds->warning == NULL) && (time_thresholds->critical != NULL)) { 302
304 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", 303 /* check if authoritative */
305 false, 0, 304 if (result == STATE_OK && config.expect_authority && non_authoritative) {
306 true, time_thresholds->critical->end, 305 result = STATE_CRITICAL;
307 true, 0, false, 0)); 306 xasprintf(&msg, _("server %s is not authoritative for %s"), config.dns_server,
308 } else if ((time_thresholds->warning != NULL) && (time_thresholds->critical == NULL)) { 307 config.query_address);
309 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", 308 }
310 true, time_thresholds->warning->end, 309
311 false, 0, 310 long microsec = deltime(tv);
312 true, 0, false, 0)); 311 double elapsed_time = (double)microsec / 1.0e6;
313 } else 312
314 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0)); 313 if (result == STATE_OK) {
315 } 314 result = get_status(elapsed_time, config.time_thresholds);
316 else if (result == STATE_WARNING) 315 if (result == STATE_OK) {
317 printf (_("DNS WARNING - %s\n"), 316 printf("DNS %s: ", _("OK"));
318 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg); 317 } else if (result == STATE_WARNING) {
319 else if (result == STATE_CRITICAL) 318 printf("DNS %s: ", _("WARNING"));
320 printf (_("DNS CRITICAL - %s\n"), 319 } else if (result == STATE_CRITICAL) {
321 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg); 320 printf("DNS %s: ", _("CRITICAL"));
322 else 321 }
323 printf (_("DNS UNKNOWN - %s\n"), 322 printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time),
324 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg); 323 elapsed_time);
325 324 printf(_(". %s returns %s"), config.query_address, address);
326 return result; 325 if ((config.time_thresholds->warning != NULL) &&
326 (config.time_thresholds->critical != NULL)) {
327 printf("|%s\n",
328 fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end,
329 true, config.time_thresholds->critical->end, true, 0, false, 0));
330 } else if ((config.time_thresholds->warning == NULL) &&
331 (config.time_thresholds->critical != NULL)) {
332 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true,
333 config.time_thresholds->critical->end, true, 0, false, 0));
334 } else if ((config.time_thresholds->warning != NULL) &&
335 (config.time_thresholds->critical == NULL)) {
336 printf("|%s\n",
337 fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end,
338 false, 0, true, 0, false, 0));
339 } else {
340 printf("|%s\n",
341 fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0));
342 }
343 } else if (result == STATE_WARNING) {
344 printf(_("DNS WARNING - %s\n"),
345 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
346 } else if (result == STATE_CRITICAL) {
347 printf(_("DNS CRITICAL - %s\n"),
348 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
349 } else {
350 printf(_("DNS UNKNOWN - %s\n"),
351 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
352 }
353
354 exit(result);
327} 355}
328 356
329bool ip_match_cidr(const char *addr, const char *cidr_ro) { 357bool ip_match_cidr(const char *addr, const char *cidr_ro) {
330 char *subnet, *mask_c, *cidr = strdup(cidr_ro); 358 char *subnet;
331 int mask; 359 char *mask_c;
332 subnet = strtok(cidr, "/"); 360 char *cidr = strdup(cidr_ro);
333 mask_c = strtok(NULL, "\0"); 361 int mask;
334 if (!subnet || !mask_c) { 362 subnet = strtok(cidr, "/");
335 return false; 363 mask_c = strtok(NULL, "\0");
364 if (!subnet || !mask_c) {
365 return false;
336 } 366 }
337 mask = atoi(mask_c); 367 mask = atoi(mask_c);
338 368
339 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ 369 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */
340 return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) << (32 - mask); 370 return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask))
371 << (32 - mask);
341} 372}
342 373
343unsigned long 374unsigned long ip2long(const char *src) {
344ip2long(const char* src) { 375 unsigned long ip[4];
345 unsigned long ip[4]; 376 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */
346 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ 377 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 &&
347 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", 378 ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && ip[3] < 256)
348 &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && 379 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]
349 ip[0] < 256 && ip[1] < 256 && 380 : 0;
350 ip[2] < 256 && ip[3] < 256)
351 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]
352 : 0;
353} 381}
354 382
355int 383mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain,
356error_scan (char *input_buffer, bool *is_nxdomain) 384 const char dns_server[ADDRESS_LENGTH]) {
357{
358
359 const int nxdomain = strstr (input_buffer, "Non-existent") ||
360 strstr (input_buffer, "** server can't find") ||
361 strstr (input_buffer, "** Can't find") ||
362 strstr (input_buffer, "NXDOMAIN");
363 if (nxdomain) *is_nxdomain = true;
364
365 /* the DNS lookup timed out */
366 if (strstr (input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) ||
367 strstr (input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) ||
368 strstr (input_buffer, _("the `-sil[ent]' option to prevent this message from appearing.")))
369 return STATE_OK;
370
371 /* DNS server is not running... */
372 else if (strstr (input_buffer, "No response from server"))
373 die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
374 else if (strstr (input_buffer, "no servers could be reached"))
375 die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
376
377 /* Host name is valid, but server doesn't have records... */
378 else if (strstr (input_buffer, "No records"))
379 die (STATE_CRITICAL, _("DNS %s has no records\n"), dns_server);
380
381 /* Connection was refused */
382 else if (strstr (input_buffer, "Connection refused") ||
383 strstr (input_buffer, "Couldn't find server") ||
384 strstr (input_buffer, "Refused") ||
385 (strstr (input_buffer, "** server can't find") &&
386 strstr (input_buffer, ": REFUSED")))
387 die (STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server);
388
389 /* Query refused (usually by an ACL in the namserver) */
390 else if (strstr (input_buffer, "Query refused"))
391 die (STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server);
392
393 /* No information (e.g. nameserver IP has two PTR records) */
394 else if (strstr (input_buffer, "No information"))
395 die (STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server);
396
397 /* Network is unreachable */
398 else if (strstr (input_buffer, "Network is unreachable"))
399 die (STATE_CRITICAL, _("Network is unreachable\n"));
400
401 /* Internal server failure */
402 else if (strstr (input_buffer, "Server failure"))
403 die (STATE_CRITICAL, _("DNS failure for %s\n"), dns_server);
404
405 /* Request error or the DNS lookup timed out */
406 else if (strstr (input_buffer, "Format error") ||
407 strstr (input_buffer, "Timed out"))
408 return STATE_WARNING;
409
410 return STATE_OK;
411 385
412} 386 const int nxdomain = strstr(input_buffer, "Non-existent") ||
387 strstr(input_buffer, "** server can't find") ||
388 strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN");
389 if (nxdomain) {
390 *is_nxdomain = true;
391 }
413 392
393 /* the DNS lookup timed out */
394 if (strstr(input_buffer,
395 _("Note: nslookup is deprecated and may be removed from future releases.")) ||
396 strstr(input_buffer,
397 _("Consider using the `dig' or `host' programs instead. Run nslookup with")) ||
398 strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing."))) {
399 return STATE_OK;
400 }
414 401
415/* process command-line arguments */ 402 /* DNS server is not running... */
416int 403 else if (strstr(input_buffer, "No response from server")) {
417process_arguments (int argc, char **argv) 404 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
418{ 405 } else if (strstr(input_buffer, "no servers could be reached")) {
419 int c; 406 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
420 char *warning = NULL; 407 }
421 char *critical = NULL; 408
422 409 /* Host name is valid, but server doesn't have records... */
423 int opt_index = 0; 410 else if (strstr(input_buffer, "No records")) {
424 static struct option long_opts[] = { 411 die(STATE_CRITICAL, _("DNS %s has no records\n"), dns_server);
425 {"help", no_argument, 0, 'h'}, 412 }
426 {"version", no_argument, 0, 'V'}, 413
427 {"verbose", no_argument, 0, 'v'}, 414 /* Connection was refused */
428 {"timeout", required_argument, 0, 't'}, 415 else if (strstr(input_buffer, "Connection refused") ||
429 {"hostname", required_argument, 0, 'H'}, 416 strstr(input_buffer, "Couldn't find server") || strstr(input_buffer, "Refused") ||
430 {"server", required_argument, 0, 's'}, 417 (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) {
431 {"reverse-server", required_argument, 0, 'r'}, 418 die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server);
432 {"expected-address", required_argument, 0, 'a'}, 419 }
433 {"expect-nxdomain", no_argument, 0, 'n'}, 420
434 {"expect-authority", no_argument, 0, 'A'}, 421 /* Query refused (usually by an ACL in the namserver) */
435 {"all", no_argument, 0, 'L'}, 422 else if (strstr(input_buffer, "Query refused")) {
436 {"warning", required_argument, 0, 'w'}, 423 die(STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server);
437 {"critical", required_argument, 0, 'c'}, 424 }
438 {0, 0, 0, 0} 425
439 }; 426 /* No information (e.g. nameserver IP has two PTR records) */
440 427 else if (strstr(input_buffer, "No information")) {
441 if (argc < 2) 428 die(STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server);
442 return ERROR; 429 }
443 430
444 for (c = 1; c < argc; c++) 431 /* Network is unreachable */
445 if (strcmp ("-to", argv[c]) == 0) 432 else if (strstr(input_buffer, "Network is unreachable")) {
446 strcpy (argv[c], "-t"); 433 die(STATE_CRITICAL, _("Network is unreachable\n"));
447 434 }
448 while (1) { 435
449 c = getopt_long (argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index); 436 /* Internal server failure */
450 437 else if (strstr(input_buffer, "Server failure")) {
451 if (c == -1 || c == EOF) 438 die(STATE_CRITICAL, _("DNS failure for %s\n"), dns_server);
452 break; 439 }
453 440
454 switch (c) { 441 /* Request error or the DNS lookup timed out */
455 case 'h': /* help */ 442 else if (strstr(input_buffer, "Format error") || strstr(input_buffer, "Timed out")) {
456 print_help (); 443 return STATE_WARNING;
457 exit (STATE_UNKNOWN);
458 case 'V': /* version */
459 print_revision (progname, NP_VERSION);
460 exit (STATE_UNKNOWN);
461 case 'v': /* version */
462 verbose = true;
463 break;
464 case 't': /* timeout period */
465 timeout_interval = atoi (optarg);
466 break;
467 case 'H': /* hostname */
468 if (strlen (optarg) >= ADDRESS_LENGTH)
469 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
470 strcpy (query_address, optarg);
471 break;
472 case 's': /* server name */
473 /* TODO: this host_or_die check is probably unnecessary.
474 * Better to confirm nslookup response matches */
475 host_or_die(optarg);
476 if (strlen (optarg) >= ADDRESS_LENGTH)
477 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
478 strcpy (dns_server, optarg);
479 break;
480 case 'r': /* reverse server name */
481 /* TODO: Is this host_or_die necessary? */
482 host_or_die(optarg);
483 if (strlen (optarg) >= ADDRESS_LENGTH)
484 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
485 strcpy (ptr_server, optarg);
486 break;
487 case 'a': /* expected address */
488 if (strlen (optarg) >= ADDRESS_LENGTH)
489 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
490 if (strchr(optarg, ',') != NULL) {
491 char *comma = strchr(optarg, ',');
492 while (comma != NULL) {
493 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**));
494 expected_address[expected_address_cnt] = strndup(optarg, comma - optarg);
495 expected_address_cnt++;
496 optarg = comma + 1;
497 comma = strchr(optarg, ',');
498 } 444 }
499 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**)); 445
500 expected_address[expected_address_cnt] = strdup(optarg); 446 return STATE_OK;
501 expected_address_cnt++;
502 } else {
503 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**));
504 expected_address[expected_address_cnt] = strdup(optarg);
505 expected_address_cnt++;
506 }
507 break;
508 case 'n': /* expect NXDOMAIN */
509 expect_nxdomain = true;
510 break;
511 case 'A': /* expect authority */
512 expect_authority = true;
513 break;
514 case 'L': /* all must match */
515 all_match = true;
516 break;
517 case 'w':
518 warning = optarg;
519 break;
520 case 'c':
521 critical = optarg;
522 break;
523 default: /* args not parsable */
524 usage5();
525 }
526 }
527
528 c = optind;
529 if (strlen(query_address)==0 && c<argc) {
530 if (strlen(argv[c])>=ADDRESS_LENGTH)
531 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
532 strcpy (query_address, argv[c++]);
533 }
534
535 if (strlen(dns_server)==0 && c<argc) {
536 /* TODO: See -s option */
537 host_or_die(argv[c]);
538 if (strlen(argv[c]) >= ADDRESS_LENGTH)
539 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
540 strcpy (dns_server, argv[c++]);
541 }
542
543 set_thresholds(&time_thresholds, warning, critical);
544
545 return validate_arguments ();
546} 447}
547 448
449/* process command-line arguments */
450check_dns_config_wrapper process_arguments(int argc, char **argv) {
451 static struct option long_opts[] = {{"help", no_argument, 0, 'h'},
452 {"version", no_argument, 0, 'V'},
453 {"verbose", no_argument, 0, 'v'},
454 {"timeout", required_argument, 0, 't'},
455 {"hostname", required_argument, 0, 'H'},
456 {"server", required_argument, 0, 's'},
457 {"reverse-server", required_argument, 0, 'r'},
458 {"expected-address", required_argument, 0, 'a'},
459 {"expect-nxdomain", no_argument, 0, 'n'},
460 {"expect-authority", no_argument, 0, 'A'},
461 {"all", no_argument, 0, 'L'},
462 {"warning", required_argument, 0, 'w'},
463 {"critical", required_argument, 0, 'c'},
464 {0, 0, 0, 0}};
465
466 check_dns_config_wrapper result = {
467 .config = check_dns_config_init(),
468 .errorcode = OK,
469 };
470
471 if (argc < 2) {
472 result.errorcode = ERROR;
473 return result;
474 }
475
476 for (int index = 1; index < argc; index++) {
477 if (strcmp("-to", argv[index]) == 0) {
478 strcpy(argv[index], "-t");
479 }
480 }
548 481
549int 482 char *warning = NULL;
550validate_arguments () 483 char *critical = NULL;
551{ 484 int opt_index = 0;
552 if (query_address[0] == 0) { 485 int index = 0;
553 printf ("missing --host argument\n"); 486 while (true) {
554 return ERROR; 487 index = getopt_long(argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index);
555 } 488
489 if (index == -1 || index == EOF) {
490 break;
491 }
492
493 switch (index) {
494 case 'h': /* help */
495 print_help();
496 exit(STATE_UNKNOWN);
497 case 'V': /* version */
498 print_revision(progname, NP_VERSION);
499 exit(STATE_UNKNOWN);
500 case 'v': /* version */
501 verbose = true;
502 break;
503 case 't': /* timeout period */
504 timeout_interval = atoi(optarg);
505 break;
506 case 'H': /* hostname */
507 if (strlen(optarg) >= ADDRESS_LENGTH) {
508 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
509 }
510 strcpy(result.config.query_address, optarg);
511 break;
512 case 's': /* server name */
513 /* TODO: this host_or_die check is probably unnecessary.
514 * Better to confirm nslookup response matches */
515 host_or_die(optarg);
516 if (strlen(optarg) >= ADDRESS_LENGTH) {
517 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
518 }
519 strcpy(result.config.dns_server, optarg);
520 break;
521 case 'r': /* reverse server name */
522 /* TODO: Is this host_or_die necessary? */
523 // TODO This does not do anything!!! 2025-03-08 rincewind
524 host_or_die(optarg);
525 if (strlen(optarg) >= ADDRESS_LENGTH) {
526 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
527 }
528 static char ptr_server[ADDRESS_LENGTH] = "";
529 strcpy(ptr_server, optarg);
530 break;
531 case 'a': /* expected address */
532 if (strlen(optarg) >= ADDRESS_LENGTH) {
533 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
534 }
535 if (strchr(optarg, ',') != NULL) {
536 char *comma = strchr(optarg, ',');
537 while (comma != NULL) {
538 result.config.expected_address = (char **)realloc(
539 result.config.expected_address,
540 (result.config.expected_address_cnt + 1) * sizeof(char **));
541 result.config.expected_address[result.config.expected_address_cnt] =
542 strndup(optarg, comma - optarg);
543 result.config.expected_address_cnt++;
544 optarg = comma + 1;
545 comma = strchr(optarg, ',');
546 }
547 result.config.expected_address =
548 (char **)realloc(result.config.expected_address,
549 (result.config.expected_address_cnt + 1) * sizeof(char **));
550 result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg);
551 result.config.expected_address_cnt++;
552 } else {
553 result.config.expected_address =
554 (char **)realloc(result.config.expected_address,
555 (result.config.expected_address_cnt + 1) * sizeof(char **));
556 result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg);
557 result.config.expected_address_cnt++;
558 }
559 break;
560 case 'n': /* expect NXDOMAIN */
561 result.config.expect_nxdomain = true;
562 break;
563 case 'A': /* expect authority */
564 result.config.expect_authority = true;
565 break;
566 case 'L': /* all must match */
567 result.config.all_match = true;
568 break;
569 case 'w':
570 warning = optarg;
571 break;
572 case 'c':
573 critical = optarg;
574 break;
575 default: /* args not parsable */
576 usage5();
577 }
578 }
556 579
557 if (expected_address_cnt > 0 && expect_nxdomain) { 580 index = optind;
558 printf ("--expected-address and --expect-nxdomain cannot be combined\n"); 581 if (strlen(result.config.query_address) == 0 && index < argc) {
559 return ERROR; 582 if (strlen(argv[index]) >= ADDRESS_LENGTH) {
560 } 583 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
584 }
585 strcpy(result.config.query_address, argv[index++]);
586 }
561 587
562 return OK; 588 if (strlen(result.config.dns_server) == 0 && index < argc) {
589 /* TODO: See -s option */
590 host_or_die(argv[index]);
591 if (strlen(argv[index]) >= ADDRESS_LENGTH) {
592 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
593 }
594 strcpy(result.config.dns_server, argv[index++]);
595 }
596
597 set_thresholds(&result.config.time_thresholds, warning, critical);
598
599 return validate_arguments(result);
563} 600}
564 601
602check_dns_config_wrapper validate_arguments(check_dns_config_wrapper config_wrapper) {
603 if (config_wrapper.config.query_address[0] == 0) {
604 printf("missing --host argument\n");
605 config_wrapper.errorcode = ERROR;
606 return config_wrapper;
607 }
565 608
566void 609 if (config_wrapper.config.expected_address_cnt > 0 && config_wrapper.config.expect_nxdomain) {
567print_help (void) 610 printf("--expected-address and --expect-nxdomain cannot be combined\n");
568{ 611 config_wrapper.errorcode = ERROR;
569 print_revision (progname, NP_VERSION); 612 return config_wrapper;
570 613 }
571 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 614
572 printf (COPYRIGHT, copyright, email); 615 return config_wrapper;
573
574 printf ("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given host/domain query."));
575 printf ("%s\n", _("An optional DNS server to use may be specified."));
576 printf ("%s\n", _("If no DNS server is specified, the default server(s) specified in /etc/resolv.conf will be used."));
577
578 printf ("\n\n");
579
580 print_usage ();
581
582 printf (UT_HELP_VRSN);
583 printf (UT_EXTRA_OPTS);
584
585 printf (" -H, --hostname=HOST\n");
586 printf (" %s\n", _("The name or address you want to query"));
587 printf (" -s, --server=HOST\n");
588 printf (" %s\n", _("Optional DNS server you want to use for the lookup"));
589 printf (" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n");
590 printf (" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end"));
591 printf (" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any"));
592 printf (" %s\n", _("value matches)."));
593 printf (" -n, --expect-nxdomain\n");
594 printf (" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)"));
595 printf (" %s\n", _("Cannot be used together with -a"));
596 printf (" -A, --expect-authority\n");
597 printf (" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup"));
598 printf (" -w, --warning=seconds\n");
599 printf (" %s\n", _("Return warning if elapsed time exceeds value. Default off"));
600 printf (" -c, --critical=seconds\n");
601 printf (" %s\n", _("Return critical if elapsed time exceeds value. Default off"));
602 printf (" -L, --all\n");
603 printf (" %s\n", _("Return critical if the list of expected addresses does not match all addresses"));
604 printf (" %s\n", _("returned. Default off"));
605
606 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
607
608 printf (UT_SUPPORT);
609} 616}
610 617
618void print_help(void) {
619 print_revision(progname, NP_VERSION);
620
621 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
622 printf(COPYRIGHT, copyright, email);
623
624 printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given "
625 "host/domain query."));
626 printf("%s\n", _("An optional DNS server to use may be specified."));
627 printf("%s\n", _("If no DNS server is specified, the default server(s) specified in "
628 "/etc/resolv.conf will be used."));
629
630 printf("\n\n");
631
632 print_usage();
633
634 printf(UT_HELP_VRSN);
635 printf(UT_EXTRA_OPTS);
636
637 printf(" -H, --hostname=HOST\n");
638 printf(" %s\n", _("The name or address you want to query"));
639 printf(" -s, --server=HOST\n");
640 printf(" %s\n", _("Optional DNS server you want to use for the lookup"));
641 printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n");
642 printf(" %s\n",
643 _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end"));
644 printf(" %s\n",
645 _("with a dot (.). This option can be repeated multiple times (Returns OK if any"));
646 printf(" %s\n", _("value matches)."));
647 printf(" -n, --expect-nxdomain\n");
648 printf(" %s\n",
649 _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)"));
650 printf(" %s\n", _("Cannot be used together with -a"));
651 printf(" -A, --expect-authority\n");
652 printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup"));
653 printf(" -w, --warning=seconds\n");
654 printf(" %s\n", _("Return warning if elapsed time exceeds value. Default off"));
655 printf(" -c, --critical=seconds\n");
656 printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off"));
657 printf(" -L, --all\n");
658 printf(" %s\n",
659 _("Return critical if the list of expected addresses does not match all addresses"));
660 printf(" %s\n", _("returned. Default off"));
661
662 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
663
664 printf(UT_SUPPORT);
665}
611 666
612void 667void print_usage(void) {
613print_usage (void) 668 printf("%s\n", _("Usage:"));
614{ 669 printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c "
615 printf ("%s\n", _("Usage:")); 670 "crit] [-L]\n",
616 printf ("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname); 671 progname);
617} 672}
diff --git a/plugins/check_dns.d/config.h b/plugins/check_dns.d/config.h
new file mode 100644
index 00000000..9ec4eb82
--- /dev/null
+++ b/plugins/check_dns.d/config.h
@@ -0,0 +1,34 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7#define ADDRESS_LENGTH 256
8
9typedef struct {
10 bool all_match;
11 char dns_server[ADDRESS_LENGTH];
12 char query_address[ADDRESS_LENGTH];
13 bool expect_nxdomain;
14 bool expect_authority;
15 char **expected_address;
16 size_t expected_address_cnt;
17
18 thresholds *time_thresholds;
19} check_dns_config;
20
21check_dns_config check_dns_config_init() {
22 check_dns_config tmp = {
23 .all_match = false,
24 .dns_server = "",
25 .query_address = "",
26 .expect_nxdomain = false,
27 .expect_authority = false,
28 .expected_address = NULL,
29 .expected_address_cnt = 0,
30
31 .time_thresholds = NULL,
32 };
33 return tmp;
34}
diff --git a/plugins/check_dummy.c b/plugins/check_dummy.c
index 212a1344..602d5868 100644
--- a/plugins/check_dummy.c
+++ b/plugins/check_dummy.c
@@ -1,124 +1,114 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dummy plugin 3 * Monitoring check_dummy plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 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_dummy plugin 10 * This file contains the check_dummy plugin
11* 11 *
12* This plugin will simply return the state corresponding to the numeric value 12 * This plugin will simply return the state corresponding to the numeric value
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_dummy"; 31const char *progname = "check_dummy";
32const char *copyright = "1999-2007"; 32const char *copyright = "1999-2024";
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 "utils.h" 36#include "utils.h"
37 37
38void print_help (void); 38static void print_help(void);
39void print_usage (void); 39void print_usage(void);
40 40
41 41int main(int argc, char **argv) {
42int 42 int result = STATE_UNKNOWN;
43main (int argc, char **argv) 43
44{ 44 setlocale(LC_ALL, "");
45 int result = STATE_UNKNOWN; 45 bindtextdomain(PACKAGE, LOCALEDIR);
46 46 textdomain(PACKAGE);
47 setlocale (LC_ALL, ""); 47
48 bindtextdomain (PACKAGE, LOCALEDIR); 48 if (argc < 2) {
49 textdomain (PACKAGE); 49 usage4(_("Could not parse arguments"));
50 50 } else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) {
51 if (argc < 2) 51 print_revision(progname, NP_VERSION);
52 usage4 (_("Could not parse arguments")); 52 exit(STATE_UNKNOWN);
53 else if (strcmp (argv[1], "-V") == 0 || strcmp (argv[1], "--version") == 0) { 53 } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
54 print_revision (progname, NP_VERSION); 54 print_help();
55 exit (STATE_UNKNOWN); 55 exit(STATE_UNKNOWN);
56 } 56 } else if (!is_integer(argv[1])) {
57 else if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) { 57 usage4(_("Arguments to check_dummy must be an integer"));
58 print_help (); 58 } else {
59 exit (STATE_UNKNOWN); 59 result = atoi(argv[1]);
60 } 60 }
61 else if (!is_integer (argv[1])) 61
62 usage4 (_("Arguments to check_dummy must be an integer")); 62 switch (result) {
63 else 63 case STATE_OK:
64 result = atoi (argv[1]); 64 printf(_("OK"));
65 65 break;
66 switch (result) { 66 case STATE_WARNING:
67 case STATE_OK: 67 printf(_("WARNING"));
68 printf (_("OK")); 68 break;
69 break; 69 case STATE_CRITICAL:
70 case STATE_WARNING: 70 printf(_("CRITICAL"));
71 printf (_("WARNING")); 71 break;
72 break; 72 case STATE_UNKNOWN:
73 case STATE_CRITICAL: 73 printf(_("UNKNOWN"));
74 printf (_("CRITICAL")); 74 break;
75 break; 75 default:
76 case STATE_UNKNOWN: 76 printf(_("UNKNOWN"));
77 printf (_("UNKNOWN")); 77 printf(": ");
78 break; 78 printf(_("Status %d is not a supported error state\n"), result);
79 default: 79 return STATE_UNKNOWN;
80 printf (_("UNKNOWN")); 80 }
81 printf (": "); 81
82 printf (_("Status %d is not a supported error state\n"), result); 82 if (argc >= 3) {
83 return STATE_UNKNOWN; 83 printf(": %s", argv[2]);
84 } 84 }
85 85
86 if (argc >= 3) 86 printf("\n");
87 printf (": %s", argv[2]); 87
88 88 return result;
89 printf("\n");
90
91 return result;
92} 89}
93 90
91void print_help(void) {
92 print_revision(progname, NP_VERSION);
94 93
94 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
95 printf(COPYRIGHT, copyright, email);
95 96
96void 97 printf("%s\n",
97print_help (void) 98 _("This plugin will simply return the state corresponding to the numeric value"));
98{
99 print_revision (progname, NP_VERSION);
100 99
101 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 100 printf("%s\n", _("of the <state> argument with optional text"));
102 printf (COPYRIGHT, copyright, email);
103 101
104 printf ("%s\n", _("This plugin will simply return the state corresponding to the numeric value")); 102 printf("\n\n");
105 103
106 printf ("%s\n", _("of the <state> argument with optional text")); 104 print_usage();
107 105
108 printf ("\n\n"); 106 printf(UT_HELP_VRSN);
109 107
110 print_usage (); 108 printf(UT_SUPPORT);
111
112 printf (UT_HELP_VRSN);
113
114 printf (UT_SUPPORT);
115} 109}
116 110
117 111void print_usage(void) {
118 112 printf("%s\n", _("Usage:"));
119void 113 printf(" %s <integer state> [optional text]\n", progname);
120print_usage (void)
121{
122 printf ("%s\n", _("Usage:"));
123 printf (" %s <integer state> [optional text]\n", progname);
124} 114}
diff --git a/plugins/check_fping.c b/plugins/check_fping.c
index 70d6f9fc..86ef64a4 100644
--- a/plugins/check_fping.c
+++ b/plugins/check_fping.c
@@ -1,36 +1,36 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_fping plugin 3 * Monitoring check_fping plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 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_disk plugin 10 * This file contains the check_fping plugin
11* 11 *
12* This plugin will use the fping command to ping the specified host for a 12 * This plugin will use the fping command to ping the specified host for a
13* fast check 13 * fast check
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_fping"; 32const char *progname = "check_fping";
33const char *copyright = "2000-2007"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
@@ -38,490 +38,560 @@ const char *email = "devel@monitoring-plugins.org";
38#include "netutils.h" 38#include "netutils.h"
39#include "utils.h" 39#include "utils.h"
40#include <stdbool.h> 40#include <stdbool.h>
41#include "check_fping.d/config.h"
42#include "states.h"
41 43
42enum { 44enum {
43 PACKET_COUNT = 1, 45 PL = 0,
44 PACKET_SIZE = 56, 46 RTA = 1
45 PL = 0,
46 RTA = 1
47}; 47};
48 48
49int textscan (char *buf); 49static mp_state_enum textscan(char *buf, const char * /*server_name*/, bool /*crta_p*/,
50int process_arguments (int, char **); 50 double /*crta*/, bool /*wrta_p*/, double /*wrta*/, bool /*cpl_p*/,
51int get_threshold (char *arg, char *rv[2]); 51 int /*cpl*/, bool /*wpl_p*/, int /*wpl*/, bool /*alive_p*/);
52void print_help (void); 52
53void print_usage (void); 53typedef struct {
54 54 int errorcode;
55char *server_name = NULL; 55 check_fping_config config;
56char *sourceip = NULL; 56} check_fping_config_wrapper;
57char *sourceif = NULL; 57static check_fping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
58int packet_size = PACKET_SIZE; 58static int get_threshold(char *arg, char *rv[2]);
59int packet_count = PACKET_COUNT; 59static void print_help(void);
60int target_timeout = 0; 60void print_usage(void);
61int packet_interval = 0; 61
62bool verbose = false; 62static bool verbose = false;
63int cpl; 63
64int wpl; 64int main(int argc, char **argv) {
65double crta; 65 setlocale(LC_ALL, "");
66double wrta; 66 bindtextdomain(PACKAGE, LOCALEDIR);
67bool cpl_p = false; 67 textdomain(PACKAGE);
68bool wpl_p = false; 68
69bool alive_p = false; 69 /* Parse extra opts if any */
70bool crta_p = false; 70 argv = np_extra_opts(&argc, argv, progname);
71bool wrta_p = false; 71
72 72 check_fping_config_wrapper tmp_config = process_arguments(argc, argv);
73int 73 if (tmp_config.errorcode == ERROR) {
74main (int argc, char **argv) 74 usage4(_("Could not parse arguments"));
75{ 75 }
76/* normally should be int result = STATE_UNKNOWN; */ 76
77 77 const check_fping_config config = tmp_config.config;
78 int status = STATE_UNKNOWN; 78
79 int result = 0; 79 char *server = NULL;
80 char *fping_prog = NULL; 80 server = strscpy(server, config.server_name);
81 char *server = NULL; 81
82 char *command_line = NULL; 82 char *option_string = "";
83 char *input_buffer = NULL; 83 char *fping_prog = NULL;
84 char *option_string = ""; 84
85 input_buffer = malloc (MAX_INPUT_BUFFER); 85 /* First determine if the target is dualstack or ipv6 only. */
86 86 bool server_is_inet6_addr = is_inet6_addr(server);
87 setlocale (LC_ALL, ""); 87
88 bindtextdomain (PACKAGE, LOCALEDIR); 88 /*
89 textdomain (PACKAGE); 89 * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack
90 90 * -> we use ipv6
91 /* Parse extra opts if any */ 91 * If the user requested -4 OR the user made no assertion and the address is v4 ONLY
92 argv=np_extra_opts (&argc, argv, progname); 92 * -> we use ipv4
93 93 */
94 if (process_arguments (argc, argv) == ERROR) 94 if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) {
95 usage4 (_("Could not parse arguments")); 95 xasprintf(&option_string, "%s-6 ", option_string);
96 96 } else {
97 server = strscpy (server, server_name); 97 xasprintf(&option_string, "%s-4 ", option_string);
98 98 }
99 /* compose the command */ 99 fping_prog = strdup(PATH_TO_FPING);
100 if (target_timeout) 100
101 xasprintf(&option_string, "%s-t %d ", option_string, target_timeout); 101 /* compose the command */
102 if (packet_interval) 102 if (config.target_timeout) {
103 xasprintf(&option_string, "%s-p %d ", option_string, packet_interval); 103 xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout);
104 if (sourceip) 104 }
105 xasprintf(&option_string, "%s-S %s ", option_string, sourceip); 105 if (config.packet_interval) {
106 if (sourceif) 106 xasprintf(&option_string, "%s-p %d ", option_string, config.packet_interval);
107 xasprintf(&option_string, "%s-I %s ", option_string, sourceif); 107 }
108 108 if (config.sourceip) {
109#ifdef PATH_TO_FPING6 109 xasprintf(&option_string, "%s-S %s ", option_string, config.sourceip);
110 if (address_family != AF_INET && is_inet6_addr(server)) 110 }
111 fping_prog = strdup(PATH_TO_FPING6); 111 if (config.sourceif) {
112 else 112 xasprintf(&option_string, "%s-I %s ", option_string, config.sourceif);
113 fping_prog = strdup(PATH_TO_FPING); 113 }
114#else 114 if (config.dontfrag) {
115 fping_prog = strdup(PATH_TO_FPING); 115 xasprintf(&option_string, "%s-M ", option_string);
116#endif 116 }
117 117 if (config.randomize_packet_data) {
118 xasprintf (&command_line, "%s %s-b %d -c %d %s", fping_prog, 118 xasprintf(&option_string, "%s-R ", option_string);
119 option_string, packet_size, packet_count, server); 119 }
120 120
121 if (verbose) 121 if (config.fwmark_set) {
122 printf ("%s\n", command_line); 122 xasprintf(&option_string, "%s--fwmark %u ", option_string, config.fwmark);
123 123 }
124 /* run the command */ 124
125 child_process = spopen (command_line); 125 if (config.icmp_timestamp) {
126 if (child_process == NULL) { 126 xasprintf(&option_string, "%s--icmp-timestamp ", option_string);
127 printf (_("Could not open pipe: %s\n"), command_line); 127 }
128 return STATE_UNKNOWN; 128
129 } 129 if (config.check_source) {
130 130 xasprintf(&option_string, "%s--check-source ", option_string);
131 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 131 }
132 if (child_stderr == NULL) { 132
133 printf (_("Could not open stderr for %s\n"), command_line); 133 char *command_line = NULL;
134 } 134
135 135 if (config.icmp_timestamp) {
136 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 136 // no packet size settable for ICMP timestamp
137 if (verbose) 137 xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count,
138 printf ("%s", input_buffer); 138 server);
139 status = max_state (status, textscan (input_buffer)); 139 } else {
140 } 140 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string,
141 141 config.packet_size, config.packet_count, server);
142 /* If we get anything on STDERR, at least set warning */ 142 }
143 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { 143
144 status = max_state (status, STATE_WARNING); 144 if (verbose) {
145 if (verbose) 145 printf("%s\n", command_line);
146 printf ("%s", input_buffer); 146 }
147 status = max_state (status, textscan (input_buffer)); 147
148 } 148 /* run the command */
149 (void) fclose (child_stderr); 149 child_process = spopen(command_line);
150 150 if (child_process == NULL) {
151 /* close the pipe */ 151 printf(_("Could not open pipe: %s\n"), command_line);
152 result = spclose (child_process); 152 return STATE_UNKNOWN;
153 if (result) { 153 }
154 /* need to use max_state not max */ 154
155 status = max_state (status, STATE_WARNING); 155 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
156 } 156 if (child_stderr == NULL) {
157 157 printf(_("Could not open stderr for %s\n"), command_line);
158 if (result > 1 ) { 158 }
159 status = max_state (status, STATE_UNKNOWN); 159
160 if (result == 2) { 160 char *input_buffer = malloc(MAX_INPUT_BUFFER);
161 die (STATE_UNKNOWN, _("FPING UNKNOWN - IP address not found\n")); 161 mp_state_enum status = STATE_UNKNOWN;
162 } 162 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
163 if (result == 3) { 163 if (verbose) {
164 die (STATE_UNKNOWN, _("FPING UNKNOWN - invalid commandline argument\n")); 164 printf("%s", input_buffer);
165 } 165 }
166 if (result == 4) { 166 status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p,
167 die (STATE_UNKNOWN, _("FPING UNKNOWN - failed system call\n")); 167 config.crta, config.wrta_p, config.wrta, config.cpl_p,
168 } 168 config.cpl, config.wpl_p, config.wpl, config.alive_p));
169 169 }
170 } 170
171 171 /* If we get anything on STDERR, at least set warning */
172 printf ("FPING %s - %s\n", state_text (status), server_name); 172 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
173 173 status = max_state(status, STATE_WARNING);
174 return status; 174 if (verbose) {
175 printf("%s", input_buffer);
176 }
177 status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p,
178 config.crta, config.wrta_p, config.wrta, config.cpl_p,
179 config.cpl, config.wpl_p, config.wpl, config.alive_p));
180 }
181 (void)fclose(child_stderr);
182
183 /* close the pipe */
184 int result = spclose(child_process);
185 if (result) {
186 /* need to use max_state not max */
187 status = max_state(status, STATE_WARNING);
188 }
189
190 if (result > 1) {
191 status = max_state(status, STATE_UNKNOWN);
192 if (result == 2) {
193 die(STATE_UNKNOWN, _("FPING UNKNOWN - IP address not found\n"));
194 }
195 if (result == 3) {
196 die(STATE_UNKNOWN, _("FPING UNKNOWN - invalid commandline argument\n"));
197 }
198 if (result == 4) {
199 die(STATE_UNKNOWN, _("FPING UNKNOWN - failed system call\n"));
200 }
201 }
202
203 printf("FPING %s - %s\n", state_text(status), config.server_name);
204
205 return status;
175} 206}
176 207
177 208mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double crta, bool wrta_p,
178int textscan (char *buf) { 209 double wrta, bool cpl_p, int cpl, bool wpl_p, int wpl, bool alive_p) {
179 char *rtastr = NULL; 210 /* stops testing after the first successful reply. */
180 char *losstr = NULL; 211 double rta;
181 char *xmtstr = NULL; 212 double loss;
182 double loss; 213 char *rtastr = NULL;
183 double rta; 214 if (alive_p && strstr(buf, "avg, 0% loss)")) {
184 double xmt; 215 rtastr = strstr(buf, "ms (");
185 int status = STATE_UNKNOWN; 216 rtastr = 1 + index(rtastr, '(');
186 217 rta = strtod(rtastr, NULL);
187 /* stops testing after the first successful reply. */ 218 loss = strtod("0", NULL);
188 if (alive_p && strstr(buf, "avg, 0% loss)")) { 219 die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta,
189 rtastr = strstr (buf, "ms ("); 220 /* No loss since we only waited for the first reply
190 rtastr = 1 + index(rtastr, '('); 221 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */
191 rta = strtod(rtastr, NULL); 222 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0,
192 loss=strtod("0",NULL); 223 false, 0));
193 die (STATE_OK, 224 }
194 _("FPING %s - %s (rta=%f ms)|%s\n"), 225
195 state_text (STATE_OK), server_name,rta, 226 mp_state_enum status = STATE_UNKNOWN;
196 /* No loss since we only waited for the first reply 227 char *xmtstr = NULL;
197 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */ 228 double xmt;
198 fperfdata ("rta", rta/1.0e3, "s", wrta_p, wrta/1.0e3, crta_p, crta/1.0e3, true, 0, false, 0)); 229 char *losstr = NULL;
199 } 230 if (strstr(buf, "not found")) {
200 231 die(STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name);
201 if (strstr (buf, "not found")) { 232
202 die (STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name); 233 } else if (strstr(buf, "is unreachable") || strstr(buf, "Unreachable")) {
203 234 die(STATE_CRITICAL, _("FPING CRITICAL - %s is unreachable\n"), "host");
204 } 235
205 else if (strstr (buf, "is unreachable") || strstr (buf, "Unreachable")) { 236 } else if (strstr(buf, "Operation not permitted") || strstr(buf, "No such device")) {
206 die (STATE_CRITICAL, _("FPING CRITICAL - %s is unreachable\n"), 237 die(STATE_UNKNOWN, _("FPING UNKNOWN - %s parameter error\n"), "host");
207 "host"); 238 } else if (strstr(buf, "is down")) {
208 239 die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name);
209 } 240
210 else if (strstr (buf, "Operation not permitted") || strstr (buf, "No such device") ) { 241 } else if (strstr(buf, "is alive")) {
211 die (STATE_UNKNOWN, _("FPING UNKNOWN - %s parameter error\n"), 242 status = STATE_OK;
212 "host"); 243
213 } 244 } else if (strstr(buf, "xmt/rcv/%loss") && strstr(buf, "min/avg/max")) {
214 else if (strstr (buf, "is down")) { 245 losstr = strstr(buf, "=");
215 die (STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name); 246 losstr = 1 + strstr(losstr, "/");
216 247 losstr = 1 + strstr(losstr, "/");
217 } 248 rtastr = strstr(buf, "min/avg/max");
218 else if (strstr (buf, "is alive")) { 249 rtastr = strstr(rtastr, "=");
219 status = STATE_OK; 250 rtastr = 1 + index(rtastr, '/');
220 251 loss = strtod(losstr, NULL);
221 } 252 rta = strtod(rtastr, NULL);
222 else if (strstr (buf, "xmt/rcv/%loss") && strstr (buf, "min/avg/max")) { 253 if (cpl_p && loss > cpl) {
223 losstr = strstr (buf, "="); 254 status = STATE_CRITICAL;
224 losstr = 1 + strstr (losstr, "/"); 255 } else if (crta_p && rta > crta) {
225 losstr = 1 + strstr (losstr, "/"); 256 status = STATE_CRITICAL;
226 rtastr = strstr (buf, "min/avg/max"); 257 } else if (wpl_p && loss > wpl) {
227 rtastr = strstr (rtastr, "="); 258 status = STATE_WARNING;
228 rtastr = 1 + index (rtastr, '/'); 259 } else if (wrta_p && rta > wrta) {
229 loss = strtod (losstr, NULL); 260 status = STATE_WARNING;
230 rta = strtod (rtastr, NULL); 261 } else {
231 if (cpl_p && loss > cpl) 262 status = STATE_OK;
232 status = STATE_CRITICAL; 263 }
233 else if (crta_p && rta > crta) 264 die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status),
234 status = STATE_CRITICAL; 265 server_name, loss, rta,
235 else if (wpl_p && loss > wpl) 266 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0),
236 status = STATE_WARNING; 267 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0,
237 else if (wrta_p && rta > wrta) 268 false, 0));
238 status = STATE_WARNING; 269
239 else 270 } else if (strstr(buf, "xmt/rcv/%loss")) {
240 status = STATE_OK; 271 /* no min/max/avg if host was unreachable in fping v2.2.b1 */
241 die (status, 272 /* in v2.4b2: 10.99.0.1 : xmt/rcv/%loss = 0/0/0% */
242 _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), 273 losstr = strstr(buf, "=");
243 state_text (status), server_name, loss, rta, 274 xmtstr = 1 + losstr;
244 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), 275 xmt = strtod(xmtstr, NULL);
245 fperfdata ("rta", rta/1.0e3, "s", wrta_p, wrta/1.0e3, crta_p, crta/1.0e3, true, 0, false, 0)); 276 if (xmt == 0) {
246 277 die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name);
247 } 278 }
248 else if(strstr (buf, "xmt/rcv/%loss") ) { 279 losstr = 1 + strstr(losstr, "/");
249 /* no min/max/avg if host was unreachable in fping v2.2.b1 */ 280 losstr = 1 + strstr(losstr, "/");
250 /* in v2.4b2: 10.99.0.1 : xmt/rcv/%loss = 0/0/0% */ 281 loss = strtod(losstr, NULL);
251 losstr = strstr (buf, "="); 282 if (atoi(losstr) == 100) {
252 xmtstr = 1 + losstr; 283 status = STATE_CRITICAL;
253 xmt = strtod (xmtstr, NULL); 284 } else if (cpl_p && loss > cpl) {
254 if(xmt == 0) 285 status = STATE_CRITICAL;
255 die (STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name); 286 } else if (wpl_p && loss > wpl) {
256 losstr = 1 + strstr (losstr, "/"); 287 status = STATE_WARNING;
257 losstr = 1 + strstr (losstr, "/"); 288 } else {
258 loss = strtod (losstr, NULL); 289 status = STATE_OK;
259 if (atoi(losstr) == 100) 290 }
260 status = STATE_CRITICAL; 291 /* loss=%.0f%%;%d;%d;0;100 */
261 else if (cpl_p && loss > cpl) 292 die(status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), state_text(status), server_name, loss,
262 status = STATE_CRITICAL; 293 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0));
263 else if (wpl_p && loss > wpl) 294
264 status = STATE_WARNING; 295 } else {
265 else 296 status = max_state(status, STATE_WARNING);
266 status = STATE_OK; 297 }
267 /* loss=%.0f%%;%d;%d;0;100 */ 298
268 die (status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), 299 return status;
269 state_text (status), server_name, loss ,
270 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100));
271
272 }
273 else {
274 status = max_state (status, STATE_WARNING);
275 }
276
277 return status;
278} 300}
279 301
280
281
282/* process command-line arguments */ 302/* process command-line arguments */
283int 303check_fping_config_wrapper process_arguments(int argc, char **argv) {
284process_arguments (int argc, char **argv) 304 enum {
285{ 305 FWMARK_OPT = CHAR_MAX + 1,
286 int c; 306 ICMP_TIMESTAMP_OPT,
287 char *rv[2]; 307 CHECK_SOURCE_OPT,
288 308 };
289 int option = 0; 309 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
290 static struct option longopts[] = { 310 {"sourceip", required_argument, 0, 'S'},
291 {"hostname", required_argument, 0, 'H'}, 311 {"sourceif", required_argument, 0, 'I'},
292 {"sourceip", required_argument, 0, 'S'}, 312 {"critical", required_argument, 0, 'c'},
293 {"sourceif", required_argument, 0, 'I'}, 313 {"warning", required_argument, 0, 'w'},
294 {"critical", required_argument, 0, 'c'}, 314 {"alive", no_argument, 0, 'a'},
295 {"warning", required_argument, 0, 'w'}, 315 {"bytes", required_argument, 0, 'b'},
296 {"alive", no_argument, 0, 'a'}, 316 {"number", required_argument, 0, 'n'},
297 {"bytes", required_argument, 0, 'b'}, 317 {"target-timeout", required_argument, 0, 'T'},
298 {"number", required_argument, 0, 'n'}, 318 {"interval", required_argument, 0, 'i'},
299 {"target-timeout", required_argument, 0, 'T'}, 319 {"verbose", no_argument, 0, 'v'},
300 {"interval", required_argument, 0, 'i'}, 320 {"version", no_argument, 0, 'V'},
301 {"verbose", no_argument, 0, 'v'}, 321 {"help", no_argument, 0, 'h'},
302 {"version", no_argument, 0, 'V'}, 322 {"use-ipv4", no_argument, 0, '4'},
303 {"help", no_argument, 0, 'h'}, 323 {"use-ipv6", no_argument, 0, '6'},
304 {"use-ipv4", no_argument, 0, '4'}, 324 {"dontfrag", no_argument, 0, 'M'},
305 {"use-ipv6", no_argument, 0, '6'}, 325 {"random", no_argument, 0, 'R'},
306 {0, 0, 0, 0} 326#ifdef FPING_VERSION_5_2_OR_HIGHER
307 }; 327 // only available with fping version >= 5.2
308 328 {"fwmark", required_argument, NULL, FWMARK_OPT},
309 rv[PL] = NULL; 329# ifdef FPING_VERSION_5_3_OR_HIGHER
310 rv[RTA] = NULL; 330 // only available with fping version >= 5.3
311 331 {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT},
312 if (argc < 2) 332 {"check-source", no_argument, NULL, CHECK_SOURCE_OPT},
313 return ERROR; 333# endif
314
315 if (!is_option (argv[1])) {
316 server_name = argv[1];
317 argv[1] = argv[0];
318 argv = &argv[1];
319 argc--;
320 }
321
322 while (1) {
323 c = getopt_long (argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:46", longopts, &option);
324
325 if (c == -1 || c == EOF || c == 1)
326 break;
327
328 switch (c) {
329 case '?': /* print short usage statement if args not parsable */
330 usage5 ();
331 case 'a': /* host alive mode */
332 alive_p = true;
333 break;
334 case 'h': /* help */
335 print_help ();
336 exit (STATE_UNKNOWN);
337 case 'V': /* version */
338 print_revision (progname, NP_VERSION);
339 exit (STATE_UNKNOWN);
340 case 'v': /* verbose mode */
341 verbose = true;
342 break;
343 case 'H': /* hostname */
344 if (is_host (optarg) == false) {
345 usage2 (_("Invalid hostname/address"), optarg);
346 }
347 server_name = strscpy (server_name, optarg);
348 break;
349 case 'S': /* sourceip */
350 if (is_host (optarg) == false) {
351 usage2 (_("Invalid hostname/address"), optarg);
352 }
353 sourceip = strscpy (sourceip, optarg);
354 break;
355 case 'I': /* sourceip */
356 sourceif = strscpy (sourceif, optarg);
357 break;
358 case '4': /* IPv4 only */
359 address_family = AF_INET;
360 break;
361 case '6': /* IPv6 only */
362#ifdef USE_IPV6
363 address_family = AF_INET6;
364#else
365 usage (_("IPv6 support not available\n"));
366#endif 334#endif
367 break; 335 {0, 0, 0, 0}};
368 case 'c':
369 get_threshold (optarg, rv);
370 if (rv[RTA]) {
371 crta = strtod (rv[RTA], NULL);
372 crta_p = true;
373 rv[RTA] = NULL;
374 }
375 if (rv[PL]) {
376 cpl = atoi (rv[PL]);
377 cpl_p = true;
378 rv[PL] = NULL;
379 }
380 break;
381 case 'w':
382 get_threshold (optarg, rv);
383 if (rv[RTA]) {
384 wrta = strtod (rv[RTA], NULL);
385 wrta_p = true;
386 rv[RTA] = NULL;
387 }
388 if (rv[PL]) {
389 wpl = atoi (rv[PL]);
390 wpl_p = true;
391 rv[PL] = NULL;
392 }
393 break;
394 case 'b': /* bytes per packet */
395 if (is_intpos (optarg))
396 packet_size = atoi (optarg);
397 else
398 usage (_("Packet size must be a positive integer"));
399 break;
400 case 'n': /* number of packets */
401 if (is_intpos (optarg))
402 packet_count = atoi (optarg);
403 else
404 usage (_("Packet count must be a positive integer"));
405 break;
406 case 'T': /* timeout in msec */
407 if (is_intpos (optarg))
408 target_timeout = atoi (optarg);
409 else
410 usage (_("Target timeout must be a positive integer"));
411 break;
412 case 'i': /* interval in msec */
413 if (is_intpos (optarg))
414 packet_interval = atoi (optarg);
415 else
416 usage (_("Interval must be a positive integer"));
417 break;
418 }
419 }
420
421 if (server_name == NULL)
422 usage4 (_("Hostname was not supplied"));
423
424 return OK;
425}
426
427 336
428int 337 char *rv[2];
429get_threshold (char *arg, char *rv[2]) 338 rv[PL] = NULL;
430{ 339 rv[RTA] = NULL;
431 char *arg1 = NULL;
432 char *arg2 = NULL;
433
434 arg1 = strscpy (arg1, arg);
435 if (strpbrk (arg1, ",:"))
436 arg2 = 1 + strpbrk (arg1, ",:");
437
438 if (arg2) {
439 arg1[strcspn (arg1, ",:")] = 0;
440 if (strstr (arg1, "%") && strstr (arg2, "%"))
441 die (STATE_UNKNOWN,
442 _("%s: Only one threshold may be packet loss (%s)\n"), progname,
443 arg);
444 if (!strstr (arg1, "%") && !strstr (arg2, "%"))
445 die (STATE_UNKNOWN,
446 _("%s: Only one threshold must be packet loss (%s)\n"),
447 progname, arg);
448 }
449
450 if (arg2 && strstr (arg2, "%")) {
451 rv[PL] = arg2;
452 rv[RTA] = arg1;
453 }
454 else if (arg2) {
455 rv[PL] = arg1;
456 rv[RTA] = arg2;
457 }
458 else if (strstr (arg1, "%")) {
459 rv[PL] = arg1;
460 }
461 else {
462 rv[RTA] = arg1;
463 }
464
465 return OK;
466}
467 340
341 int option = 0;
468 342
469void print_help (void) { 343 check_fping_config_wrapper result = {
344 .errorcode = OK,
345 .config = check_fping_config_init(),
346 };
470 347
471 print_revision (progname, NP_VERSION); 348 if (argc < 2) {
349 result.errorcode = ERROR;
350 return result;
351 }
472 352
473 printf ("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n"); 353 if (!is_option(argv[1])) {
474 printf (COPYRIGHT, copyright, email); 354 result.config.server_name = argv[1];
355 argv[1] = argv[0];
356 argv = &argv[1];
357 argc--;
358 }
475 359
476 printf ("%s\n", _("This plugin will use the fping command to ping the specified host for a fast check")); 360 while (true) {
361 int option_index =
362 getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option);
477 363
478 printf ("%s\n", _("Note that it is necessary to set the suid flag on fping.")); 364 if (CHECK_EOF(option_index) || option_index == 1) {
365 break;
366 }
479 367
480 printf ("\n\n"); 368 switch (option_index) {
369 case '?': /* print short usage statement if args not parsable */
370 usage5();
371 case 'a': /* host alive mode */
372 result.config.alive_p = true;
373 break;
374 case 'h': /* help */
375 print_help();
376 exit(STATE_UNKNOWN);
377 case 'V': /* version */
378 print_revision(progname, NP_VERSION);
379 exit(STATE_UNKNOWN);
380 case 'v': /* verbose mode */
381 verbose = true;
382 break;
383 case 'H': /* hostname */
384 if (!is_host(optarg)) {
385 usage2(_("Invalid hostname/address"), optarg);
386 }
387 result.config.server_name = optarg;
388 break;
389 case 'S': /* sourceip */
390 if (!is_host(optarg)) {
391 usage2(_("Invalid hostname/address"), optarg);
392 }
393 result.config.sourceip = optarg;
394 break;
395 case 'I': /* sourceip */
396 result.config.sourceif = optarg;
397 break;
398 case '4': /* IPv4 only */
399 address_family = AF_INET;
400 break;
401 case '6': /* IPv6 only */
402 address_family = AF_INET6;
403 break;
404 case 'c':
405 get_threshold(optarg, rv);
406 if (rv[RTA]) {
407 result.config.crta = strtod(rv[RTA], NULL);
408 result.config.crta_p = true;
409 rv[RTA] = NULL;
410 }
411 if (rv[PL]) {
412 result.config.cpl = atoi(rv[PL]);
413 result.config.cpl_p = true;
414 rv[PL] = NULL;
415 }
416 break;
417 case 'w':
418 get_threshold(optarg, rv);
419 if (rv[RTA]) {
420 result.config.wrta = strtod(rv[RTA], NULL);
421 result.config.wrta_p = true;
422 rv[RTA] = NULL;
423 }
424 if (rv[PL]) {
425 result.config.wpl = atoi(rv[PL]);
426 result.config.wpl_p = true;
427 rv[PL] = NULL;
428 }
429 break;
430 case 'b': /* bytes per packet */
431 if (is_intpos(optarg)) {
432 result.config.packet_size = atoi(optarg);
433 } else {
434 usage(_("Packet size must be a positive integer"));
435 }
436 break;
437 case 'n': /* number of packets */
438 if (is_intpos(optarg)) {
439 result.config.packet_count = atoi(optarg);
440 } else {
441 usage(_("Packet count must be a positive integer"));
442 }
443 break;
444 case 'T': /* timeout in msec */
445 if (is_intpos(optarg)) {
446 result.config.target_timeout = atoi(optarg);
447 } else {
448 usage(_("Target timeout must be a positive integer"));
449 }
450 break;
451 case 'i': /* interval in msec */
452 if (is_intpos(optarg)) {
453 result.config.packet_interval = atoi(optarg);
454 } else {
455 usage(_("Interval must be a positive integer"));
456 }
457 break;
458 case 'R':
459 result.config.randomize_packet_data = true;
460 break;
461 case 'M':
462 result.config.dontfrag = true;
463 break;
464 case FWMARK_OPT:
465 if (is_intpos(optarg)) {
466 result.config.fwmark = (unsigned int)atol(optarg);
467 result.config.fwmark_set = true;
468 } else {
469 usage(_("fwmark must be a positive integer"));
470 }
471 break;
472 case ICMP_TIMESTAMP_OPT:
473 result.config.icmp_timestamp = true;
474 break;
475 case CHECK_SOURCE_OPT:
476 result.config.check_source = true;
477 break;
478 }
479 }
481 480
482 print_usage (); 481 if (result.config.server_name == NULL) {
482 usage4(_("Hostname was not supplied"));
483 }
483 484
484 printf (UT_HELP_VRSN); 485 return result;
485 printf (UT_EXTRA_OPTS); 486}
486 487
487 printf (UT_IPv46); 488int get_threshold(char *arg, char *rv[2]) {
489 char *arg2 = NULL;
490
491 char *arg1 = strdup(arg);
492 if (strpbrk(arg1, ",:")) {
493 arg2 = 1 + strpbrk(arg1, ",:");
494 }
495
496 if (arg2) {
497 arg1[strcspn(arg1, ",:")] = 0;
498 if (strstr(arg1, "%") && strstr(arg2, "%")) {
499 die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname,
500 arg);
501 }
502 if (!strstr(arg1, "%") && !strstr(arg2, "%")) {
503 die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname,
504 arg);
505 }
506 }
507
508 if (arg2 && strstr(arg2, "%")) {
509 rv[PL] = arg2;
510 rv[RTA] = arg1;
511 } else if (arg2) {
512 rv[PL] = arg1;
513 rv[RTA] = arg2;
514 } else if (strstr(arg1, "%")) {
515 rv[PL] = arg1;
516 } else {
517 rv[RTA] = arg1;
518 }
519
520 return OK;
521}
488 522
489 printf (" %s\n", "-H, --hostname=HOST"); 523void print_help(void) {
490 printf (" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, reducing system load)")); 524
491 printf (" %s\n", "-w, --warning=THRESHOLD"); 525 print_revision(progname, NP_VERSION);
492 printf (" %s\n", _("warning threshold pair")); 526
493 printf (" %s\n", "-c, --critical=THRESHOLD"); 527 printf("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n");
494 printf (" %s\n", _("critical threshold pair")); 528 printf(COPYRIGHT, copyright, email);
495 printf (" %s\n", "-a, --alive"); 529
496 printf (" %s\n", _("Return OK after first successful reply")); 530 printf("%s\n",
497 printf (" %s\n", "-b, --bytes=INTEGER"); 531 _("This plugin will use the fping command to ping the specified host for a fast check"));
498 printf (" %s (default: %d)\n", _("size of ICMP packet"),PACKET_SIZE); 532
499 printf (" %s\n", "-n, --number=INTEGER"); 533 printf("%s\n", _("Note that it is necessary to set the suid flag on fping."));
500 printf (" %s (default: %d)\n", _("number of ICMP packets to send"),PACKET_COUNT); 534
501 printf (" %s\n", "-T, --target-timeout=INTEGER"); 535 printf("\n\n");
502 printf (" %s (default: fping's default for -t)\n", _("Target timeout (ms)")); 536
503 printf (" %s\n", "-i, --interval=INTEGER"); 537 print_usage();
504 printf (" %s (default: fping's default for -p)\n", _("Interval (ms) between sending packets")); 538
505 printf (" %s\n", "-S, --sourceip=HOST"); 539 printf(UT_HELP_VRSN);
506 printf (" %s\n", _("name or IP Address of sourceip")); 540 printf(UT_EXTRA_OPTS);
507 printf (" %s\n", "-I, --sourceif=IF"); 541
508 printf (" %s\n", _("source interface name")); 542 printf(UT_IPv46);
509 printf (UT_VERBOSE); 543
510 printf ("\n"); 544 printf(" %s\n", "-H, --hostname=HOST");
511 printf (" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)")); 545 printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, "
512 printf (" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of")); 546 "reducing system load)"));
513 printf (" %s\n", _("packet loss to trigger an alarm state.")); 547 printf(" %s\n", "-w, --warning=THRESHOLD");
548 printf(" %s\n", _("warning threshold pair"));
549 printf(" %s\n", "-c, --critical=THRESHOLD");
550 printf(" %s\n", _("critical threshold pair"));
551 printf(" %s\n", "-a, --alive");
552 printf(" %s\n", _("Return OK after first successful reply"));
553 printf(" %s\n", "-b, --bytes=INTEGER");
554 printf(" %s (default: %d)\n", _("size of ICMP packet"), PACKET_SIZE);
555 printf(" %s\n", "-n, --number=INTEGER");
556 printf(" %s (default: %d)\n", _("number of ICMP packets to send"), PACKET_COUNT);
557 printf(" %s\n", "-T, --target-timeout=INTEGER");
558 printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)"));
559 printf(" %s\n", "-i, --interval=INTEGER");
560 printf(" %s (default: fping's default for -p)\n",
561 _("Interval (ms) between sending packets"));
562 printf(" %s\n", "-S, --sourceip=HOST");
563 printf(" %s\n", _("name or IP Address of sourceip"));
564 printf(" %s\n", "-I, --sourceif=IF");
565 printf(" %s\n", _("source interface name"));
566 printf(" %s\n", "-M, --dontfrag");
567 printf(" %s\n", _("set the Don't Fragment flag"));
568 printf(" %s\n", "-R, --random");
569 printf(" %s\n", _("random packet data (to foil link data compression)"));
570#ifdef FPING_VERSION_5_2_OR_HIGHER
571 printf(" %s\n", "--fwmark=INTEGER");
572 printf(" %s\n", _("set the routing mark to INTEGER (fping option)"));
573# ifdef FPING_VERSION_5_3_OR_HIGHER
574 printf(" %s\n", "--icmp-timestamp");
575 printf(" %s\n", _("use ICMP Timestamp instead of ICMP Echo (fping option)"));
576 printf(" %s\n", "--check-source");
577 printf(" %s\n", _("discard replies not from target address (fping option)"));
578# endif
579#endif
580 printf(UT_VERBOSE);
581 printf("\n");
582 printf(" %s\n",
583 _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)"));
584 printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of"));
585 printf(" %s\n", _("packet loss to trigger an alarm state."));
514 586
515 printf ("\n"); 587 printf("\n");
516 printf (" %s\n", _("IPv4 is used by default. Specify -6 to use IPv6.")); 588 printf(" %s\n", _("IPv4 is used by default. Specify -6 to use IPv6."));
517 589
518 printf (UT_SUPPORT); 590 printf(UT_SUPPORT);
519} 591}
520 592
521 593void print_usage(void) {
522void 594 printf("%s\n", _("Usage:"));
523print_usage (void) 595 printf(" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n",
524{ 596 progname);
525 printf ("%s\n", _("Usage:"));
526 printf (" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", progname);
527} 597}
diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h
new file mode 100644
index 00000000..d3e50565
--- /dev/null
+++ b/plugins/check_fping.d/config.h
@@ -0,0 +1,81 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PACKET_SIZE = 56,
8 PACKET_COUNT = 1,
9};
10
11typedef struct {
12 char *server_name;
13 char *sourceip;
14 char *sourceif;
15 int packet_size;
16 int packet_count;
17 int target_timeout;
18 int packet_interval;
19 bool randomize_packet_data;
20 bool dontfrag;
21 bool alive_p;
22
23 double crta;
24 bool crta_p;
25 double wrta;
26 bool wrta_p;
27
28 int cpl;
29 bool cpl_p;
30 int wpl;
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 // only available with fping version >= 5.3
40 // Setting icmp_timestamp tells fping to use ICMP Timestamp (ICMP type 13) instead
41 // of ICMP Echo
42 bool icmp_timestamp;
43
44 // Setting check_source lets fping discard replies which are not from the target address
45 bool check_source;
46} check_fping_config;
47
48check_fping_config check_fping_config_init() {
49 check_fping_config tmp = {
50 .server_name = NULL,
51 .sourceip = NULL,
52 .sourceif = NULL,
53 .packet_size = PACKET_SIZE,
54 .packet_count = PACKET_COUNT,
55 .target_timeout = 0,
56 .packet_interval = 0,
57 .randomize_packet_data = false,
58 .dontfrag = false,
59 .alive_p = false,
60
61 .crta = 0,
62 .crta_p = false,
63 .wrta = 0,
64 .wrta_p = false,
65
66 .cpl = 0,
67 .cpl_p = false,
68 .wpl = 0,
69 .wpl_p = false,
70
71 // only available with fping version >= 5.2
72 .fwmark = 0,
73 .fwmark_set = false, // just to be deterministic
74
75 // only available with fping version >= 5.3
76 .icmp_timestamp = false,
77 .check_source = false,
78
79 };
80 return tmp;
81}
diff --git a/plugins/check_game.c b/plugins/check_game.c
index ca126973..48ec6883 100644
--- a/plugins/check_game.c
+++ b/plugins/check_game.c
@@ -1,335 +1,329 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_game plugin 3 * Monitoring check_game plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2002-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_game plugin 10 * This file contains the check_game plugin
11* 11 *
12* This plugin tests game server connections with the specified host. 12 * This plugin tests game server connections with the specified host.
13* using the qstat program 13 * using the qstat program
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_game"; 32const char *progname = "check_game";
33const char *copyright = "2002-2007"; 33const char *copyright = "2002-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "utils.h" 37#include "utils.h"
38#include "runcmd.h" 38#include "runcmd.h"
39#include "check_game.d/config.h"
40#include "../lib/monitoringplug.h"
39 41
40int process_arguments (int, char **); 42typedef struct {
41int validate_arguments (void); 43 int errorcode;
42void print_help (void); 44 check_game_config config;
43void print_usage (void); 45} check_game_config_wrapper;
44 46
45#define QSTAT_DATA_DELIMITER "," 47static check_game_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
48static void print_help(void);
49void print_usage(void);
46 50
47#define QSTAT_HOST_ERROR "ERROR" 51#define QSTAT_DATA_DELIMITER ","
48#define QSTAT_HOST_DOWN "DOWN"
49#define QSTAT_HOST_TIMEOUT "TIMEOUT"
50#define QSTAT_MAX_RETURN_ARGS 12
51
52char *server_ip;
53char *game_type;
54int port = 0;
55
56bool verbose = false;
57
58int qstat_game_players_max = -1;
59int qstat_game_players = -1;
60int qstat_game_field = -1;
61int qstat_map_field = -1;
62int qstat_ping_field = -1;
63
64
65int
66main (int argc, char **argv)
67{
68 char *command_line;
69 int result = STATE_UNKNOWN;
70 char *p, *ret[QSTAT_MAX_RETURN_ARGS];
71 size_t i = 0;
72 output chld_out;
73
74 setlocale (LC_ALL, "");
75 bindtextdomain (PACKAGE, LOCALEDIR);
76 textdomain (PACKAGE);
77
78 /* Parse extra opts if any */
79 argv=np_extra_opts (&argc, argv, progname);
80
81 if (process_arguments (argc, argv) == ERROR)
82 usage_va(_("Could not parse arguments"));
83
84 result = STATE_OK;
85
86 /* create the command line to execute */
87 xasprintf (&command_line, "%s -raw %s -%s %s",
88 PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, game_type, server_ip);
89
90 if (port)
91 xasprintf (&command_line, "%s:%-d", command_line, port);
92
93 if (verbose)
94 printf ("%s\n", command_line);
95
96 /* run the command. historically, this plugin ignores output on stderr,
97 * as well as return status of the qstat program */
98 (void)np_runcmd(command_line, &chld_out, NULL, 0);
99
100 /* sanity check */
101 /* was thinking about running qstat without any options, capturing the
102 -default line, parsing it & making an array of all know server types
103 but thought this would be too much hassle considering this is a tool
104 for intelligent sysadmins (ha). Could put a static array of known
105 server types in a header file but then we'd be limiting ourselves
106
107 In the end, I figured I'd simply let an error occur & then trap it
108 */
109
110 if (!strncmp (chld_out.line[0], "unknown option", 14)) {
111 printf (_("CRITICAL - Host type parameter incorrect!\n"));
112 result = STATE_CRITICAL;
113 return result;
114 }
115
116 p = (char *) strtok (chld_out.line[0], QSTAT_DATA_DELIMITER);
117 while (p != NULL) {
118 ret[i] = p;
119 p = (char *) strtok (NULL, QSTAT_DATA_DELIMITER);
120 i++;
121 if (i >= QSTAT_MAX_RETURN_ARGS)
122 break;
123 }
124
125 if (strstr (ret[2], QSTAT_HOST_ERROR)) {
126 printf (_("CRITICAL - Host not found\n"));
127 result = STATE_CRITICAL;
128 }
129 else if (strstr (ret[2], QSTAT_HOST_DOWN)) {
130 printf (_("CRITICAL - Game server down or unavailable\n"));
131 result = STATE_CRITICAL;
132 }
133 else if (strstr (ret[2], QSTAT_HOST_TIMEOUT)) {
134 printf (_("CRITICAL - Game server timeout\n"));
135 result = STATE_CRITICAL;
136 }
137 else {
138 printf ("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n",
139 ret[qstat_game_players],
140 ret[qstat_game_players_max],
141 ret[qstat_game_field],
142 ret[qstat_map_field],
143 ret[qstat_ping_field],
144 perfdata ("players", atol(ret[qstat_game_players]), "",
145 false, 0, false, 0,
146 true, 0, true, atol(ret[qstat_game_players_max])),
147 fperfdata ("ping", strtod(ret[qstat_ping_field], NULL), "",
148 false, 0, false, 0,
149 true, 0, false, 0));
150 }
151
152 return result;
153}
154 52
53#define QSTAT_HOST_ERROR "ERROR"
54#define QSTAT_HOST_DOWN "DOWN"
55#define QSTAT_HOST_TIMEOUT "TIMEOUT"
56#define QSTAT_MAX_RETURN_ARGS 12
155 57
156int 58static bool verbose = false;
157process_arguments (int argc, char **argv) 59
158{ 60int main(int argc, char **argv) {
159 int c; 61 setlocale(LC_ALL, "");
160 62 bindtextdomain(PACKAGE, LOCALEDIR);
161 int opt_index = 0; 63 textdomain(PACKAGE);
162 static struct option long_opts[] = { 64
163 {"help", no_argument, 0, 'h'}, 65 /* Parse extra opts if any */
164 {"version", no_argument, 0, 'V'}, 66 argv = np_extra_opts(&argc, argv, progname);
165 {"verbose", no_argument, 0, 'v'}, 67
166 {"timeout", required_argument, 0, 't'}, 68 check_game_config_wrapper tmp = process_arguments(argc, argv);
167 {"hostname", required_argument, 0, 'H'}, 69
168 {"port", required_argument, 0, 'P'}, 70 if (tmp.errorcode == ERROR) {
169 {"game-type", required_argument, 0, 'G'}, 71 usage_va(_("Could not parse arguments"));
170 {"map-field", required_argument, 0, 'm'}, 72 }
171 {"ping-field", required_argument, 0, 'p'}, 73
172 {"game-field", required_argument, 0, 'g'}, 74 check_game_config config = tmp.config;
173 {"players-field", required_argument, 0, 129}, 75
174 {"max-players-field", required_argument, 0, 130}, 76 mp_state_enum result = STATE_OK;
175 {0, 0, 0, 0} 77
176 }; 78 /* create the command line to execute */
177 79 char *command_line = NULL;
178 if (argc < 2) 80 xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER,
179 return ERROR; 81 config.game_type, config.server_ip);
180 82
181 for (c = 1; c < argc; c++) { 83 if (config.port) {
182 if (strcmp ("-mf", argv[c]) == 0) 84 xasprintf(&command_line, "%s:%-d", command_line, config.port);
183 strcpy (argv[c], "-m"); 85 }
184 else if (strcmp ("-pf", argv[c]) == 0) 86
185 strcpy (argv[c], "-p"); 87 if (verbose) {
186 else if (strcmp ("-gf", argv[c]) == 0) 88 printf("%s\n", command_line);
187 strcpy (argv[c], "-g"); 89 }
188 } 90
189 91 /* run the command. historically, this plugin ignores output on stderr,
190 while (1) { 92 * as well as return status of the qstat program */
191 c = getopt_long (argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index); 93 output chld_out = {};
192 94 (void)np_runcmd(command_line, &chld_out, NULL, 0);
193 if (c == -1 || c == EOF) 95
194 break; 96 /* sanity check */
195 97 /* was thinking about running qstat without any options, capturing the
196 switch (c) { 98 -default line, parsing it & making an array of all know server types
197 case 'h': /* help */ 99 but thought this would be too much hassle considering this is a tool
198 print_help (); 100 for intelligent sysadmins (ha). Could put a static array of known
199 exit (STATE_UNKNOWN); 101 server types in a header file but then we'd be limiting ourselves
200 case 'V': /* version */ 102
201 print_revision (progname, NP_VERSION); 103 In the end, I figured I'd simply let an error occur & then trap it
202 exit (STATE_UNKNOWN); 104 */
203 case 'v': /* version */ 105
204 verbose = true; 106 if (!strncmp(chld_out.line[0], "unknown option", strlen("unknown option"))) {
205 break; 107 printf(_("CRITICAL - Host type parameter incorrect!\n"));
206 case 't': /* timeout period */ 108 result = STATE_CRITICAL;
207 timeout_interval = atoi (optarg); 109 exit(result);
208 break; 110 }
209 case 'H': /* hostname */ 111
210 if (strlen (optarg) >= MAX_HOST_ADDRESS_LENGTH) 112 char *ret[QSTAT_MAX_RETURN_ARGS];
211 die (STATE_UNKNOWN, _("Input buffer overflow\n")); 113 size_t i = 0;
212 server_ip = optarg; 114 char *sequence = strtok(chld_out.line[0], QSTAT_DATA_DELIMITER);
213 break; 115 while (sequence != NULL) {
214 case 'P': /* port */ 116 ret[i] = sequence;
215 port = atoi (optarg); 117 sequence = strtok(NULL, QSTAT_DATA_DELIMITER);
216 break; 118 i++;
217 case 'G': /* hostname */ 119 if (i >= QSTAT_MAX_RETURN_ARGS) {
218 if (strlen (optarg) >= MAX_INPUT_BUFFER) 120 break;
219 die (STATE_UNKNOWN, _("Input buffer overflow\n")); 121 }
220 game_type = optarg; 122 }
221 break; 123
222 case 'p': /* index of ping field */ 124 if (strstr(ret[2], QSTAT_HOST_ERROR)) {
223 qstat_ping_field = atoi (optarg); 125 printf(_("CRITICAL - Host not found\n"));
224 if (qstat_ping_field < 0 || qstat_ping_field > QSTAT_MAX_RETURN_ARGS) 126 result = STATE_CRITICAL;
225 return ERROR; 127 } else if (strstr(ret[2], QSTAT_HOST_DOWN)) {
226 break; 128 printf(_("CRITICAL - Game server down or unavailable\n"));
227 case 'm': /* index on map field */ 129 result = STATE_CRITICAL;
228 qstat_map_field = atoi (optarg); 130 } else if (strstr(ret[2], QSTAT_HOST_TIMEOUT)) {
229 if (qstat_map_field < 0 || qstat_map_field > QSTAT_MAX_RETURN_ARGS) 131 printf(_("CRITICAL - Game server timeout\n"));
230 return ERROR; 132 result = STATE_CRITICAL;
231 break; 133 } else {
232 case 'g': /* index of game field */ 134 printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[config.qstat_game_players],
233 qstat_game_field = atoi (optarg); 135 ret[config.qstat_game_players_max], ret[config.qstat_game_field],
234 if (qstat_game_field < 0 || qstat_game_field > QSTAT_MAX_RETURN_ARGS) 136 ret[config.qstat_map_field], ret[config.qstat_ping_field],
235 return ERROR; 137 perfdata("players", atol(ret[config.qstat_game_players]), "", false, 0, false, 0,
236 break; 138 true, 0, true, atol(ret[config.qstat_game_players_max])),
237 case 129: /* index of player count field */ 139 fperfdata("ping", strtod(ret[config.qstat_ping_field], NULL), "", false, 0, false, 0,
238 qstat_game_players = atoi (optarg); 140 true, 0, false, 0));
239 if (qstat_game_players_max == 0) 141 }
240 qstat_game_players_max = qstat_game_players - 1; 142
241 if (qstat_game_players < 0 || qstat_game_players > QSTAT_MAX_RETURN_ARGS) 143 exit(result);
242 return ERROR;
243 break;
244 case 130: /* index of max players field */
245 qstat_game_players_max = atoi (optarg);
246 if (qstat_game_players_max < 0 || qstat_game_players_max > QSTAT_MAX_RETURN_ARGS)
247 return ERROR;
248 break;
249 default: /* args not parsable */
250 usage5();
251 }
252 }
253
254 c = optind;
255 /* first option is the game type */
256 if (!game_type && c<argc)
257 game_type = strdup (argv[c++]);
258
259 /* Second option is the server name */
260 if (!server_ip && c<argc)
261 server_ip = strdup (argv[c++]);
262
263 return validate_arguments ();
264} 144}
265 145
266 146#define players_field_index 129
267int 147#define max_players_field_index 130
268validate_arguments (void) 148
269{ 149check_game_config_wrapper process_arguments(int argc, char **argv) {
270 if (qstat_game_players_max < 0) 150 static struct option long_opts[] = {
271 qstat_game_players_max = 4; 151 {"help", no_argument, 0, 'h'},
272 152 {"version", no_argument, 0, 'V'},
273 if (qstat_game_players < 0) 153 {"verbose", no_argument, 0, 'v'},
274 qstat_game_players = 5; 154 {"timeout", required_argument, 0, 't'},
275 155 {"hostname", required_argument, 0, 'H'},
276 if (qstat_game_field < 0) 156 {"port", required_argument, 0, 'P'},
277 qstat_game_field = 2; 157 {"game-type", required_argument, 0, 'G'},
278 158 {"map-field", required_argument, 0, 'm'},
279 if (qstat_map_field < 0) 159 {"ping-field", required_argument, 0, 'p'},
280 qstat_map_field = 3; 160 {"game-field", required_argument, 0, 'g'},
281 161 {"players-field", required_argument, 0, players_field_index},
282 if (qstat_ping_field < 0) 162 {"max-players-field", required_argument, 0, max_players_field_index},
283 qstat_ping_field = 5; 163 {0, 0, 0, 0}};
284 164
285 return OK; 165 check_game_config_wrapper result = {
166 .config = check_game_config_init(),
167 .errorcode = OK,
168 };
169
170 if (argc < 2) {
171 result.errorcode = ERROR;
172 return result;
173 }
174
175 for (int option_counter = 1; option_counter < argc; option_counter++) {
176 if (strcmp("-mf", argv[option_counter]) == 0) {
177 strcpy(argv[option_counter], "-m");
178 } else if (strcmp("-pf", argv[option_counter]) == 0) {
179 strcpy(argv[option_counter], "-p");
180 } else if (strcmp("-gf", argv[option_counter]) == 0) {
181 strcpy(argv[option_counter], "-g");
182 }
183 }
184
185 int opt_index = 0;
186 while (true) {
187 int option_index = getopt_long(argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index);
188
189 if (CHECK_EOF(option_index)) {
190 break;
191 }
192
193 switch (option_index) {
194 case 'h': /* help */
195 print_help();
196 exit(STATE_UNKNOWN);
197 case 'V': /* version */
198 print_revision(progname, NP_VERSION);
199 exit(STATE_UNKNOWN);
200 case 'v': /* version */
201 verbose = true;
202 break;
203 case 't': /* timeout period */
204 timeout_interval = atoi(optarg);
205 break;
206 case 'H': /* hostname */
207 if (strlen(optarg) >= MAX_HOST_ADDRESS_LENGTH) {
208 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
209 }
210 result.config.server_ip = optarg;
211 break;
212 case 'P': /* port */
213 result.config.port = atoi(optarg);
214 break;
215 case 'G': /* hostname */
216 if (strlen(optarg) >= MAX_INPUT_BUFFER) {
217 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
218 }
219 result.config.game_type = optarg;
220 break;
221 case 'p': /* index of ping field */
222 result.config.qstat_ping_field = atoi(optarg);
223 if (result.config.qstat_ping_field < 0 ||
224 result.config.qstat_ping_field > QSTAT_MAX_RETURN_ARGS) {
225 result.errorcode = ERROR;
226 return result;
227 }
228 break;
229 case 'm': /* index on map field */
230 result.config.qstat_map_field = atoi(optarg);
231 if (result.config.qstat_map_field < 0 ||
232 result.config.qstat_map_field > QSTAT_MAX_RETURN_ARGS) {
233 result.errorcode = ERROR;
234 return result;
235 }
236 break;
237 case 'g': /* index of game field */
238 result.config.qstat_game_field = atoi(optarg);
239 if (result.config.qstat_game_field < 0 ||
240 result.config.qstat_game_field > QSTAT_MAX_RETURN_ARGS) {
241 result.errorcode = ERROR;
242 return result;
243 }
244 break;
245 case players_field_index: /* index of player count field */
246 result.config.qstat_game_players = atoi(optarg);
247 if (result.config.qstat_game_players_max == 0) {
248 result.config.qstat_game_players_max = result.config.qstat_game_players - 1;
249 }
250 if (result.config.qstat_game_players < 0 ||
251 result.config.qstat_game_players > QSTAT_MAX_RETURN_ARGS) {
252 result.errorcode = ERROR;
253 return result;
254 }
255 break;
256 case max_players_field_index: /* index of max players field */
257 result.config.qstat_game_players_max = atoi(optarg);
258 if (result.config.qstat_game_players_max < 0 ||
259 result.config.qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) {
260 result.errorcode = ERROR;
261 return result;
262 }
263 break;
264 default: /* args not parsable */
265 usage5();
266 }
267 }
268
269 int option_counter = optind;
270 /* first option is the game type */
271 if (!result.config.game_type && option_counter < argc) {
272 result.config.game_type = strdup(argv[option_counter++]);
273 }
274
275 /* Second option is the server name */
276 if (!result.config.server_ip && option_counter < argc) {
277 result.config.server_ip = strdup(argv[option_counter++]);
278 }
279
280 return result;
286} 281}
287 282
283void print_help(void) {
284 print_revision(progname, NP_VERSION);
288 285
289void 286 printf("Copyright (c) 1999 Ian Cass, Knowledge Matters Limited\n");
290print_help (void) 287 printf(COPYRIGHT, copyright, email);
291{
292 print_revision (progname, NP_VERSION);
293
294 printf ("Copyright (c) 1999 Ian Cass, Knowledge Matters Limited\n");
295 printf (COPYRIGHT, copyright, email);
296 288
297 printf (_("This plugin tests game server connections with the specified host.")); 289 printf(_("This plugin tests game server connections with the specified host."));
298 290
299 printf ("\n\n"); 291 printf("\n\n");
300 292
301 print_usage (); 293 print_usage();
302 294
303 printf (UT_HELP_VRSN); 295 printf(UT_HELP_VRSN);
304 printf (UT_EXTRA_OPTS); 296 printf(UT_EXTRA_OPTS);
297 printf(" -H, --hostname=ADDRESS\n"
298 " Host name, IP Address, or unix socket (must be an absolute path)\n");
299 printf(" %s\n", "-P");
300 printf(" %s\n", _("Optional port to connect to"));
301 printf(" %s\n", "-g");
302 printf(" %s\n", _("Field number in raw qstat output that contains game name"));
303 printf(" %s\n", "-m");
304 printf(" %s\n", _("Field number in raw qstat output that contains map name"));
305 printf(" %s\n", "-p");
306 printf(" %s\n", _("Field number in raw qstat output that contains ping time"));
305 307
306 printf (" %s\n", "-p"); 308 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
307 printf (" %s\n", _("Optional port of which to connect"));
308 printf (" %s\n", "gf");
309 printf (" %s\n", _("Field number in raw qstat output that contains game name"));
310 printf (" %s\n", "-mf");
311 printf (" %s\n", _("Field number in raw qstat output that contains map name"));
312 printf (" %s\n", "-pf");
313 printf (" %s\n", _("Field number in raw qstat output that contains ping time"));
314 309
315 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 310 printf("\n");
311 printf("%s\n", _("Notes:"));
312 printf(" %s\n",
313 _("This plugin uses the 'qstat' command, the popular game server status query tool."));
314 printf(" %s\n",
315 _("If you don't have the package installed, you will need to download it from"));
316 printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin."));
316 317
317 printf ("\n"); 318 printf(UT_SUPPORT);
318 printf ("%s\n", _("Notes:"));
319 printf (" %s\n", _("This plugin uses the 'qstat' command, the popular game server status query tool."));
320 printf (" %s\n", _("If you don't have the package installed, you will need to download it from"));
321 printf (" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin."));
322
323 printf (UT_SUPPORT);
324} 319}
325 320
326 321void print_usage(void) {
327 322 printf("%s\n", _("Usage:"));
328void 323 printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G "
329print_usage (void) 324 "game-time] [-H hostname] <game> "
330{ 325 "<ip_address>\n",
331 printf ("%s\n", _("Usage:")); 326 progname);
332 printf (" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G game-time] [-H hostname] <game> <ip_address>\n", progname);
333} 327}
334 328
335/****************************************************************************** 329/******************************************************************************
diff --git a/plugins/check_game.d/config.h b/plugins/check_game.d/config.h
new file mode 100644
index 00000000..c95a1ced
--- /dev/null
+++ b/plugins/check_game.d/config.h
@@ -0,0 +1,30 @@
1#pragma once
2#include "../../config.h"
3#include <stddef.h>
4
5typedef struct {
6 char *server_ip;
7 char *game_type;
8 int port;
9
10 int qstat_game_players_max;
11 int qstat_game_players;
12 int qstat_game_field;
13 int qstat_map_field;
14 int qstat_ping_field;
15} check_game_config;
16
17check_game_config check_game_config_init() {
18 check_game_config tmp = {
19 .server_ip = NULL,
20 .game_type = NULL,
21 .port = 0,
22
23 .qstat_game_players_max = 4,
24 .qstat_game_players = 5,
25 .qstat_map_field = 3,
26 .qstat_game_field = 2,
27 .qstat_ping_field = 5,
28 };
29 return tmp;
30}
diff --git a/plugins/check_hpjd.c b/plugins/check_hpjd.c
index c34bb082..883f1df0 100644
--- a/plugins/check_hpjd.c
+++ b/plugins/check_hpjd.c
@@ -1,47 +1,46 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_hpjd plugin 3 * Monitoring check_hpjd plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 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_hpjd plugin 10 * This file contains the check_hpjd plugin
11* 11 *
12* This plugin tests the STATUS of an HP printer with a JetDirect card. 12 * This plugin tests the STATUS of an HP printer with a JetDirect card.
13* Net-SNMP must be installed on the computer running the plugin. 13 * Net-SNMP must be installed on the computer running the plugin.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_hpjd"; 32const char *progname = "check_hpjd";
33const char *copyright = "2000-2007"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "popen.h" 37#include "popen.h"
38#include "utils.h" 38#include "utils.h"
39#include "netutils.h" 39#include "netutils.h"
40#include "states.h"
41#include "check_hpjd.d/config.h"
40 42
41#define DEFAULT_COMMUNITY "public" 43#define DEFAULT_COMMUNITY "public"
42#define DEFAULT_PORT "161"
43
44const char *option_summary = "-H host [-C community]\n";
45 44
46#define HPJD_LINE_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.1" 45#define HPJD_LINE_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.1"
47#define HPJD_PAPER_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.2" 46#define HPJD_PAPER_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.2"
@@ -56,185 +55,165 @@ const char *option_summary = "-H host [-C community]\n";
56#define HPJD_GD_PAPER_OUTPUT ".1.3.6.1.4.1.11.2.3.9.1.1.2.19" 55#define HPJD_GD_PAPER_OUTPUT ".1.3.6.1.4.1.11.2.3.9.1.1.2.19"
57#define HPJD_GD_STATUS_DISPLAY ".1.3.6.1.4.1.11.2.3.9.1.1.3" 56#define HPJD_GD_STATUS_DISPLAY ".1.3.6.1.4.1.11.2.3.9.1.1.3"
58 57
59#define ONLINE 0 58#define ONLINE 0
60#define OFFLINE 1 59#define OFFLINE 1
61
62int process_arguments (int, char **);
63int validate_arguments (void);
64void print_help (void);
65void print_usage (void);
66 60
67char *community = NULL; 61typedef struct {
68char *address = NULL; 62 int errorcode;
69unsigned int port = 0; 63 check_hpjd_config config;
70int check_paper_out = 1; 64} check_hpjd_config_wrapper;
65static check_hpjd_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
66static void print_help(void);
67void print_usage(void);
71 68
72int 69int main(int argc, char **argv) {
73main (int argc, char **argv) 70 setlocale(LC_ALL, "");
74{ 71 bindtextdomain(PACKAGE, LOCALEDIR);
75 char command_line[1024]; 72 textdomain(PACKAGE);
76 int result = STATE_UNKNOWN;
77 int line;
78 char input_buffer[MAX_INPUT_BUFFER];
79 char query_string[512];
80 char *errmsg;
81 char *temp_buffer;
82 int line_status = ONLINE;
83 int paper_status = 0;
84 int intervention_required = 0;
85 int peripheral_error = 0;
86 int paper_jam = 0;
87 int paper_out = 0;
88 int toner_low = 0;
89 int page_punt = 0;
90 int memory_out = 0;
91 int door_open = 0;
92 int paper_output = 0;
93 char display_message[MAX_INPUT_BUFFER];
94 73
95 errmsg = malloc(MAX_INPUT_BUFFER); 74 /* Parse extra opts if any */
75 argv = np_extra_opts(&argc, argv, progname);
96 76
97 setlocale (LC_ALL, ""); 77 check_hpjd_config_wrapper tmp_config = process_arguments(argc, argv);
98 bindtextdomain (PACKAGE, LOCALEDIR);
99 textdomain (PACKAGE);
100 78
101 /* Parse extra opts if any */ 79 if (tmp_config.errorcode == ERROR) {
102 argv=np_extra_opts (&argc, argv, progname); 80 usage4(_("Could not parse arguments"));
81 }
103 82
104 if (process_arguments (argc, argv) == ERROR) 83 const check_hpjd_config config = tmp_config.config;
105 usage4 (_("Could not parse arguments"));
106 84
85 char query_string[512];
107 /* removed ' 2>1' at end of command 10/27/1999 - EG */ 86 /* removed ' 2>1' at end of command 10/27/1999 - EG */
108 /* create the query string */ 87 /* create the query string */
109 sprintf 88 sprintf(query_string, "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0",
110 (query_string, 89 HPJD_LINE_STATUS, HPJD_PAPER_STATUS, HPJD_INTERVENTION_REQUIRED,
111 "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0", 90 HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW,
112 HPJD_LINE_STATUS, 91 HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT,
113 HPJD_PAPER_STATUS, 92 HPJD_GD_STATUS_DISPLAY);
114 HPJD_INTERVENTION_REQUIRED,
115 HPJD_GD_PERIPHERAL_ERROR,
116 HPJD_GD_PAPER_JAM,
117 HPJD_GD_PAPER_OUT,
118 HPJD_GD_TONER_LOW,
119 HPJD_GD_PAGE_PUNT,
120 HPJD_GD_MEMORY_OUT,
121 HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT, HPJD_GD_STATUS_DISPLAY);
122 93
123 /* get the command to run */ 94 /* get the command to run */
124 sprintf (command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", 95 char command_line[1024];
125 PATH_TO_SNMPGET, 96 sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, config.community,
126 community, 97 config.address, config.port, query_string);
127 address,
128 port,
129 query_string);
130 98
131 /* run the command */ 99 /* run the command */
132 child_process = spopen (command_line); 100 child_process = spopen(command_line);
133 if (child_process == NULL) { 101 if (child_process == NULL) {
134 printf (_("Could not open pipe: %s\n"), command_line); 102 printf(_("Could not open pipe: %s\n"), command_line);
135 return STATE_UNKNOWN; 103 return STATE_UNKNOWN;
136 } 104 }
137 105
138 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 106 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
139 if (child_stderr == NULL) { 107 if (child_stderr == NULL) {
140 printf (_("Could not open stderr for %s\n"), command_line); 108 printf(_("Could not open stderr for %s\n"), command_line);
141 } 109 }
142 110
143 result = STATE_OK; 111 mp_state_enum result = STATE_OK;
144 112
145 line = 0; 113 int line_status = ONLINE;
146 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 114 int paper_status = 0;
115 int intervention_required = 0;
116 int peripheral_error = 0;
117 int paper_jam = 0;
118 int paper_out = 0;
119 int toner_low = 0;
120 int page_punt = 0;
121 int memory_out = 0;
122 int door_open = 0;
123 int paper_output = 0;
124 char display_message[MAX_INPUT_BUFFER];
125
126 char input_buffer[MAX_INPUT_BUFFER];
127 char *errmsg = malloc(MAX_INPUT_BUFFER);
128 int line = 0;
147 129
130 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
148 /* strip the newline character from the end of the input */ 131 /* strip the newline character from the end of the input */
149 if (input_buffer[strlen (input_buffer) - 1] == '\n') 132 if (input_buffer[strlen(input_buffer) - 1] == '\n') {
150 input_buffer[strlen (input_buffer) - 1] = 0; 133 input_buffer[strlen(input_buffer) - 1] = 0;
134 }
151 135
152 line++; 136 line++;
153 137
154 temp_buffer = strtok (input_buffer, "="); 138 char *temp_buffer = strtok(input_buffer, "=");
155 temp_buffer = strtok (NULL, "="); 139 temp_buffer = strtok(NULL, "=");
156 140
157 if (temp_buffer == NULL && line < 13) { 141 if (temp_buffer == NULL && line < 13) {
158 142 result = STATE_UNKNOWN;
159 result = STATE_UNKNOWN; 143 strcpy(errmsg, input_buffer);
160 strcpy (errmsg, input_buffer);
161
162 } else { 144 } else {
163
164 switch (line) { 145 switch (line) {
165 146 case 1: /* 1st line should contain the line status */
166 case 1: /* 1st line should contain the line status */ 147 line_status = atoi(temp_buffer);
167 line_status = atoi (temp_buffer);
168 break; 148 break;
169 case 2: /* 2nd line should contain the paper status */ 149 case 2: /* 2nd line should contain the paper status */
170 paper_status = atoi (temp_buffer); 150 paper_status = atoi(temp_buffer);
171 break; 151 break;
172 case 3: /* 3rd line should be intervention required */ 152 case 3: /* 3rd line should be intervention required */
173 intervention_required = atoi (temp_buffer); 153 intervention_required = atoi(temp_buffer);
174 break; 154 break;
175 case 4: /* 4th line should be peripheral error */ 155 case 4: /* 4th line should be peripheral error */
176 peripheral_error = atoi (temp_buffer); 156 peripheral_error = atoi(temp_buffer);
177 break; 157 break;
178 case 5: /* 5th line should contain the paper jam status */ 158 case 5: /* 5th line should contain the paper jam status */
179 paper_jam = atoi (temp_buffer); 159 paper_jam = atoi(temp_buffer);
180 break; 160 break;
181 case 6: /* 6th line should contain the paper out status */ 161 case 6: /* 6th line should contain the paper out status */
182 paper_out = atoi (temp_buffer); 162 paper_out = atoi(temp_buffer);
183 break; 163 break;
184 case 7: /* 7th line should contain the toner low status */ 164 case 7: /* 7th line should contain the toner low status */
185 toner_low = atoi (temp_buffer); 165 toner_low = atoi(temp_buffer);
186 break; 166 break;
187 case 8: /* did data come too slow for engine */ 167 case 8: /* did data come too slow for engine */
188 page_punt = atoi (temp_buffer); 168 page_punt = atoi(temp_buffer);
189 break; 169 break;
190 case 9: /* did we run out of memory */ 170 case 9: /* did we run out of memory */
191 memory_out = atoi (temp_buffer); 171 memory_out = atoi(temp_buffer);
192 break; 172 break;
193 case 10: /* is there a door open */ 173 case 10: /* is there a door open */
194 door_open = atoi (temp_buffer); 174 door_open = atoi(temp_buffer);
195 break; 175 break;
196 case 11: /* is output tray full */ 176 case 11: /* is output tray full */
197 paper_output = atoi (temp_buffer); 177 paper_output = atoi(temp_buffer);
198 break; 178 break;
199 case 12: /* display panel message */ 179 case 12: /* display panel message */
200 strcpy (display_message, temp_buffer + 1); 180 strcpy(display_message, temp_buffer + 1);
201 break; 181 break;
202 default: /* fold multiline message */ 182 default: /* fold multiline message */
203 strncat (display_message, input_buffer, 183 strncat(display_message, input_buffer,
204 sizeof (display_message) - strlen (display_message) - 1); 184 sizeof(display_message) - strlen(display_message) - 1);
205 } 185 }
206
207 } 186 }
208 187
209 /* break out of the read loop if we encounter an error */ 188 /* break out of the read loop if we encounter an error */
210 if (result != STATE_OK) 189 if (result != STATE_OK) {
211 break; 190 break;
191 }
212 } 192 }
213 193
214 /* WARNING if output found on stderr */ 194 /* WARNING if output found on stderr */
215 if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { 195 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
216 result = max_state (result, STATE_WARNING); 196 result = max_state(result, STATE_WARNING);
217 /* remove CRLF */ 197 /* remove CRLF */
218 if (input_buffer[strlen (input_buffer) - 1] == '\n') 198 if (input_buffer[strlen(input_buffer) - 1] == '\n') {
219 input_buffer[strlen (input_buffer) - 1] = 0; 199 input_buffer[strlen(input_buffer) - 1] = 0;
220 sprintf (errmsg, "%s", input_buffer ); 200 }
221 201 sprintf(errmsg, "%s", input_buffer);
222 } 202 }
223 203
224 /* close stderr */ 204 /* close stderr */
225 (void) fclose (child_stderr); 205 (void)fclose(child_stderr);
226 206
227 /* close the pipe */ 207 /* close the pipe */
228 if (spclose (child_process)) 208 if (spclose(child_process)) {
229 result = max_state (result, STATE_WARNING); 209 result = max_state(result, STATE_WARNING);
210 }
230 211
231 /* if there wasn't any output, display an error */ 212 /* if there wasn't any output, display an error */
232 if (line == 0) { 213 if (line == 0) {
233
234 /* might not be the problem, but most likely is. */ 214 /* might not be the problem, but most likely is. */
235 result = STATE_UNKNOWN ; 215 result = STATE_UNKNOWN;
236 xasprintf (&errmsg, "%s : Timeout from host %s\n", errmsg, address ); 216 xasprintf(&errmsg, "%s : Timeout from host %s\n", errmsg, config.address);
237
238 } 217 }
239 218
240 /* if we had no read errors, check the printer status results... */ 219 /* if we had no read errors, check the printer status results... */
@@ -242,201 +221,171 @@ main (int argc, char **argv)
242 221
243 if (paper_jam) { 222 if (paper_jam) {
244 result = STATE_WARNING; 223 result = STATE_WARNING;
245 strcpy (errmsg, _("Paper Jam")); 224 strcpy(errmsg, _("Paper Jam"));
246 } 225 } else if (paper_out) {
247 else if (paper_out) { 226 if (config.check_paper_out) {
248 if (check_paper_out)
249 result = STATE_WARNING; 227 result = STATE_WARNING;
250 strcpy (errmsg, _("Out of Paper")); 228 }
251 } 229 strcpy(errmsg, _("Out of Paper"));
252 else if (line_status == OFFLINE) { 230 } else if (line_status == OFFLINE) {
253 if (strcmp (errmsg, "POWERSAVE ON") != 0) { 231 if (strcmp(errmsg, "POWERSAVE ON") != 0) {
254 result = STATE_WARNING; 232 result = STATE_WARNING;
255 strcpy (errmsg, _("Printer Offline")); 233 strcpy(errmsg, _("Printer Offline"));
256 } 234 }
257 } 235 } else if (peripheral_error) {
258 else if (peripheral_error) {
259 result = STATE_WARNING; 236 result = STATE_WARNING;
260 strcpy (errmsg, _("Peripheral Error")); 237 strcpy(errmsg, _("Peripheral Error"));
261 } 238 } else if (intervention_required) {
262 else if (intervention_required) {
263 result = STATE_WARNING; 239 result = STATE_WARNING;
264 strcpy (errmsg, _("Intervention Required")); 240 strcpy(errmsg, _("Intervention Required"));
265 } 241 } else if (toner_low) {
266 else if (toner_low) {
267 result = STATE_WARNING; 242 result = STATE_WARNING;
268 strcpy (errmsg, _("Toner Low")); 243 strcpy(errmsg, _("Toner Low"));
269 } 244 } else if (memory_out) {
270 else if (memory_out) {
271 result = STATE_WARNING; 245 result = STATE_WARNING;
272 strcpy (errmsg, _("Insufficient Memory")); 246 strcpy(errmsg, _("Insufficient Memory"));
273 } 247 } else if (door_open) {
274 else if (door_open) {
275 result = STATE_WARNING; 248 result = STATE_WARNING;
276 strcpy (errmsg, _("A Door is Open")); 249 strcpy(errmsg, _("A Door is Open"));
277 } 250 } else if (paper_output) {
278 else if (paper_output) {
279 result = STATE_WARNING; 251 result = STATE_WARNING;
280 strcpy (errmsg, _("Output Tray is Full")); 252 strcpy(errmsg, _("Output Tray is Full"));
281 } 253 } else if (page_punt) {
282 else if (page_punt) {
283 result = STATE_WARNING; 254 result = STATE_WARNING;
284 strcpy (errmsg, _("Data too Slow for Engine")); 255 strcpy(errmsg, _("Data too Slow for Engine"));
285 } 256 } else if (paper_status) {
286 else if (paper_status) {
287 result = STATE_WARNING; 257 result = STATE_WARNING;
288 strcpy (errmsg, _("Unknown Paper Error")); 258 strcpy(errmsg, _("Unknown Paper Error"));
289 } 259 }
290 } 260 }
291 261
292 if (result == STATE_OK) 262 if (result == STATE_OK) {
293 printf (_("Printer ok - (%s)\n"), display_message); 263 printf(_("Printer ok - (%s)\n"), display_message);
294 264 } else if (result == STATE_UNKNOWN) {
295 else if (result == STATE_UNKNOWN) { 265 printf("%s\n", errmsg);
296
297 printf ("%s\n", errmsg);
298
299 /* if printer could not be reached, escalate to critical */ 266 /* if printer could not be reached, escalate to critical */
300 if (strstr (errmsg, "Timeout")) 267 if (strstr(errmsg, "Timeout")) {
301 result = STATE_CRITICAL; 268 result = STATE_CRITICAL;
269 }
270 } else if (result == STATE_WARNING) {
271 printf("%s (%s)\n", errmsg, display_message);
302 } 272 }
303 273
304 else if (result == STATE_WARNING) 274 exit(result);
305 printf ("%s (%s)\n", errmsg, display_message);
306
307 return result;
308} 275}
309 276
310
311/* process command-line arguments */ 277/* process command-line arguments */
312int 278check_hpjd_config_wrapper process_arguments(int argc, char **argv) {
313process_arguments (int argc, char **argv) 279 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
314{ 280 {"community", required_argument, 0, 'C'},
315 int c; 281 /* {"critical", required_argument,0,'c'}, */
316 282 /* {"warning", required_argument,0,'w'}, */
317 int option = 0; 283 {"port", required_argument, 0, 'p'},
318 static struct option longopts[] = { 284 {"version", no_argument, 0, 'V'},
319 {"hostname", required_argument, 0, 'H'}, 285 {"help", no_argument, 0, 'h'},
320 {"community", required_argument, 0, 'C'}, 286 {0, 0, 0, 0}};
321/* {"critical", required_argument,0,'c'}, */ 287
322/* {"warning", required_argument,0,'w'}, */ 288 check_hpjd_config_wrapper result = {
323 {"port", required_argument,0,'p'}, 289 .errorcode = OK,
324 {"version", no_argument, 0, 'V'}, 290 .config = check_hpjd_config_init(),
325 {"help", no_argument, 0, 'h'},
326 {0, 0, 0, 0}
327 }; 291 };
328 292
329 if (argc < 2) 293 if (argc < 2) {
330 return ERROR; 294 result.errorcode = ERROR;
331 295 return result;
296 }
332 297
333 while (1) { 298 int option = 0;
334 c = getopt_long (argc, argv, "+hVH:C:p:D", longopts, &option); 299 while (true) {
300 int option_index = getopt_long(argc, argv, "+hVH:C:p:D", longopts, &option);
335 301
336 if (c == -1 || c == EOF || c == 1) 302 if (CHECK_EOF(option_index) || option_index == 1) {
337 break; 303 break;
304 }
338 305
339 switch (c) { 306 switch (option_index) {
340 case 'H': /* hostname */ 307 case 'H': /* hostname */
341 if (is_host (optarg)) { 308 if (is_host(optarg)) {
342 address = strscpy(address, optarg) ; 309 result.config.address = strscpy(result.config.address, optarg);
343 } 310 } else {
344 else { 311 usage2(_("Invalid hostname/address"), optarg);
345 usage2 (_("Invalid hostname/address"), optarg);
346 } 312 }
347 break; 313 break;
348 case 'C': /* community */ 314 case 'C': /* community */
349 community = strscpy (community, optarg); 315 result.config.community = strscpy(result.config.community, optarg);
350 break; 316 break;
351 case 'p': 317 case 'p':
352 if (!is_intpos(optarg)) 318 if (!is_intpos(optarg)) {
353 usage2 (_("Port must be a positive short integer"), optarg); 319 usage2(_("Port must be a positive short integer"), optarg);
354 else 320 } else {
355 port = atoi(optarg); 321 result.config.port = atoi(optarg);
322 }
356 break; 323 break;
357 case 'D': /* disable paper out check*/ 324 case 'D': /* disable paper out check*/
358 check_paper_out = 0; 325 result.config.check_paper_out = false;
359 break; 326 break;
360 case 'V': /* version */ 327 case 'V': /* version */
361 print_revision (progname, NP_VERSION); 328 print_revision(progname, NP_VERSION);
362 exit (STATE_UNKNOWN); 329 exit(STATE_UNKNOWN);
363 case 'h': /* help */ 330 case 'h': /* help */
364 print_help (); 331 print_help();
365 exit (STATE_UNKNOWN); 332 exit(STATE_UNKNOWN);
366 case '?': /* help */ 333 case '?': /* help */
367 usage5 (); 334 usage5();
368 } 335 }
369 } 336 }
370 337
371 c = optind; 338 int c = optind;
372 if (address == NULL) { 339 if (result.config.address == NULL) {
373 if (is_host (argv[c])) { 340 if (is_host(argv[c])) {
374 address = argv[c++]; 341 result.config.address = argv[c++];
375 } 342 } else {
376 else { 343 usage2(_("Invalid hostname/address"), argv[c]);
377 usage2 (_("Invalid hostname/address"), argv[c]);
378 } 344 }
379 } 345 }
380 346
381 if (community == NULL) { 347 if (result.config.community == NULL) {
382 if (argv[c] != NULL ) 348 if (argv[c] != NULL) {
383 community = argv[c]; 349 result.config.community = argv[c];
384 else 350 } else {
385 community = strdup (DEFAULT_COMMUNITY); 351 result.config.community = strdup(DEFAULT_COMMUNITY);
386 } 352 }
387
388 if (port == 0) {
389 port = atoi(DEFAULT_PORT);
390 } 353 }
391 354
392 return validate_arguments (); 355 return result;
393}
394
395
396int
397validate_arguments (void)
398{
399 return OK;
400} 356}
401 357
358void print_help(void) {
359 print_revision(progname, NP_VERSION);
402 360
403void 361 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
404print_help (void) 362 printf(COPYRIGHT, copyright, email);
405{
406 print_revision (progname, NP_VERSION);
407
408 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
409 printf (COPYRIGHT, copyright, email);
410 363
411 printf ("%s\n", _("This plugin tests the STATUS of an HP printer with a JetDirect card.")); 364 printf("%s\n", _("This plugin tests the STATUS of an HP printer with a JetDirect card."));
412 printf ("%s\n", _("Net-snmp must be installed on the computer running the plugin.")); 365 printf("%s\n", _("Net-snmp must be installed on the computer running the plugin."));
413 366
414 printf ("\n\n"); 367 printf("\n\n");
415 368
416 print_usage (); 369 print_usage();
417 370
418 printf (UT_HELP_VRSN); 371 printf(UT_HELP_VRSN);
419 printf (UT_EXTRA_OPTS); 372 printf(UT_EXTRA_OPTS);
420 373
421 printf (" %s\n", "-C, --community=STRING"); 374 printf(" %s\n", "-C, --community=STRING");
422 printf (" %s", _("The SNMP community name ")); 375 printf(" %s", _("The SNMP community name "));
423 printf (_("(default=%s)"), DEFAULT_COMMUNITY); 376 printf(_("(default=%s)"), DEFAULT_COMMUNITY);
424 printf ("\n"); 377 printf("\n");
425 printf (" %s\n", "-p, --port=STRING"); 378 printf(" %s\n", "-p, --port=STRING");
426 printf (" %s", _("Specify the port to check ")); 379 printf(" %s", _("Specify the port to check "));
427 printf (_("(default=%s)"), DEFAULT_PORT); 380 printf(_("(default=%s)"), DEFAULT_PORT);
428 printf ("\n"); 381 printf("\n");
429 printf (" %s\n", "-D"); 382 printf(" %s\n", "-D");
430 printf (" %s", _("Disable paper check ")); 383 printf(" %s", _("Disable paper check "));
431 384
432 printf (UT_SUPPORT); 385 printf(UT_SUPPORT);
433} 386}
434 387
435 388void print_usage(void) {
436 389 printf("%s\n", _("Usage:"));
437void 390 printf("%s -H host [-C community] [-p port] [-D]\n", progname);
438print_usage (void)
439{
440 printf ("%s\n", _("Usage:"));
441 printf ("%s -H host [-C community] [-p port] [-D]\n", progname);
442} 391}
diff --git a/plugins/check_hpjd.d/config.h b/plugins/check_hpjd.d/config.h
new file mode 100644
index 00000000..e36b7972
--- /dev/null
+++ b/plugins/check_hpjd.d/config.h
@@ -0,0 +1,25 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7#define DEFAULT_PORT "161"
8
9typedef struct {
10 char *address;
11 char *community;
12 unsigned int port;
13 bool check_paper_out;
14
15} check_hpjd_config;
16
17check_hpjd_config check_hpjd_config_init() {
18 check_hpjd_config tmp = {
19 .address = NULL,
20 .community = NULL,
21 .port = (unsigned int)atoi(DEFAULT_PORT),
22 .check_paper_out = true,
23 };
24 return tmp;
25}
diff --git a/plugins/check_http.c b/plugins/check_http.c
index cdf768c9..71f94b91 100644
--- a/plugins/check_http.c
+++ b/plugins/check_http.c
@@ -1,38 +1,38 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_http plugin 3 * Monitoring check_http plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2013 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_http plugin 10 * This file contains the check_http plugin
11* 11 *
12* This plugin tests the HTTP service on the specified host. It can test 12 * This plugin tests the HTTP service on the specified host. It can test
13* normal (http) and secure (https) servers, follow redirects, search for 13 * normal (http) and secure (https) servers, follow redirects, search for
14* strings and regular expressions, check connection times, and report on 14 * strings and regular expressions, check connection times, and report on
15* certificate expiration times. 15 * certificate expiration times.
16* 16 *
17* 17 *
18* This program is free software: you can redistribute it and/or modify 18 * This program is free software: you can redistribute it and/or modify
19* it under the terms of the GNU General Public License as published by 19 * it under the terms of the GNU General Public License as published by
20* the Free Software Foundation, either version 3 of the License, or 20 * the Free Software Foundation, either version 3 of the License, or
21* (at your option) any later version. 21 * (at your option) any later version.
22* 22 *
23* This program is distributed in the hope that it will be useful, 23 * This program is distributed in the hope that it will be useful,
24* but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26* GNU General Public License for more details. 26 * GNU General Public License for more details.
27* 27 *
28* You should have received a copy of the GNU General Public License 28 * You should have received a copy of the GNU General Public License
29* along with this program. If not, see <http://www.gnu.org/licenses/>. 29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30* 30 *
31* 31 *
32*****************************************************************************/ 32 *****************************************************************************/
33 33
34const char *progname = "check_http"; 34const char *progname = "check_http";
35const char *copyright = "1999-2022"; 35const char *copyright = "1999-2024";
36const char *email = "devel@monitoring-plugins.org"; 36const char *email = "devel@monitoring-plugins.org";
37 37
38// Do NOT sort those headers, it will break the build 38// Do NOT sort those headers, it will break the build
@@ -41,7 +41,6 @@ const char *email = "devel@monitoring-plugins.org";
41#include "base64.h" 41#include "base64.h"
42#include "netutils.h" 42#include "netutils.h"
43#include "utils.h" 43#include "utils.h"
44#include "base64.h"
45#include <ctype.h> 44#include <ctype.h>
46 45
47#define STICKY_NONE 0 46#define STICKY_NONE 0
@@ -50,1346 +49,1390 @@ const char *email = "devel@monitoring-plugins.org";
50 49
51#define HTTP_EXPECT "HTTP/1." 50#define HTTP_EXPECT "HTTP/1."
52enum { 51enum {
53 MAX_IPV4_HOSTLENGTH = 255, 52 MAX_IPV4_HOSTLENGTH = 255,
54 HTTP_PORT = 80, 53 HTTP_PORT = 80,
55 HTTPS_PORT = 443, 54 HTTPS_PORT = 443,
56 MAX_PORT = 65535, 55 MAX_PORT = 65535,
57 DEFAULT_MAX_REDIRS = 15 56 DEFAULT_MAX_REDIRS = 15
58}; 57};
59 58
60#ifdef HAVE_SSL 59#ifdef HAVE_SSL
61bool check_cert = false; 60static bool check_cert = false;
62bool continue_after_check_cert = false; 61static bool continue_after_check_cert = false;
63int ssl_version = 0; 62static int ssl_version = 0;
64int days_till_exp_warn, days_till_exp_crit; 63static int days_till_exp_warn, days_till_exp_crit;
65char *randbuff; 64# define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
66X509 *server_cert; 65# define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
67# define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
68# define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
69#else /* ifndef HAVE_SSL */ 66#else /* ifndef HAVE_SSL */
70# define my_recv(buf, len) read(sd, buf, len) 67# define my_recv(buf, len) read(sd, buf, len)
71# define my_send(buf, len) send(sd, buf, len, 0) 68# define my_send(buf, len) send(sd, buf, len, 0)
72#endif /* HAVE_SSL */ 69#endif /* HAVE_SSL */
73bool no_body = false; 70static bool no_body = false;
74int maximum_age = -1; 71static int maximum_age = -1;
75 72
76enum { 73enum {
77 REGS = 2, 74 REGS = 2,
78 MAX_RE_SIZE = 1024 75 MAX_RE_SIZE = 1024
79}; 76};
80#include "regex.h" 77#include "regex.h"
81regex_t preg; 78static regex_t preg;
82regmatch_t pmatch[REGS]; 79static regmatch_t pmatch[REGS];
83char regexp[MAX_RE_SIZE]; 80static char regexp[MAX_RE_SIZE];
84char errbuf[MAX_INPUT_BUFFER]; 81static char errbuf[MAX_INPUT_BUFFER];
85int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; 82static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
86int errcode; 83static int errcode;
87int invert_regex = 0; 84static int invert_regex = 0;
88int state_regex = STATE_CRITICAL; 85static int state_regex = STATE_CRITICAL;
89 86
90struct timeval tv; 87static struct timeval tv;
91struct timeval tv_temp; 88static struct timeval tv_temp;
92 89
93#define HTTP_URL "/" 90#define HTTP_URL "/"
94#define CRLF "\r\n" 91#define CRLF "\r\n"
95 92
96bool specify_port = false; 93static bool specify_port = false;
97int server_port = HTTP_PORT; 94static int server_port = HTTP_PORT;
98int virtual_port = 0; 95static int virtual_port = 0;
99char server_port_text[6] = ""; 96static char server_type[6] = "http";
100char server_type[6] = "http"; 97static char *server_address;
101char *server_address; 98static char *host_name;
102char *host_name; 99static int host_name_length;
103int host_name_length; 100static char *server_url;
104char *server_url; 101static char *user_agent;
105char *user_agent; 102static int server_url_length;
106int server_url_length; 103static int server_expect_yn = 0;
107int server_expect_yn = 0; 104static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
108char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; 105static char header_expect[MAX_INPUT_BUFFER] = "";
109char header_expect[MAX_INPUT_BUFFER] = ""; 106static char string_expect[MAX_INPUT_BUFFER] = "";
110char string_expect[MAX_INPUT_BUFFER] = ""; 107static char *warning_thresholds = NULL;
111char *warning_thresholds = NULL; 108static char *critical_thresholds = NULL;
112char *critical_thresholds = NULL; 109static thresholds *thlds;
113thresholds *thlds; 110static char user_auth[MAX_INPUT_BUFFER] = "";
114char user_auth[MAX_INPUT_BUFFER] = ""; 111static char proxy_auth[MAX_INPUT_BUFFER] = "";
115char proxy_auth[MAX_INPUT_BUFFER] = ""; 112static bool display_html = false;
116bool display_html = false; 113static char **http_opt_headers;
117char **http_opt_headers; 114static int http_opt_headers_count = 0;
118int http_opt_headers_count = 0; 115static int onredirect = STATE_OK;
119int onredirect = STATE_OK; 116static int followsticky = STICKY_NONE;
120int followsticky = STICKY_NONE; 117static bool use_ssl = false;
121bool use_ssl = false; 118static bool use_sni = false;
122bool use_sni = false; 119static bool verbose = false;
123bool verbose = false; 120static bool show_extended_perfdata = false;
124bool show_extended_perfdata = false; 121static bool show_body = false;
125bool show_body = false; 122static int sd;
126int sd; 123static int min_page_len = 0;
127int min_page_len = 0; 124static int max_page_len = 0;
128int max_page_len = 0; 125static int redir_depth = 0;
129int redir_depth = 0; 126static int max_depth = DEFAULT_MAX_REDIRS;
130int max_depth = DEFAULT_MAX_REDIRS; 127static char *http_method;
131char *http_method; 128static char *http_method_proxy;
132char *http_method_proxy; 129static char *http_post_data;
133char *http_post_data; 130static char *http_content_type;
134char *http_content_type; 131static char buffer[MAX_INPUT_BUFFER];
135char buffer[MAX_INPUT_BUFFER]; 132static char *client_cert = NULL;
136char *client_cert = NULL; 133static char *client_privkey = NULL;
137char *client_privkey = NULL;
138 134
139// Forward function declarations 135// Forward function declarations
140bool process_arguments (int, char **); 136static bool process_arguments(int /*argc*/, char ** /*argv*/);
141int check_http (void); 137static int check_http(void);
142void redir (char *pos, char *status_line); 138static void redir(char *pos, char *status_line);
143bool server_type_check(const char *type); 139static bool server_type_check(const char *type);
144int server_port_check(int ssl_flag); 140static int server_port_check(int ssl_flag);
145char *perfd_time (double microsec); 141static char *perfd_time(double elapsed_time);
146char *perfd_time_connect (double microsec); 142static char *perfd_time_connect(double elapsed_time_connect);
147char *perfd_time_ssl (double microsec); 143static char *perfd_time_ssl(double elapsed_time_ssl);
148char *perfd_time_firstbyte (double microsec); 144static char *perfd_time_firstbyte(double elapsed_time_firstbyte);
149char *perfd_time_headers (double microsec); 145static char *perfd_time_headers(double elapsed_time_headers);
150char *perfd_time_transfer (double microsec); 146static char *perfd_time_transfer(double elapsed_time_transfer);
151char *perfd_size (int page_len); 147static char *perfd_size(int page_len);
152void print_help (void); 148void print_help(void);
153void print_usage (void); 149void print_usage(void);
154char *unchunk_content(const char *content); 150static char *unchunk_content(const char *content);
155 151
156int 152int main(int argc, char **argv) {
157main (int argc, char **argv) 153 int result = STATE_UNKNOWN;
158{ 154
159 int result = STATE_UNKNOWN; 155 setlocale(LC_ALL, "");
160 156 bindtextdomain(PACKAGE, LOCALEDIR);
161 setlocale (LC_ALL, ""); 157 textdomain(PACKAGE);
162 bindtextdomain (PACKAGE, LOCALEDIR); 158
163 textdomain (PACKAGE); 159 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
164 160 server_url = strdup(HTTP_URL);
165 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */ 161 server_url_length = strlen(server_url);
166 server_url = strdup(HTTP_URL); 162 xasprintf(&user_agent, "User-Agent: check_http/v%s (monitoring-plugins %s)", NP_VERSION,
167 server_url_length = strlen(server_url); 163 VERSION);
168 xasprintf (&user_agent, "User-Agent: check_http/v%s (monitoring-plugins %s)", 164
169 NP_VERSION, VERSION); 165 /* Parse extra opts if any */
170 166 argv = np_extra_opts(&argc, argv, progname);
171 /* Parse extra opts if any */ 167
172 argv=np_extra_opts (&argc, argv, progname); 168 if (!process_arguments(argc, argv)) {
173 169 usage4(_("Could not parse arguments"));
174 if (process_arguments (argc, argv) == false) 170 }
175 usage4 (_("Could not parse arguments")); 171
176 172 if (display_html) {
177 if (display_html == true) 173 printf("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", use_ssl ? "https" : "http",
178 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", 174 host_name ? host_name : server_address, server_port, server_url);
179 use_ssl ? "https" : "http", host_name ? host_name : server_address, 175 }
180 server_port, server_url); 176
181 177 /* initialize alarm signal handling, set socket timeout, start timer */
182 /* initialize alarm signal handling, set socket timeout, start timer */ 178 (void)signal(SIGALRM, socket_timeout_alarm_handler);
183 (void) signal (SIGALRM, socket_timeout_alarm_handler); 179 (void)alarm(socket_timeout);
184 (void) alarm (socket_timeout); 180 gettimeofday(&tv, NULL);
185 gettimeofday (&tv, NULL); 181
186 182 result = check_http();
187 result = check_http (); 183 return result;
188 return result;
189} 184}
190 185
191/* check whether a file exists */ 186/* check whether a file exists */
192void 187void test_file(char *path) {
193test_file (char *path) 188 if (access(path, R_OK) == 0) {
194{ 189 return;
195 if (access(path, R_OK) == 0) 190 }
196 return; 191 usage2(_("file does not exist or is not readable"), path);
197 usage2 (_("file does not exist or is not readable"), path);
198} 192}
199 193
200/* 194/*
201 * process command-line arguments 195 * process command-line arguments
202 * returns true on success, false otherwise 196 * returns true on success, false otherwise
203 */ 197 */
204bool process_arguments (int argc, char **argv) 198bool process_arguments(int argc, char **argv) {
205{ 199 int c = 1;
206 int c = 1; 200 char *p;
207 char *p; 201 char *temp;
208 char *temp; 202
209 203 enum {
210 enum { 204 INVERT_REGEX = CHAR_MAX + 1,
211 INVERT_REGEX = CHAR_MAX + 1, 205 SNI_OPTION,
212 SNI_OPTION, 206 MAX_REDIRS_OPTION,
213 MAX_REDIRS_OPTION, 207 CONTINUE_AFTER_CHECK_CERT,
214 CONTINUE_AFTER_CHECK_CERT, 208 STATE_REGEX
215 STATE_REGEX 209 };
216 }; 210
217 211 int option = 0;
218 int option = 0; 212 static struct option longopts[] = {
219 static struct option longopts[] = { 213 STD_LONG_OPTS,
220 STD_LONG_OPTS, 214 {"link", no_argument, 0, 'L'},
221 {"link", no_argument, 0, 'L'}, 215 {"nohtml", no_argument, 0, 'n'},
222 {"nohtml", no_argument, 0, 'n'}, 216 {"ssl", optional_argument, 0, 'S'},
223 {"ssl", optional_argument, 0, 'S'}, 217 {"sni", no_argument, 0, SNI_OPTION},
224 {"sni", no_argument, 0, SNI_OPTION}, 218 {"post", required_argument, 0, 'P'},
225 {"post", required_argument, 0, 'P'}, 219 {"method", required_argument, 0, 'j'},
226 {"method", required_argument, 0, 'j'}, 220 {"IP-address", required_argument, 0, 'I'},
227 {"IP-address", required_argument, 0, 'I'}, 221 {"url", required_argument, 0, 'u'},
228 {"url", required_argument, 0, 'u'}, 222 {"port", required_argument, 0, 'p'},
229 {"port", required_argument, 0, 'p'}, 223 {"authorization", required_argument, 0, 'a'},
230 {"authorization", required_argument, 0, 'a'}, 224 {"proxy-authorization", required_argument, 0, 'b'},
231 {"proxy-authorization", required_argument, 0, 'b'}, 225 {"header-string", required_argument, 0, 'd'},
232 {"header-string", required_argument, 0, 'd'}, 226 {"string", required_argument, 0, 's'},
233 {"string", required_argument, 0, 's'}, 227 {"expect", required_argument, 0, 'e'},
234 {"expect", required_argument, 0, 'e'}, 228 {"regex", required_argument, 0, 'r'},
235 {"regex", required_argument, 0, 'r'}, 229 {"ereg", required_argument, 0, 'r'},
236 {"ereg", required_argument, 0, 'r'}, 230 {"eregi", required_argument, 0, 'R'},
237 {"eregi", required_argument, 0, 'R'}, 231 {"linespan", no_argument, 0, 'l'},
238 {"linespan", no_argument, 0, 'l'}, 232 {"onredirect", required_argument, 0, 'f'},
239 {"onredirect", required_argument, 0, 'f'}, 233 {"certificate", required_argument, 0, 'C'},
240 {"certificate", required_argument, 0, 'C'}, 234 {"client-cert", required_argument, 0, 'J'},
241 {"client-cert", required_argument, 0, 'J'}, 235 {"private-key", required_argument, 0, 'K'},
242 {"private-key", required_argument, 0, 'K'}, 236 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
243 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, 237 {"useragent", required_argument, 0, 'A'},
244 {"useragent", required_argument, 0, 'A'}, 238 {"header", required_argument, 0, 'k'},
245 {"header", required_argument, 0, 'k'}, 239 {"no-body", no_argument, 0, 'N'},
246 {"no-body", no_argument, 0, 'N'}, 240 {"max-age", required_argument, 0, 'M'},
247 {"max-age", required_argument, 0, 'M'}, 241 {"content-type", required_argument, 0, 'T'},
248 {"content-type", required_argument, 0, 'T'}, 242 {"pagesize", required_argument, 0, 'm'},
249 {"pagesize", required_argument, 0, 'm'}, 243 {"invert-regex", no_argument, NULL, INVERT_REGEX},
250 {"invert-regex", no_argument, NULL, INVERT_REGEX}, 244 {"state-regex", required_argument, 0, STATE_REGEX},
251 {"state-regex", required_argument, 0, STATE_REGEX}, 245 {"use-ipv4", no_argument, 0, '4'},
252 {"use-ipv4", no_argument, 0, '4'}, 246 {"use-ipv6", no_argument, 0, '6'},
253 {"use-ipv6", no_argument, 0, '6'}, 247 {"extended-perfdata", no_argument, 0, 'E'},
254 {"extended-perfdata", no_argument, 0, 'E'}, 248 {"show-body", no_argument, 0, 'B'},
255 {"show-body", no_argument, 0, 'B'}, 249 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
256 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, 250 {0, 0, 0, 0}};
257 {0, 0, 0, 0} 251
258 }; 252 if (argc < 2) {
259 253 return false;
260 if (argc < 2) 254 }
261 return false; 255
262 256 for (c = 1; c < argc; c++) {
263 for (c = 1; c < argc; c++) { 257 if (strcmp("-to", argv[c]) == 0) {
264 if (strcmp ("-to", argv[c]) == 0) 258 strcpy(argv[c], "-t");
265 strcpy (argv[c], "-t"); 259 }
266 if (strcmp ("-hn", argv[c]) == 0) 260 if (strcmp("-hn", argv[c]) == 0) {
267 strcpy (argv[c], "-H"); 261 strcpy(argv[c], "-H");
268 if (strcmp ("-wt", argv[c]) == 0) 262 }
269 strcpy (argv[c], "-w"); 263 if (strcmp("-wt", argv[c]) == 0) {
270 if (strcmp ("-ct", argv[c]) == 0) 264 strcpy(argv[c], "-w");
271 strcpy (argv[c], "-c"); 265 }
272 if (strcmp ("-nohtml", argv[c]) == 0) 266 if (strcmp("-ct", argv[c]) == 0) {
273 strcpy (argv[c], "-n"); 267 strcpy(argv[c], "-c");
274 } 268 }
275 269 if (strcmp("-nohtml", argv[c]) == 0) {
276 while (1) { 270 strcpy(argv[c], "-n");
277 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NEB", longopts, &option); 271 }
278 if (c == -1 || c == EOF) 272 }
279 break; 273
280 274 while (1) {
281 switch (c) { 275 c = getopt_long(argc, argv,
282 case '?': /* usage */ 276 "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NEB",
283 usage5 (); 277 longopts, &option);
284 break; 278 if (c == -1 || c == EOF) {
285 case 'h': /* help */ 279 break;
286 print_help (); 280 }
287 exit (STATE_UNKNOWN); 281
288 break; 282 switch (c) {
289 case 'V': /* version */ 283 case '?': /* usage */
290 print_revision (progname, NP_VERSION); 284 usage5();
291 exit (STATE_UNKNOWN); 285 break;
292 break; 286 case 'h': /* help */
293 case 't': /* timeout period */ 287 print_help();
294 if (!is_intnonneg (optarg)) 288 exit(STATE_UNKNOWN);
295 usage2 (_("Timeout interval must be a positive integer"), optarg); 289 break;
296 else 290 case 'V': /* version */
297 socket_timeout = atoi (optarg); 291 print_revision(progname, NP_VERSION);
298 break; 292 exit(STATE_UNKNOWN);
299 case 'c': /* critical time threshold */ 293 break;
300 critical_thresholds = optarg; 294 case 't': /* timeout period */
301 break; 295 if (!is_intnonneg(optarg)) {
302 case 'w': /* warning time threshold */ 296 usage2(_("Timeout interval must be a positive integer"), optarg);
303 warning_thresholds = optarg; 297 } else {
304 break; 298 socket_timeout = atoi(optarg);
305 case 'A': /* User Agent String */ 299 }
306 xasprintf (&user_agent, "User-Agent: %s", optarg); 300 break;
307 break; 301 case 'c': /* critical time threshold */
308 case 'k': /* Additional headers */ 302 critical_thresholds = optarg;
309 if (http_opt_headers_count == 0) 303 break;
310 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count)); 304 case 'w': /* warning time threshold */
311 else 305 warning_thresholds = optarg;
312 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count)); 306 break;
313 http_opt_headers[http_opt_headers_count - 1] = optarg; 307 case 'A': /* User Agent String */
314 /* xasprintf (&http_opt_headers, "%s", optarg); */ 308 xasprintf(&user_agent, "User-Agent: %s", optarg);
315 break; 309 break;
316 case 'L': /* show html link */ 310 case 'k': /* Additional headers */
317 display_html = true; 311 if (http_opt_headers_count == 0) {
318 break; 312 http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count));
319 case 'n': /* do not show html link */ 313 } else {
320 display_html = false; 314 http_opt_headers =
321 break; 315 realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count));
322 case 'C': /* Check SSL cert validity */ 316 }
317 http_opt_headers[http_opt_headers_count - 1] = optarg;
318 /* xasprintf (&http_opt_headers, "%s", optarg); */
319 break;
320 case 'L': /* show html link */
321 display_html = true;
322 break;
323 case 'n': /* do not show html link */
324 display_html = false;
325 break;
326 case 'C': /* Check SSL cert validity */
323#ifdef HAVE_SSL 327#ifdef HAVE_SSL
324 if ((temp=strchr(optarg,','))!=NULL) { 328 if ((temp = strchr(optarg, ',')) != NULL) {
325 *temp='\0'; 329 *temp = '\0';
326 if (!is_intnonneg (optarg)) 330 if (!is_intnonneg(optarg)) {
327 usage2 (_("Invalid certificate expiration period"), optarg); 331 usage2(_("Invalid certificate expiration period"), optarg);
328 days_till_exp_warn = atoi(optarg); 332 }
329 *temp=','; 333 days_till_exp_warn = atoi(optarg);
330 temp++; 334 *temp = ',';
331 if (!is_intnonneg (temp)) 335 temp++;
332 usage2 (_("Invalid certificate expiration period"), temp); 336 if (!is_intnonneg(temp)) {
333 days_till_exp_crit = atoi (temp); 337 usage2(_("Invalid certificate expiration period"), temp);
334 } 338 }
335 else { 339 days_till_exp_crit = atoi(temp);
336 days_till_exp_crit=0; 340 } else {
337 if (!is_intnonneg (optarg)) 341 days_till_exp_crit = 0;
338 usage2 (_("Invalid certificate expiration period"), optarg); 342 if (!is_intnonneg(optarg)) {
339 days_till_exp_warn = atoi (optarg); 343 usage2(_("Invalid certificate expiration period"), optarg);
340 } 344 }
341 check_cert = true; 345 days_till_exp_warn = atoi(optarg);
342 goto enable_ssl; 346 }
347 check_cert = true;
348 goto enable_ssl;
343#endif 349#endif
344 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ 350 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
345#ifdef HAVE_SSL 351#ifdef HAVE_SSL
346 continue_after_check_cert = true; 352 continue_after_check_cert = true;
347 break; 353 break;
348#endif 354#endif
349 case 'J': /* use client certificate */ 355 case 'J': /* use client certificate */
350#ifdef HAVE_SSL 356#ifdef HAVE_SSL
351 test_file(optarg); 357 test_file(optarg);
352 client_cert = optarg; 358 client_cert = optarg;
353 goto enable_ssl; 359 goto enable_ssl;
354#endif 360#endif
355 case 'K': /* use client private key */ 361 case 'K': /* use client private key */
356#ifdef HAVE_SSL 362#ifdef HAVE_SSL
357 test_file(optarg); 363 test_file(optarg);
358 client_privkey = optarg; 364 client_privkey = optarg;
359 goto enable_ssl; 365 goto enable_ssl;
360#endif 366#endif
361 case 'S': /* use SSL */ 367 case 'S': /* use SSL */
362#ifdef HAVE_SSL 368#ifdef HAVE_SSL
363 enable_ssl: 369 enable_ssl:
364 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple 370 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps
365 parameters, like -S and -C combinations */ 371 when we include multiple parameters, like -S and -C combinations */
366 use_ssl = true; 372 use_ssl = true;
367 if (c=='S' && optarg != NULL) { 373 if (c == 'S' && optarg != NULL) {
368 int got_plus = strchr(optarg, '+') != NULL; 374 int got_plus = strchr(optarg, '+') != NULL;
369 375
370 if (!strncmp (optarg, "1.2", 3)) 376 if (!strncmp(optarg, "1.2", 3)) {
371 ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2; 377 ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2;
372 else if (!strncmp (optarg, "1.1", 3)) 378 } else if (!strncmp(optarg, "1.1", 3)) {
373 ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1; 379 ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1;
374 else if (optarg[0] == '1') 380 } else if (optarg[0] == '1') {
375 ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1; 381 ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1;
376 else if (optarg[0] == '3') 382 } else if (optarg[0] == '3') {
377 ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3; 383 ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3;
378 else if (optarg[0] == '2') 384 } else if (optarg[0] == '2') {
379 ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2; 385 ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2;
380 else 386 } else {
381 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)")); 387 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with "
382 } 388 "optional '+' suffix)"));
383 if (specify_port == false) 389 }
384 server_port = HTTPS_PORT; 390 }
391 if (!specify_port) {
392 server_port = HTTPS_PORT;
393 }
385#else 394#else
386 /* -C -J and -K fall through to here without SSL */ 395 /* -C -J and -K fall through to here without SSL */
387 usage4 (_("Invalid option - SSL is not available")); 396 usage4(_("Invalid option - SSL is not available"));
388#endif 397#endif
389 break; 398 break;
390 case SNI_OPTION: 399 case SNI_OPTION:
391 use_sni = true; 400 use_sni = true;
392 break; 401 break;
393 case MAX_REDIRS_OPTION: 402 case MAX_REDIRS_OPTION:
394 if (!is_intnonneg (optarg)) 403 if (!is_intnonneg(optarg)) {
395 usage2 (_("Invalid max_redirs count"), optarg); 404 usage2(_("Invalid max_redirs count"), optarg);
396 else { 405 } else {
397 max_depth = atoi (optarg); 406 max_depth = atoi(optarg);
398 } 407 }
399 break; 408 break;
400 case 'f': /* onredirect */ 409 case 'f': /* onredirect */
401 if (!strcmp (optarg, "stickyport")) 410 if (!strcmp(optarg, "stickyport")) {
402 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT; 411 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST | STICKY_PORT;
403 else if (!strcmp (optarg, "sticky")) 412 } else if (!strcmp(optarg, "sticky")) {
404 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST; 413 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
405 else if (!strcmp (optarg, "follow")) 414 } else if (!strcmp(optarg, "follow")) {
406 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE; 415 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
407 else if (!strcmp (optarg, "unknown")) 416 } else if (!strcmp(optarg, "unknown")) {
408 onredirect = STATE_UNKNOWN; 417 onredirect = STATE_UNKNOWN;
409 else if (!strcmp (optarg, "ok")) 418 } else if (!strcmp(optarg, "ok")) {
410 onredirect = STATE_OK; 419 onredirect = STATE_OK;
411 else if (!strcmp (optarg, "warning")) 420 } else if (!strcmp(optarg, "warning")) {
412 onredirect = STATE_WARNING; 421 onredirect = STATE_WARNING;
413 else if (!strcmp (optarg, "critical")) 422 } else if (!strcmp(optarg, "critical")) {
414 onredirect = STATE_CRITICAL; 423 onredirect = STATE_CRITICAL;
415 else usage2 (_("Invalid onredirect option"), optarg); 424 } else {
416 if (verbose) 425 usage2(_("Invalid onredirect option"), optarg);
417 printf(_("option f:%d \n"), onredirect); 426 }
418 break; 427 if (verbose) {
419 /* Note: H, I, and u must be malloc'd or will fail on redirects */ 428 printf(_("option f:%d \n"), onredirect);
420 case 'H': /* Host Name (virtual host) */ 429 }
421 host_name = strdup (optarg); 430 break;
422 if (host_name[0] == '[') { 431 /* Note: H, I, and u must be malloc'd or will fail on redirects */
423 if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */ 432 case 'H': /* Host Name (virtual host) */
424 virtual_port = atoi (p + 2); 433 host_name = strdup(optarg);
425 /* cut off the port */ 434 if (host_name[0] == '[') {
426 host_name_length = strlen (host_name) - strlen (p) - 1; 435 if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */
427 free (host_name); 436 virtual_port = atoi(p + 2);
428 host_name = strndup (optarg, host_name_length); 437 /* cut off the port */
429 if (specify_port == false) 438 host_name_length = strlen(host_name) - strlen(p) - 1;
430 server_port = virtual_port; 439 free(host_name);
431 } 440 host_name = strndup(optarg, host_name_length);
432 } else if ((p = strchr (host_name, ':')) != NULL 441 if (!specify_port) {
433 && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */ 442 server_port = virtual_port;
434 virtual_port = atoi (p); 443 }
435 /* cut off the port */ 444 }
436 host_name_length = strlen (host_name) - strlen (p) - 1; 445 } else if ((p = strchr(host_name, ':')) != NULL &&
437 free (host_name); 446 strchr(++p, ':') == NULL) { /* IPv4:port or host:port */
438 host_name = strndup (optarg, host_name_length); 447 virtual_port = atoi(p);
439 if (specify_port == false) 448 /* cut off the port */
440 server_port = virtual_port; 449 host_name_length = strlen(host_name) - strlen(p) - 1;
441 } 450 free(host_name);
442 break; 451 host_name = strndup(optarg, host_name_length);
443 case 'I': /* Server IP-address */ 452 if (!specify_port) {
444 server_address = strdup (optarg); 453 server_port = virtual_port;
445 break; 454 }
446 case 'u': /* URL path */ 455 }
447 server_url = strdup (optarg); 456 break;
448 server_url_length = strlen (server_url); 457 case 'I': /* Server IP-address */
449 break; 458 server_address = strdup(optarg);
450 case 'p': /* Server port */ 459 break;
451 if (!is_intnonneg (optarg)) 460 case 'u': /* URL path */
452 usage2 (_("Invalid port number"), optarg); 461 server_url = strdup(optarg);
453 else { 462 server_url_length = strlen(server_url);
454 server_port = atoi (optarg); 463 break;
455 specify_port = true; 464 case 'p': /* Server port */
456 } 465 if (!is_intnonneg(optarg)) {
457 break; 466 usage2(_("Invalid port number"), optarg);
458 case 'a': /* authorization info */ 467 } else {
459 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1); 468 server_port = atoi(optarg);
460 user_auth[MAX_INPUT_BUFFER - 1] = 0; 469 specify_port = true;
461 break; 470 }
462 case 'b': /* proxy-authorization info */ 471 break;
463 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1); 472 case 'a': /* authorization info */
464 proxy_auth[MAX_INPUT_BUFFER - 1] = 0; 473 strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1);
465 break; 474 user_auth[MAX_INPUT_BUFFER - 1] = 0;
466 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ 475 break;
467 if (! http_post_data) 476 case 'b': /* proxy-authorization info */
468 http_post_data = strdup (optarg); 477 strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
469 if (! http_method) 478 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
470 http_method = strdup("POST"); 479 break;
471 break; 480 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
472 case 'j': /* Set HTTP method */ 481 if (!http_post_data) {
473 if (http_method) 482 http_post_data = strdup(optarg);
474 free(http_method); 483 }
475 http_method = strdup (optarg); 484 if (!http_method) {
476 char *tmp; 485 http_method = strdup("POST");
477 if ((tmp = strstr(http_method, ":")) != NULL) { 486 }
478 tmp[0] = '\0'; // set the ":" in the middle to 0 487 break;
479 http_method_proxy = ++tmp; // this points to the second part 488 case 'j': /* Set HTTP method */
480 } 489 if (http_method) {
481 break; 490 free(http_method);
482 case 'd': /* string or substring */ 491 }
483 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1); 492 http_method = strdup(optarg);
484 header_expect[MAX_INPUT_BUFFER - 1] = 0; 493 char *tmp;
485 break; 494 if ((tmp = strstr(http_method, ":")) != NULL) {
486 case 's': /* string or substring */ 495 tmp[0] = '\0'; // set the ":" in the middle to 0
487 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1); 496 http_method_proxy = ++tmp; // this points to the second part
488 string_expect[MAX_INPUT_BUFFER - 1] = 0; 497 }
489 break; 498 break;
490 case 'e': /* string or substring */ 499 case 'd': /* string or substring */
491 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1); 500 strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1);
492 server_expect[MAX_INPUT_BUFFER - 1] = 0; 501 header_expect[MAX_INPUT_BUFFER - 1] = 0;
493 server_expect_yn = 1; 502 break;
494 break; 503 case 's': /* string or substring */
495 case 'T': /* Content-type */ 504 strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1);
496 xasprintf (&http_content_type, "%s", optarg); 505 string_expect[MAX_INPUT_BUFFER - 1] = 0;
497 break; 506 break;
498 case 'l': /* linespan */ 507 case 'e': /* string or substring */
499 cflags &= ~REG_NEWLINE; 508 strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1);
500 break; 509 server_expect[MAX_INPUT_BUFFER - 1] = 0;
501 case 'R': /* regex */ 510 server_expect_yn = 1;
502 cflags |= REG_ICASE; 511 break;
512 case 'T': /* Content-type */
513 xasprintf(&http_content_type, "%s", optarg);
514 break;
515 case 'l': /* linespan */
516 cflags &= ~REG_NEWLINE;
517 break;
518 case 'R': /* regex */
519 cflags |= REG_ICASE;
503 // fall through 520 // fall through
504 case 'r': /* regex */ 521 case 'r': /* regex */
505 strncpy (regexp, optarg, MAX_RE_SIZE - 1); 522 strncpy(regexp, optarg, MAX_RE_SIZE - 1);
506 regexp[MAX_RE_SIZE - 1] = 0; 523 regexp[MAX_RE_SIZE - 1] = 0;
507 errcode = regcomp (&preg, regexp, cflags); 524 errcode = regcomp(&preg, regexp, cflags);
508 if (errcode != 0) { 525 if (errcode != 0) {
509 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 526 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
510 printf (_("Could Not Compile Regular Expression: %s"), errbuf); 527 printf(_("Could Not Compile Regular Expression: %s"), errbuf);
511 return false; 528 return false;
512 } 529 }
513 break; 530 break;
514 case INVERT_REGEX: 531 case INVERT_REGEX:
515 invert_regex = 1; 532 invert_regex = 1;
516 break; 533 break;
517 case STATE_REGEX: 534 case STATE_REGEX:
518 if (!strcmp (optarg, "critical")) 535 if (!strcmp(optarg, "critical")) {
519 state_regex = STATE_CRITICAL; 536 state_regex = STATE_CRITICAL;
520 else if (!strcmp (optarg, "warning")) 537 } else if (!strcmp(optarg, "warning")) {
521 state_regex = STATE_WARNING; 538 state_regex = STATE_WARNING;
522 else usage2 (_("Invalid state-regex option"), optarg); 539 } else {
523 break; 540 usage2(_("Invalid state-regex option"), optarg);
524 case '4': 541 }
525 address_family = AF_INET; 542 break;
526 break; 543 case '4':
527 case '6': 544 address_family = AF_INET;
528#ifdef USE_IPV6 545 break;
529 address_family = AF_INET6; 546 case '6':
530#else 547 address_family = AF_INET6;
531 usage4 (_("IPv6 support not available")); 548 break;
532#endif 549 case 'v': /* verbose */
533 break; 550 verbose = true;
534 case 'v': /* verbose */ 551 break;
535 verbose = true; 552 case 'm': /* min_page_length */
536 break; 553 {
537 case 'm': /* min_page_length */ 554 char *tmp;
538 { 555 if (strchr(optarg, ':') != (char *)NULL) {
539 char *tmp; 556 /* range, so get two values, min:max */
540 if (strchr(optarg, ':') != (char *)NULL) { 557 tmp = strtok(optarg, ":");
541 /* range, so get two values, min:max */ 558 if (tmp == NULL) {
542 tmp = strtok(optarg, ":"); 559 printf("Bad format: try \"-m min:max\"\n");
543 if (tmp == NULL) { 560 exit(STATE_WARNING);
544 printf("Bad format: try \"-m min:max\"\n"); 561 } else {
545 exit (STATE_WARNING); 562 min_page_len = atoi(tmp);
546 } else 563 }
547 min_page_len = atoi(tmp); 564
548 565 tmp = strtok(NULL, ":");
549 tmp = strtok(NULL, ":"); 566 if (tmp == NULL) {
550 if (tmp == NULL) { 567 printf("Bad format: try \"-m min:max\"\n");
551 printf("Bad format: try \"-m min:max\"\n"); 568 exit(STATE_WARNING);
552 exit (STATE_WARNING); 569 } else {
553 } else 570 max_page_len = atoi(tmp);
554 max_page_len = atoi(tmp); 571 }
555 } else 572 } else {
556 min_page_len = atoi (optarg); 573 min_page_len = atoi(optarg);
557 break; 574 }
558 } 575 break;
559 case 'N': /* no-body */ 576 }
560 no_body = true; 577 case 'N': /* no-body */
561 break; 578 no_body = true;
562 case 'M': /* max-age */ 579 break;
563 { 580 case 'M': /* max-age */
564 int L = strlen(optarg); 581 {
565 if (L && optarg[L-1] == 'm') 582 int L = strlen(optarg);
566 maximum_age = atoi (optarg) * 60; 583 if (L && optarg[L - 1] == 'm') {
567 else if (L && optarg[L-1] == 'h') 584 maximum_age = atoi(optarg) * 60;
568 maximum_age = atoi (optarg) * 60 * 60; 585 } else if (L && optarg[L - 1] == 'h') {
569 else if (L && optarg[L-1] == 'd') 586 maximum_age = atoi(optarg) * 60 * 60;
570 maximum_age = atoi (optarg) * 60 * 60 * 24; 587 } else if (L && optarg[L - 1] == 'd') {
571 else if (L && (optarg[L-1] == 's' || 588 maximum_age = atoi(optarg) * 60 * 60 * 24;
572 isdigit (optarg[L-1]))) 589 } else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) {
573 maximum_age = atoi (optarg); 590 maximum_age = atoi(optarg);
574 else { 591 } else {
575 fprintf (stderr, "unparsable max-age: %s\n", optarg); 592 fprintf(stderr, "unparsable max-age: %s\n", optarg);
576 exit (STATE_WARNING); 593 exit(STATE_WARNING);
577 } 594 }
578 } 595 } break;
579 break; 596 case 'E': /* show extended perfdata */
580 case 'E': /* show extended perfdata */ 597 show_extended_perfdata = true;
581 show_extended_perfdata = true; 598 break;
582 break; 599 case 'B': /* print body content after status line */
583 case 'B': /* print body content after status line */ 600 show_body = true;
584 show_body = true; 601 break;
585 break; 602 }
586 } 603 }
587 }
588
589 c = optind;
590
591 if (server_address == NULL && c < argc)
592 server_address = strdup (argv[c++]);
593
594 if (host_name == NULL && c < argc)
595 host_name = strdup (argv[c++]);
596
597 if (server_address == NULL) {
598 if (host_name == NULL)
599 usage4 (_("You must specify a server address or host name"));
600 else
601 server_address = strdup (host_name);
602 }
603
604 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
605
606 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
607 socket_timeout = (int)thlds->critical->end + 1;
608
609 if (http_method == NULL)
610 http_method = strdup ("GET");
611
612 if (http_method_proxy == NULL)
613 http_method_proxy = strdup ("GET");
614
615 if (client_cert && !client_privkey)
616 usage4 (_("If you use a client certificate you must also specify a private key file"));
617
618 if (virtual_port == 0)
619 virtual_port = server_port;
620
621 return true;
622}
623 604
605 c = optind;
624 606
607 if (server_address == NULL && c < argc) {
608 server_address = strdup(argv[c++]);
609 }
610
611 if (host_name == NULL && c < argc) {
612 host_name = strdup(argv[c++]);
613 }
614
615 if (server_address == NULL) {
616 if (host_name == NULL) {
617 usage4(_("You must specify a server address or host name"));
618 } else {
619 server_address = strdup(host_name);
620 }
621 }
622
623 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
624
625 if (critical_thresholds && thlds->critical->end > (double)socket_timeout) {
626 socket_timeout = (int)thlds->critical->end + 1;
627 }
628
629 if (http_method == NULL) {
630 http_method = strdup("GET");
631 }
632
633 if (http_method_proxy == NULL) {
634 http_method_proxy = strdup("GET");
635 }
636
637 if (client_cert && !client_privkey) {
638 usage4(_("If you use a client certificate you must also specify a private key file"));
639 }
640
641 if (virtual_port == 0) {
642 virtual_port = server_port;
643 }
644
645 return true;
646}
625 647
626/* Returns 1 if we're done processing the document body; 0 to keep going */ 648/* Returns 1 if we're done processing the document body; 0 to keep going */
627static int 649static int document_headers_done(char *full_page) {
628document_headers_done (char *full_page) 650 const char *body;
629{
630 const char *body;
631 651
632 for (body = full_page; *body; body++) { 652 for (body = full_page; *body; body++) {
633 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3)) 653 if (!strncmp(body, "\n\n", 2) || !strncmp(body, "\n\r\n", 3)) {
634 break; 654 break;
635 } 655 }
656 }
636 657
637 if (!*body) 658 if (!*body) {
638 return 0; /* haven't read end of headers yet */ 659 return 0; /* haven't read end of headers yet */
660 }
639 661
640 full_page[body - full_page] = 0; 662 full_page[body - full_page] = 0;
641 return 1; 663 return 1;
642} 664}
643 665
644static time_t 666static time_t parse_time_string(const char *string) {
645parse_time_string (const char *string) 667 struct tm tm;
646{ 668 time_t t;
647 struct tm tm; 669 memset(&tm, 0, sizeof(tm));
648 time_t t; 670
649 memset (&tm, 0, sizeof(tm)); 671 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
650 672
651 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */ 673 if (isupper(string[0]) && /* Tue */
652 674 islower(string[1]) && islower(string[2]) && ',' == string[3] && ' ' == string[4] &&
653 if (isupper (string[0]) && /* Tue */ 675 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
654 islower (string[1]) && 676 isdigit(string[6]) && ' ' == string[7] && isupper(string[8]) && /* Dec */
655 islower (string[2]) && 677 islower(string[9]) && islower(string[10]) && ' ' == string[11] &&
656 ',' == string[3] && 678 isdigit(string[12]) && /* 2001 */
657 ' ' == string[4] && 679 isdigit(string[13]) && isdigit(string[14]) && isdigit(string[15]) && ' ' == string[16] &&
658 (isdigit(string[5]) || string[5] == ' ') && /* 25 */ 680 isdigit(string[17]) && /* 02: */
659 isdigit (string[6]) && 681 isdigit(string[18]) && ':' == string[19] && isdigit(string[20]) && /* 59: */
660 ' ' == string[7] && 682 isdigit(string[21]) && ':' == string[22] && isdigit(string[23]) && /* 03 */
661 isupper (string[8]) && /* Dec */ 683 isdigit(string[24]) && ' ' == string[25] && 'G' == string[26] && /* GMT */
662 islower (string[9]) && 684 'M' == string[27] && /* GMT */
663 islower (string[10]) && 685 'T' == string[28]) {
664 ' ' == string[11] && 686
665 isdigit (string[12]) && /* 2001 */ 687 tm.tm_sec = 10 * (string[23] - '0') + (string[24] - '0');
666 isdigit (string[13]) && 688 tm.tm_min = 10 * (string[20] - '0') + (string[21] - '0');
667 isdigit (string[14]) && 689 tm.tm_hour = 10 * (string[17] - '0') + (string[18] - '0');
668 isdigit (string[15]) && 690 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5] - '0') + (string[6] - '0');
669 ' ' == string[16] && 691 tm.tm_mon = (!strncmp(string + 8, "Jan", 3) ? 0
670 isdigit (string[17]) && /* 02: */ 692 : !strncmp(string + 8, "Feb", 3) ? 1
671 isdigit (string[18]) && 693 : !strncmp(string + 8, "Mar", 3) ? 2
672 ':' == string[19] && 694 : !strncmp(string + 8, "Apr", 3) ? 3
673 isdigit (string[20]) && /* 59: */ 695 : !strncmp(string + 8, "May", 3) ? 4
674 isdigit (string[21]) && 696 : !strncmp(string + 8, "Jun", 3) ? 5
675 ':' == string[22] && 697 : !strncmp(string + 8, "Jul", 3) ? 6
676 isdigit (string[23]) && /* 03 */ 698 : !strncmp(string + 8, "Aug", 3) ? 7
677 isdigit (string[24]) && 699 : !strncmp(string + 8, "Sep", 3) ? 8
678 ' ' == string[25] && 700 : !strncmp(string + 8, "Oct", 3) ? 9
679 'G' == string[26] && /* GMT */ 701 : !strncmp(string + 8, "Nov", 3) ? 10
680 'M' == string[27] && /* GMT */ 702 : !strncmp(string + 8, "Dec", 3) ? 11
681 'T' == string[28]) { 703 : -1);
682 704 tm.tm_year = ((1000 * (string[12] - '0') + 100 * (string[13] - '0') +
683 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0'); 705 10 * (string[14] - '0') + (string[15] - '0')) -
684 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0'); 706 1900);
685 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0'); 707
686 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0'); 708 tm.tm_isdst = 0; /* GMT is never in DST, right? */
687 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 : 709
688 !strncmp (string+8, "Feb", 3) ? 1 : 710 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) {
689 !strncmp (string+8, "Mar", 3) ? 2 : 711 return 0;
690 !strncmp (string+8, "Apr", 3) ? 3 : 712 }
691 !strncmp (string+8, "May", 3) ? 4 : 713
692 !strncmp (string+8, "Jun", 3) ? 5 : 714 /*
693 !strncmp (string+8, "Jul", 3) ? 6 : 715 This is actually wrong: we need to subtract the local timezone
694 !strncmp (string+8, "Aug", 3) ? 7 : 716 offset from GMT from this value. But, that's ok in this usage,
695 !strncmp (string+8, "Sep", 3) ? 8 : 717 because we only comparing these two GMT dates against each other,
696 !strncmp (string+8, "Oct", 3) ? 9 : 718 so it doesn't matter what time zone we parse them in.
697 !strncmp (string+8, "Nov", 3) ? 10 : 719 */
698 !strncmp (string+8, "Dec", 3) ? 11 : 720
699 -1); 721 t = mktime(&tm);
700 tm.tm_year = ((1000 * (string[12]-'0') + 722 if (t == (time_t)-1) {
701 100 * (string[13]-'0') + 723 t = 0;
702 10 * (string[14]-'0') + 724 }
703 (string[15]-'0')) 725
704 - 1900); 726 if (verbose) {
705 727 const char *s = string;
706 tm.tm_isdst = 0; /* GMT is never in DST, right? */ 728 while (*s && *s != '\r' && *s != '\n') {
707 729 fputc(*s++, stdout);
708 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) 730 }
709 return 0; 731 printf(" ==> %lu\n", (unsigned long)t);
710 732 }
711 /* 733
712 This is actually wrong: we need to subtract the local timezone 734 return t;
713 offset from GMT from this value. But, that's ok in this usage, 735 }
714 because we only comparing these two GMT dates against each other, 736 return 0;
715 so it doesn't matter what time zone we parse them in.
716 */
717
718 t = mktime (&tm);
719 if (t == (time_t) -1) t = 0;
720
721 if (verbose) {
722 const char *s = string;
723 while (*s && *s != '\r' && *s != '\n')
724 fputc (*s++, stdout);
725 printf (" ==> %lu\n", (unsigned long) t);
726 }
727
728 return t;
729
730 } else {
731 return 0;
732 }
733} 737}
734 738
735/* Checks if the server 'reply' is one of the expected 'statuscodes' */ 739/* Checks if the server 'reply' is one of the expected 'statuscodes' */
736static int 740static int expected_statuscode(const char *reply, const char *statuscodes) {
737expected_statuscode (const char *reply, const char *statuscodes) 741 char *expected;
738{ 742 char *code;
739 char *expected, *code; 743 int result = 0;
740 int result = 0; 744
741 745 if ((expected = strdup(statuscodes)) == NULL) {
742 if ((expected = strdup (statuscodes)) == NULL) 746 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
743 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); 747 }
744 748
745 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ",")) 749 for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) {
746 if (strstr (reply, code) != NULL) { 750 if (strstr(reply, code) != NULL) {
747 result = 1; 751 result = 1;
748 break; 752 break;
749 } 753 }
750 754 }
751 free (expected); 755
752 return result; 756 free(expected);
757 return result;
753} 758}
754 759
755static int 760static int check_document_dates(const char *headers, char **msg) {
756check_document_dates (const char *headers, char **msg) 761 const char *s;
757{ 762 char *server_date = 0;
758 const char *s; 763 char *document_date = 0;
759 char *server_date = 0; 764 int date_result = STATE_OK;
760 char *document_date = 0; 765
761 int date_result = STATE_OK; 766 s = headers;
762 767 while (*s) {
763 s = headers; 768 const char *field = s;
764 while (*s) { 769 const char *value = 0;
765 const char *field = s; 770
766 const char *value = 0; 771 /* Find the end of the header field */
767 772 while (*s && !isspace(*s) && *s != ':') {
768 /* Find the end of the header field */ 773 s++;
769 while (*s && !isspace(*s) && *s != ':') 774 }
770 s++; 775
771 776 /* Remember the header value, if any. */
772 /* Remember the header value, if any. */ 777 if (*s == ':') {
773 if (*s == ':') 778 value = ++s;
774 value = ++s; 779 }
775 780
776 /* Skip to the end of the header, including continuation lines. */ 781 /* Skip to the end of the header, including continuation lines. */
777 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) 782 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) {
778 s++; 783 s++;
779 784 }
780 /* Avoid stepping over end-of-string marker */ 785
781 if (*s) 786 /* Avoid stepping over end-of-string marker */
782 s++; 787 if (*s) {
783 788 s++;
784 /* Process this header. */ 789 }
785 if (value && value > field+2) { 790
786 char *ff = (char *) malloc (value-field); 791 /* Process this header. */
787 char *ss = ff; 792 if (value && value > field + 2) {
788 while (field < value-1) 793 char *ff = (char *)malloc(value - field);
789 *ss++ = tolower(*field++); 794 char *ss = ff;
790 *ss++ = 0; 795 while (field < value - 1) {
791 796 *ss++ = tolower(*field++);
792 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) { 797 }
793 const char *e; 798 *ss++ = 0;
794 while (*value && isspace (*value)) 799
795 value++; 800 if (!strcmp(ff, "date") || !strcmp(ff, "last-modified")) {
796 for (e = value; *e && *e != '\r' && *e != '\n'; e++) 801 const char *e;
797 ; 802 while (*value && isspace(*value)) {
798 ss = (char *) malloc (e - value + 1); 803 value++;
799 strncpy (ss, value, e - value); 804 }
800 ss[e - value] = 0; 805 for (e = value; *e && *e != '\r' && *e != '\n'; e++) {
801 if (!strcmp (ff, "date")) { 806 ;
802 if (server_date) free (server_date); 807 }
803 server_date = ss; 808 ss = (char *)malloc(e - value + 1);
804 } else { 809 strncpy(ss, value, e - value);
805 if (document_date) free (document_date); 810 ss[e - value] = 0;
806 document_date = ss; 811 if (!strcmp(ff, "date")) {
807 } 812 if (server_date) {
808 } 813 free(server_date);
809 free (ff); 814 }
810 } 815 server_date = ss;
811 } 816 } else {
812 817 if (document_date) {
813 /* Done parsing the body. Now check the dates we (hopefully) parsed. */ 818 free(document_date);
814 if (!server_date || !*server_date) { 819 }
815 xasprintf (msg, _("%sServer date unknown, "), *msg); 820 document_date = ss;
816 date_result = max_state_alt(STATE_UNKNOWN, date_result); 821 }
817 } else if (!document_date || !*document_date) { 822 }
818 xasprintf (msg, _("%sDocument modification date unknown, "), *msg); 823 free(ff);
819 date_result = max_state_alt(STATE_CRITICAL, date_result); 824 }
820 } else { 825 }
821 time_t srv_data = parse_time_string (server_date); 826
822 time_t doc_data = parse_time_string (document_date); 827 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
823 828 if (!server_date || !*server_date) {
824 if (srv_data <= 0) { 829 xasprintf(msg, _("%sServer date unknown, "), *msg);
825 xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); 830 date_result = max_state_alt(STATE_UNKNOWN, date_result);
826 date_result = max_state_alt(STATE_CRITICAL, date_result); 831 } else if (!document_date || !*document_date) {
827 } else if (doc_data <= 0) { 832 xasprintf(msg, _("%sDocument modification date unknown, "), *msg);
828 xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); 833 date_result = max_state_alt(STATE_CRITICAL, date_result);
829 date_result = max_state_alt(STATE_CRITICAL, date_result); 834 } else {
830 } else if (doc_data > srv_data + 30) { 835 time_t srv_data = parse_time_string(server_date);
831 xasprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); 836 time_t doc_data = parse_time_string(document_date);
832 date_result = max_state_alt(STATE_CRITICAL, date_result); 837
833 } else if (doc_data < srv_data - maximum_age) { 838 if (srv_data <= 0) {
834 int n = (srv_data - doc_data); 839 xasprintf(msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
835 if (n > (60 * 60 * 24 * 2)) { 840 date_result = max_state_alt(STATE_CRITICAL, date_result);
836 xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24)); 841 } else if (doc_data <= 0) {
837 date_result = max_state_alt(STATE_CRITICAL, date_result); 842 xasprintf(msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
838 } else { 843 date_result = max_state_alt(STATE_CRITICAL, date_result);
839 xasprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); 844 } else if (doc_data > srv_data + 30) {
840 date_result = max_state_alt(STATE_CRITICAL, date_result); 845 xasprintf(msg, _("%sDocument is %d seconds in the future, "), *msg,
841 } 846 (int)doc_data - (int)srv_data);
842 } 847 date_result = max_state_alt(STATE_CRITICAL, date_result);
843 free (server_date); 848 } else if (doc_data < srv_data - maximum_age) {
844 free (document_date); 849 int n = (srv_data - doc_data);
845 } 850 if (n > (60 * 60 * 24 * 2)) {
846 return date_result; 851 xasprintf(msg, _("%sLast modified %.1f days ago, "), *msg,
852 ((float)n) / (60 * 60 * 24));
853 date_result = max_state_alt(STATE_CRITICAL, date_result);
854 } else {
855 xasprintf(msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60),
856 (n / 60) % 60, n % 60);
857 date_result = max_state_alt(STATE_CRITICAL, date_result);
858 }
859 }
860 free(server_date);
861 free(document_date);
862 }
863 return date_result;
847} 864}
848 865
849int 866int get_content_length(const char *headers) {
850get_content_length (const char *headers) 867 const char *s;
851{ 868 int content_length = 0;
852 const char *s; 869
853 int content_length = 0; 870 s = headers;
854 871 while (*s) {
855 s = headers; 872 const char *field = s;
856 while (*s) { 873 const char *value = 0;
857 const char *field = s; 874
858 const char *value = 0; 875 /* Find the end of the header field */
859 876 while (*s && !isspace(*s) && *s != ':') {
860 /* Find the end of the header field */ 877 s++;
861 while (*s && !isspace(*s) && *s != ':') 878 }
862 s++; 879
863 880 /* Remember the header value, if any. */
864 /* Remember the header value, if any. */ 881 if (*s == ':') {
865 if (*s == ':') 882 value = ++s;
866 value = ++s; 883 }
867 884
868 /* Skip to the end of the header, including continuation lines. */ 885 /* Skip to the end of the header, including continuation lines. */
869 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) 886 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) {
870 s++; 887 s++;
871 888 }
872 /* Avoid stepping over end-of-string marker */ 889
873 if (*s) 890 /* Avoid stepping over end-of-string marker */
874 s++; 891 if (*s) {
875 892 s++;
876 /* Process this header. */ 893 }
877 if (value && value > field+2) { 894
878 char *ff = (char *) malloc (value-field); 895 /* Process this header. */
879 char *ss = ff; 896 if (value && value > field + 2) {
880 while (field < value-1) 897 char *ff = (char *)malloc(value - field);
881 *ss++ = tolower(*field++); 898 char *ss = ff;
882 *ss++ = 0; 899 while (field < value - 1) {
883 900 *ss++ = tolower(*field++);
884 if (!strcmp (ff, "content-length")) { 901 }
885 const char *e; 902 *ss++ = 0;
886 while (*value && isspace (*value)) 903
887 value++; 904 if (!strcmp(ff, "content-length")) {
888 for (e = value; *e && *e != '\r' && *e != '\n'; e++) 905 const char *e;
889 ; 906 while (*value && isspace(*value)) {
890 ss = (char *) malloc (e - value + 1); 907 value++;
891 strncpy (ss, value, e - value); 908 }
892 ss[e - value] = 0; 909 for (e = value; *e && *e != '\r' && *e != '\n'; e++) {
893 content_length = atoi(ss); 910 ;
894 free (ss); 911 }
895 } 912 ss = (char *)malloc(e - value + 1);
896 free (ff); 913 strncpy(ss, value, e - value);
897 } 914 ss[e - value] = 0;
898 } 915 content_length = atoi(ss);
899 return (content_length); 916 free(ss);
917 }
918 free(ff);
919 }
920 }
921 return (content_length);
900} 922}
901 923
902char * 924char *prepend_slash(char *path) {
903prepend_slash (char *path) 925 char *newpath;
904{
905 char *newpath;
906 926
907 if (path[0] == '/') 927 if (path[0] == '/') {
908 return path; 928 return path;
929 }
909 930
910 if ((newpath = malloc (strlen(path) + 2)) == NULL) 931 if ((newpath = malloc(strlen(path) + 2)) == NULL) {
911 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); 932 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
912 newpath[0] = '/'; 933 }
913 strcpy (newpath + 1, path); 934 newpath[0] = '/';
914 free (path); 935 strcpy(newpath + 1, path);
915 return newpath; 936 free(path);
937 return newpath;
916} 938}
917 939
918int 940int check_http(void) {
919check_http (void) 941 char *msg;
920{ 942 char *status_line;
921 char *msg; 943 char *status_code;
922 char *status_line; 944 char *header;
923 char *status_code; 945 char *page;
924 char *header; 946 char *auth;
925 char *page; 947 int http_status;
926 char *auth; 948 int i = 0;
927 int http_status; 949 size_t pagesize = 0;
928 int i = 0; 950 char *full_page;
929 size_t pagesize = 0; 951 char *full_page_new;
930 char *full_page; 952 char *buf;
931 char *full_page_new; 953 char *pos;
932 char *buf; 954 long microsec = 0L;
933 char *pos; 955 double elapsed_time = 0.0;
934 long microsec = 0L; 956 long microsec_connect = 0L;
935 double elapsed_time = 0.0; 957 double elapsed_time_connect = 0.0;
936 long microsec_connect = 0L; 958 long microsec_ssl = 0L;
937 double elapsed_time_connect = 0.0; 959 double elapsed_time_ssl = 0.0;
938 long microsec_ssl = 0L; 960 long microsec_firstbyte = 0L;
939 double elapsed_time_ssl = 0.0; 961 double elapsed_time_firstbyte = 0.0;
940 long microsec_firstbyte = 0L; 962 long microsec_headers = 0L;
941 double elapsed_time_firstbyte = 0.0; 963 double elapsed_time_headers = 0.0;
942 long microsec_headers = 0L; 964 long microsec_transfer = 0L;
943 double elapsed_time_headers = 0.0; 965 double elapsed_time_transfer = 0.0;
944 long microsec_transfer = 0L; 966 int page_len = 0;
945 double elapsed_time_transfer = 0.0; 967 int result = STATE_OK;
946 int page_len = 0; 968 char *force_host_header = NULL;
947 int result = STATE_OK; 969
948 char *force_host_header = NULL; 970 /* try to connect to the host at the given port number */
949 971 gettimeofday(&tv_temp, NULL);
950 /* try to connect to the host at the given port number */ 972 if (my_tcp_connect(server_address, server_port, &sd) != STATE_OK) {
951 gettimeofday (&tv_temp, NULL); 973 die(STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
952 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) 974 }
953 die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n")); 975 microsec_connect = deltime(tv_temp);
954 microsec_connect = deltime (tv_temp); 976
955 977 /* if we are called with the -I option, the -j method is CONNECT and */
956 /* if we are called with the -I option, the -j method is CONNECT and */ 978 /* we received -S for SSL, then we tunnel the request through a proxy*/
957 /* we received -S for SSL, then we tunnel the request through a proxy*/ 979 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */
958 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */ 980
959 981 if (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && host_name != NULL &&
960 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 982 use_ssl) {
961 && host_name != NULL && use_ssl == true) { 983
962 984 if (verbose) {
963 if (verbose) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, server_port, host_name, HTTPS_PORT); 985 printf("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address,
964 asprintf (&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, user_agent); 986 server_port, host_name, HTTPS_PORT);
965 if (strlen(proxy_auth)) { 987 }
966 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth); 988 asprintf(&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT,
967 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); 989 user_agent);
968 } 990 if (strlen(proxy_auth)) {
969 /* optionally send any other header tag */ 991 base64_encode_alloc(proxy_auth, strlen(proxy_auth), &auth);
970 if (http_opt_headers_count) { 992 xasprintf(&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
971 for (i = 0; i < http_opt_headers_count ; i++) { 993 }
972 if (force_host_header != http_opt_headers[i]) { 994 /* optionally send any other header tag */
973 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]); 995 if (http_opt_headers_count) {
974 } 996 for (i = 0; i < http_opt_headers_count; i++) {
975 } 997 if (force_host_header != http_opt_headers[i]) {
976 /* This cannot be free'd here because a redirection will then try to access this and segfault */ 998 xasprintf(&buf, "%s%s\r\n", buf, http_opt_headers[i]);
977 /* Covered in a testcase in tests/check_http.t */ 999 }
978 /* free(http_opt_headers); */ 1000 }
979 } 1001 /* This cannot be free'd here because a redirection will then try to access this and
980 asprintf (&buf, "%sProxy-Connection: keep-alive\r\n", buf); 1002 * segfault */
981 asprintf (&buf, "%sHost: %s\r\n", buf, host_name); 1003 /* Covered in a testcase in tests/check_http.t */
982 /* we finished our request, send empty line with CRLF */ 1004 /* free(http_opt_headers); */
983 asprintf (&buf, "%s%s", buf, CRLF); 1005 }
984 if (verbose) printf ("%s\n", buf); 1006 asprintf(&buf, "%sProxy-Connection: keep-alive\r\n", buf);
985 send(sd, buf, strlen (buf), 0); 1007 asprintf(&buf, "%sHost: %s\r\n", buf, host_name);
986 buf[0]='\0'; 1008 /* we finished our request, send empty line with CRLF */
987 1009 asprintf(&buf, "%s%s", buf, CRLF);
988 if (verbose) printf ("Receive response from proxy\n"); 1010 if (verbose) {
989 read (sd, buffer, MAX_INPUT_BUFFER-1); 1011 printf("%s\n", buf);
990 if (verbose) printf ("%s", buffer); 1012 }
991 /* Here we should check if we got HTTP/1.1 200 Connection established */ 1013 send(sd, buf, strlen(buf), 0);
992 } 1014 buf[0] = '\0';
1015
1016 if (verbose) {
1017 printf("Receive response from proxy\n");
1018 }
1019 read(sd, buffer, MAX_INPUT_BUFFER - 1);
1020 if (verbose) {
1021 printf("%s", buffer);
1022 }
1023 /* Here we should check if we got HTTP/1.1 200 Connection established */
1024 }
993#ifdef HAVE_SSL 1025#ifdef HAVE_SSL
994 elapsed_time_connect = (double)microsec_connect / 1.0e6; 1026 elapsed_time_connect = (double)microsec_connect / 1.0e6;
995 if (use_ssl == true) { 1027 if (use_ssl) {
996 gettimeofday (&tv_temp, NULL); 1028 gettimeofday(&tv_temp, NULL);
997 result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey); 1029 result = np_net_ssl_init_with_hostname_version_and_cert(
998 if (verbose) printf ("SSL initialized\n"); 1030 sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
999 if (result != STATE_OK) 1031 if (verbose) {
1000 die (STATE_CRITICAL, NULL); 1032 printf("SSL initialized\n");
1001 microsec_ssl = deltime (tv_temp); 1033 }
1002 elapsed_time_ssl = (double)microsec_ssl / 1.0e6; 1034 if (result != STATE_OK) {
1003 if (check_cert == true) { 1035 die(STATE_CRITICAL, _("HTTP CRITICAL - SSL error\n"));
1004 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 1036 }
1005 if (continue_after_check_cert == false) { 1037 microsec_ssl = deltime(tv_temp);
1006 if (sd) close(sd); 1038 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
1007 np_net_ssl_cleanup(); 1039 if (check_cert) {
1008 return result; 1040 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
1009 } 1041 if (!continue_after_check_cert) {
1010 } 1042 if (sd) {
1011 } 1043 close(sd);
1044 }
1045 np_net_ssl_cleanup();
1046 return result;
1047 }
1048 }
1049 }
1012#endif /* HAVE_SSL */ 1050#endif /* HAVE_SSL */
1013 1051
1014 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 1052 if (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && host_name != NULL &&
1015 && host_name != NULL && use_ssl == true) 1053 use_ssl) {
1016 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method_proxy, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); 1054 asprintf(&buf, "%s %s %s\r\n%s\r\n", http_method_proxy, server_url,
1017 else 1055 host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1018 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); 1056 } else {
1019 1057 asprintf(&buf, "%s %s %s\r\n%s\r\n", http_method, server_url,
1020 /* tell HTTP/1.1 servers not to keep the connection alive */ 1058 host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1021 xasprintf (&buf, "%sConnection: close\r\n", buf); 1059 }
1022 1060
1023 /* check if Host header is explicitly set in options */ 1061 /* tell HTTP/1.1 servers not to keep the connection alive */
1024 if (http_opt_headers_count) { 1062 xasprintf(&buf, "%sConnection: close\r\n", buf);
1025 for (i = 0; i < http_opt_headers_count ; i++) { 1063
1026 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) { 1064 /* check if Host header is explicitly set in options */
1027 force_host_header = http_opt_headers[i]; 1065 if (http_opt_headers_count) {
1028 } 1066 for (i = 0; i < http_opt_headers_count; i++) {
1029 } 1067 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
1030 } 1068 force_host_header = http_opt_headers[i];
1031 1069 }
1032 /* optionally send the host header info */ 1070 }
1033 if (host_name) { 1071 }
1034 if (force_host_header) { 1072
1035 xasprintf (&buf, "%s%s\r\n", buf, force_host_header); 1073 /* optionally send the host header info */
1036 } 1074 if (host_name) {
1037 else { 1075 if (force_host_header) {
1038 /* 1076 xasprintf(&buf, "%s%s\r\n", buf, force_host_header);
1039 * Specify the port only if we're using a non-default port (see RFC 2616, 1077 } else {
1040 * 14.23). Some server applications/configurations cause trouble if the 1078 /*
1041 * (default) port is explicitly specified in the "Host:" header line. 1079 * Specify the port only if we're using a non-default port (see RFC 2616,
1042 */ 1080 * 14.23). Some server applications/configurations cause trouble if the
1043 if ((use_ssl == false && virtual_port == HTTP_PORT) || 1081 * (default) port is explicitly specified in the "Host:" header line.
1044 (use_ssl == true && virtual_port == HTTPS_PORT) || 1082 */
1045 (server_address != NULL && strcmp(http_method, "CONNECT") == 0 1083 if ((!use_ssl && virtual_port == HTTP_PORT) ||
1046 && host_name != NULL && use_ssl == true)) 1084 (use_ssl && virtual_port == HTTPS_PORT) ||
1047 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name); 1085 (server_address != NULL && strcmp(http_method, "CONNECT") == 0 &&
1048 else 1086 host_name != NULL && use_ssl)) {
1049 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port); 1087 xasprintf(&buf, "%sHost: %s\r\n", buf, host_name);
1050 } 1088 } else {
1051 } 1089 xasprintf(&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port);
1052 1090 }
1053 /* optionally send any other header tag */ 1091 }
1054 if (http_opt_headers_count) { 1092 }
1055 for (i = 0; i < http_opt_headers_count ; i++) { 1093
1056 if (force_host_header != http_opt_headers[i]) { 1094 /* optionally send any other header tag */
1057 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]); 1095 if (http_opt_headers_count) {
1058 } 1096 for (i = 0; i < http_opt_headers_count; i++) {
1059 } 1097 if (force_host_header != http_opt_headers[i]) {
1060 /* This cannot be free'd here because a redirection will then try to access this and segfault */ 1098 xasprintf(&buf, "%s%s\r\n", buf, http_opt_headers[i]);
1061 /* Covered in a testcase in tests/check_http.t */ 1099 }
1062 /* free(http_opt_headers); */ 1100 }
1063 } 1101 /* This cannot be free'd here because a redirection will then try to access this and
1064 1102 * segfault */
1065 /* optionally send the authentication info */ 1103 /* Covered in a testcase in tests/check_http.t */
1066 if (strlen(user_auth)) { 1104 /* free(http_opt_headers); */
1067 base64_encode_alloc (user_auth, strlen (user_auth), &auth); 1105 }
1068 xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth); 1106
1069 } 1107 /* optionally send the authentication info */
1070 1108 if (strlen(user_auth)) {
1071 /* optionally send the proxy authentication info */ 1109 base64_encode_alloc(user_auth, strlen(user_auth), &auth);
1072 if (strlen(proxy_auth)) { 1110 xasprintf(&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
1073 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth); 1111 }
1074 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); 1112
1075 } 1113 /* optionally send the proxy authentication info */
1076 1114 if (strlen(proxy_auth)) {
1077 /* either send http POST data (any data, not only POST)*/ 1115 base64_encode_alloc(proxy_auth, strlen(proxy_auth), &auth);
1078 if (http_post_data) { 1116 xasprintf(&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
1079 if (http_content_type) { 1117 }
1080 xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type); 1118
1081 } else { 1119 /* either send http POST data (any data, not only POST)*/
1082 xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf); 1120 if (http_post_data) {
1083 } 1121 if (http_content_type) {
1084 1122 xasprintf(&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
1085 xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data)); 1123 } else {
1086 xasprintf (&buf, "%s%s", buf, http_post_data); 1124 xasprintf(&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
1087 } else { 1125 }
1088 /* or just a newline so the server knows we're done with the request */ 1126
1089 xasprintf (&buf, "%s%s", buf, CRLF); 1127 xasprintf(&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen(http_post_data));
1090 } 1128 xasprintf(&buf, "%s%s", buf, http_post_data);
1091 1129 } else {
1092 if (verbose) printf ("%s\n", buf); 1130 /* or just a newline so the server knows we're done with the request */
1093 gettimeofday (&tv_temp, NULL); 1131 xasprintf(&buf, "%s%s", buf, CRLF);
1094 my_send (buf, strlen (buf)); 1132 }
1095 microsec_headers = deltime (tv_temp); 1133
1096 elapsed_time_headers = (double)microsec_headers / 1.0e6; 1134 if (verbose) {
1097 1135 printf("%s\n", buf);
1098 /* fetch the page */ 1136 }
1099 full_page = strdup(""); 1137 gettimeofday(&tv_temp, NULL);
1100 gettimeofday (&tv_temp, NULL); 1138 my_send(buf, strlen(buf));
1101 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) { 1139 microsec_headers = deltime(tv_temp);
1102 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) { 1140 elapsed_time_headers = (double)microsec_headers / 1.0e6;
1103 microsec_firstbyte = deltime (tv_temp); 1141
1104 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6; 1142 /* fetch the page */
1105 } 1143 full_page = strdup("");
1106 while ((pos = memchr(buffer, '\0', i))) { 1144 gettimeofday(&tv_temp, NULL);
1107 /* replace nul character with a blank */ 1145 while ((i = my_recv(buffer, MAX_INPUT_BUFFER - 1)) > 0) {
1108 *pos = ' '; 1146 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) {
1109 } 1147 microsec_firstbyte = deltime(tv_temp);
1110 buffer[i] = '\0'; 1148 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6;
1111 1149 }
1112 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL) 1150 while ((pos = memchr(buffer, '\0', i))) {
1113 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n")); 1151 /* replace nul character with a blank */
1114 1152 *pos = ' ';
1115 memmove(&full_page_new[pagesize], buffer, i + 1); 1153 }
1116 1154 buffer[i] = '\0';
1117 full_page = full_page_new; 1155
1118 1156 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL) {
1119 pagesize += i; 1157 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1120 1158 }
1121 if (no_body && document_headers_done (full_page)) { 1159
1122 i = 0; 1160 memmove(&full_page_new[pagesize], buffer, i + 1);
1123 break; 1161
1124 } 1162 full_page = full_page_new;
1125 } 1163
1126 microsec_transfer = deltime (tv_temp); 1164 pagesize += i;
1127 elapsed_time_transfer = (double)microsec_transfer / 1.0e6; 1165
1128 1166 if (no_body && document_headers_done(full_page)) {
1129 if (i < 0 && errno != ECONNRESET) { 1167 i = 0;
1130 die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n")); 1168 break;
1131 } 1169 }
1132 1170 }
1133 /* return a CRITICAL status if we couldn't read any data */ 1171 microsec_transfer = deltime(tv_temp);
1134 if (pagesize == (size_t) 0) 1172 elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
1135 die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n")); 1173
1136 1174 if (i < 0 && errno != ECONNRESET) {
1137 /* close the connection */ 1175 die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1138 if (sd) close(sd); 1176 }
1177
1178 /* return a CRITICAL status if we couldn't read any data */
1179 if (pagesize == (size_t)0) {
1180 die(STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
1181 }
1182
1183 /* close the connection */
1184 if (sd) {
1185 close(sd);
1186 }
1139#ifdef HAVE_SSL 1187#ifdef HAVE_SSL
1140 np_net_ssl_cleanup(); 1188 np_net_ssl_cleanup();
1141#endif 1189#endif
1142 1190
1143 /* Save check time */ 1191 /* Save check time */
1144 microsec = deltime (tv); 1192 microsec = deltime(tv);
1145 elapsed_time = (double)microsec / 1.0e6; 1193 elapsed_time = (double)microsec / 1.0e6;
1146 1194
1147 /* leave full_page untouched so we can free it later */ 1195 /* leave full_page untouched so we can free it later */
1148 page = full_page; 1196 page = full_page;
1149 1197
1150 if (verbose) 1198 if (verbose) {
1151 printf ("%s://%s:%d%s is %d characters\n", 1199 printf("%s://%s:%d%s is %d characters\n", use_ssl ? "https" : "http", server_address,
1152 use_ssl ? "https" : "http", server_address, 1200 server_port, server_url, (int)pagesize);
1153 server_port, server_url, (int)pagesize); 1201 }
1154 1202
1155 /* find status line and null-terminate it */ 1203 /* find status line and null-terminate it */
1156 status_line = page; 1204 status_line = page;
1157 page += (size_t) strcspn (page, "\r\n"); 1205 page += (size_t)strcspn(page, "\r\n");
1158 pos = page; 1206 pos = page;
1159 page += (size_t) strspn (page, "\r\n"); 1207 page += (size_t)strspn(page, "\r\n");
1160 status_line[strcspn(status_line, "\r\n")] = 0; 1208 status_line[strcspn(status_line, "\r\n")] = 0;
1161 strip (status_line); 1209 strip(status_line);
1162 if (verbose) 1210 if (verbose) {
1163 printf ("STATUS: %s\n", status_line); 1211 printf("STATUS: %s\n", status_line);
1164 1212 }
1165 /* find header info and null-terminate it */ 1213
1166 header = page; 1214 /* find header info and null-terminate it */
1167 while (strcspn (page, "\r\n") > 0) { 1215 header = page;
1168 page += (size_t) strcspn (page, "\r\n"); 1216 while (strcspn(page, "\r\n") > 0) {
1169 pos = page; 1217 page += (size_t)strcspn(page, "\r\n");
1170 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) || 1218 pos = page;
1171 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2)) 1219 if ((strspn(page, "\r") == 1 && strspn(page, "\r\n") >= 2) ||
1172 page += (size_t) 2; 1220 (strspn(page, "\n") == 1 && strspn(page, "\r\n") >= 2)) {
1173 else 1221 page += (size_t)2;
1174 page += (size_t) 1; 1222 } else {
1175 } 1223 page += (size_t)1;
1176 page += (size_t) strspn (page, "\r\n"); 1224 }
1177 header[pos - header] = 0; 1225 }
1178 if (verbose) 1226 page += (size_t)strspn(page, "\r\n");
1179 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, 1227 header[pos - header] = 0;
1180 (no_body ? " [[ skipped ]]" : page)); 1228 if (verbose) {
1181 1229 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
1182 /* make sure the status line matches the response we are looking for */ 1230 (no_body ? " [[ skipped ]]" : page));
1183 if (!expected_statuscode (status_line, server_expect)) { 1231 }
1184 if (server_port == HTTP_PORT) 1232
1185 xasprintf (&msg, 1233 /* make sure the status line matches the response we are looking for */
1186 _("Invalid HTTP response received from host: %s\n"), 1234 if (!expected_statuscode(status_line, server_expect)) {
1187 status_line); 1235 if (server_port == HTTP_PORT) {
1188 else 1236 xasprintf(&msg, _("Invalid HTTP response received from host: %s\n"), status_line);
1189 xasprintf (&msg, 1237 } else {
1190 _("Invalid HTTP response received from host on port %d: %s\n"), 1238 xasprintf(&msg, _("Invalid HTTP response received from host on port %d: %s\n"),
1191 server_port, status_line); 1239 server_port, status_line);
1192 if (show_body) 1240 }
1193 xasprintf (&msg, _("%s\n%s"), msg, page); 1241 if (show_body) {
1194 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg); 1242 xasprintf(&msg, _("%s\n%s"), msg, page);
1195 } 1243 }
1196 1244 die(STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
1197 /* Bypass normal status line check if server_expect was set by user and not default */ 1245 }
1198 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */ 1246
1199 if ( server_expect_yn ) { 1247 /* Bypass normal status line check if server_expect was set by user and not default */
1200 xasprintf (&msg, 1248 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
1201 _("Status line output matched \"%s\" - "), server_expect); 1249 if (server_expect_yn) {
1202 if (verbose) 1250 xasprintf(&msg, _("Status line output matched \"%s\" - "), server_expect);
1203 printf ("%s\n",msg); 1251 if (verbose) {
1204 } 1252 printf("%s\n", msg);
1205 else { 1253 }
1206 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ 1254 } else {
1207 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */ 1255 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1208 /* Status-Code = 3 DIGITS */ 1256 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1209 1257 /* Status-Code = 3 DIGITS */
1210 status_code = strchr (status_line, ' ') + sizeof (char); 1258
1211 if (strspn (status_code, "1234567890") != 3) 1259 status_code = strchr(status_line, ' ') + sizeof(char);
1212 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line); 1260 if (strspn(status_code, "1234567890") != 3) {
1213 1261 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
1214 http_status = atoi (status_code); 1262 }
1215 1263
1216 /* check the return code */ 1264 http_status = atoi(status_code);
1217 1265
1218 if (http_status >= 600 || http_status < 100) { 1266 /* check the return code */
1219 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line); 1267
1220 } 1268 if (http_status >= 600 || http_status < 100) {
1221 /* server errors result in a critical state */ 1269 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
1222 else if (http_status >= 500) { 1270 }
1223 xasprintf (&msg, _("%s - "), status_line); 1271 /* server errors result in a critical state */
1224 result = STATE_CRITICAL; 1272 else if (http_status >= 500) {
1225 } 1273 xasprintf(&msg, _("%s - "), status_line);
1226 /* client errors result in a warning state */ 1274 result = STATE_CRITICAL;
1227 else if (http_status >= 400) { 1275 }
1228 xasprintf (&msg, _("%s - "), status_line); 1276 /* client errors result in a warning state */
1229 result = max_state_alt(STATE_WARNING, result); 1277 else if (http_status >= 400) {
1230 } 1278 xasprintf(&msg, _("%s - "), status_line);
1231 /* check redirected page if specified */ 1279 result = max_state_alt(STATE_WARNING, result);
1232 else if (http_status >= 300) { 1280 }
1233 1281 /* check redirected page if specified */
1234 if (onredirect == STATE_DEPENDENT) 1282 else if (http_status >= 300) {
1235 redir (header, status_line); 1283
1236 else 1284 if (onredirect == STATE_DEPENDENT) {
1237 result = max_state_alt(onredirect, result); 1285 redir(header, status_line);
1238 xasprintf (&msg, _("%s - "), status_line); 1286 } else {
1239 } /* end if (http_status >= 300) */ 1287 result = max_state_alt(onredirect, result);
1240 else { 1288 }
1241 /* Print OK status anyway */ 1289 xasprintf(&msg, _("%s - "), status_line);
1242 xasprintf (&msg, _("%s - "), status_line); 1290 } /* end if (http_status >= 300) */
1243 } 1291 else {
1244 1292 /* Print OK status anyway */
1245 } /* end else (server_expect_yn) */ 1293 xasprintf(&msg, _("%s - "), status_line);
1246 1294 }
1247 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */ 1295
1248 alarm (0); 1296 } /* end else (server_expect_yn) */
1249 1297
1250 if (maximum_age >= 0) { 1298 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1251 result = max_state_alt(check_document_dates(header, &msg), result); 1299 alarm(0);
1252 } 1300
1253 1301 if (maximum_age >= 0) {
1254 /* Page and Header content checks go here */ 1302 result = max_state_alt(check_document_dates(header, &msg), result);
1255 if (strlen(header_expect) > 0) { 1303 }
1256 if (strstr(header, header_expect) == NULL) { 1304
1257 // We did not find the header, the rest is for building the output and setting the state 1305 /* Page and Header content checks go here */
1258 char output_header_search[30] = ""; 1306 if (strlen(header_expect) > 0) {
1259 1307 if (strstr(header, header_expect) == NULL) {
1260 strncpy(&output_header_search[0], header_expect, 1308 // We did not find the header, the rest is for building the output and setting the state
1261 sizeof(output_header_search)); 1309 char output_header_search[30] = "";
1262 1310
1263 if (output_header_search[sizeof(output_header_search) - 1] != '\0') { 1311 strncpy(&output_header_search[0], header_expect, sizeof(output_header_search));
1264 bcopy("...", 1312
1265 &output_header_search[sizeof(output_header_search) - 4], 1313 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1266 4); 1314 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
1267 } 1315 }
1268 1316
1269 xasprintf (&msg, 1317 xasprintf(&msg, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg,
1270 _("%sheader '%s' not found on '%s://%s:%d%s', "), 1318 output_header_search, use_ssl ? "https" : "http",
1271 msg, 1319 host_name ? host_name : server_address, server_port, server_url);
1272 output_header_search, use_ssl ? "https" : "http", 1320
1273 host_name ? host_name : server_address, server_port, 1321 result = STATE_CRITICAL;
1274 server_url); 1322 }
1275 1323 }
1276 result = STATE_CRITICAL; 1324
1277 } 1325 // At this point we should test if the content is chunked and unchunk it, so
1278 } 1326 // it can be searched (and possibly printed)
1279 1327 const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *";
1280 // At this point we should test if the content is chunked and unchunk it, so 1328 regex_t chunked_header_regex;
1281 // it can be searched (and possibly printed) 1329
1282 const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *"; 1330 if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) {
1283 regex_t chunked_header_regex; 1331 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN),
1284 1332 "Failed to compile chunked_header_regex regex");
1285 if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) { 1333 }
1286 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to compile chunked_header_regex regex"); 1334
1287 } 1335 regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF
1288 1336 // it was found
1289 regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF it was found 1337
1290 1338 if (!no_body && regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) {
1291 if (!no_body && regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) { 1339 if (verbose) {
1292 if (verbose) { 1340 printf("Found chunked content\n");
1293 printf("Found chunked content\n"); 1341 }
1294 } 1342 // We actually found the chunked header
1295 // We actually found the chunked header 1343 char *tmp = unchunk_content(page);
1296 char *tmp = unchunk_content(page); 1344 if (tmp == NULL) {
1297 if (tmp == NULL) { 1345 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN),
1298 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to unchunk message body"); 1346 "Failed to unchunk message body");
1299 } 1347 }
1300 page = tmp; 1348 page = tmp;
1301 } 1349 }
1302 1350
1303 if (strlen(string_expect) > 0) { 1351 if (strlen(string_expect) > 0) {
1304 if (!strstr(page, string_expect)) { 1352 if (!strstr(page, string_expect)) {
1305 // We found the string the body, the rest is for building the output 1353 // We found the string the body, the rest is for building the output
1306 char output_string_search[30] = ""; 1354 char output_string_search[30] = "";
1307 strncpy(&output_string_search[0], string_expect, 1355 strncpy(&output_string_search[0], string_expect, sizeof(output_string_search));
1308 sizeof(output_string_search)); 1356 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
1309 if (output_string_search[sizeof(output_string_search) - 1] != '\0') { 1357 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
1310 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 1358 }
1311 4); 1359 xasprintf(&msg, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg,
1312 } 1360 output_string_search, use_ssl ? "https" : "http",
1313 xasprintf (&msg, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); 1361 host_name ? host_name : server_address, server_port, server_url);
1314 result = STATE_CRITICAL; 1362 result = STATE_CRITICAL;
1315 } 1363 }
1316 } 1364 }
1317 1365
1318 if (strlen(regexp) > 0) { 1366 if (strlen(regexp) > 0) {
1319 errcode = regexec(&preg, page, REGS, pmatch, 0); 1367 errcode = regexec(&preg, page, REGS, pmatch, 0);
1320 if ((errcode == 0 && invert_regex == 0) || 1368 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1321 (errcode == REG_NOMATCH && invert_regex == 1)) { 1369 /* OK - No-op to avoid changing the logic around it */
1322 /* OK - No-op to avoid changing the logic around it */ 1370 result = max_state_alt(STATE_OK, result);
1323 result = max_state_alt(STATE_OK, result); 1371 } else if ((errcode == REG_NOMATCH && invert_regex == 0) ||
1324 } 1372 (errcode == 0 && invert_regex == 1)) {
1325 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) { 1373 if (invert_regex == 0) {
1326 if (invert_regex == 0) 1374 xasprintf(&msg, _("%spattern not found, "), msg);
1327 xasprintf (&msg, _("%spattern not found, "), msg); 1375 } else {
1328 else 1376 xasprintf(&msg, _("%spattern found, "), msg);
1329 xasprintf (&msg, _("%spattern found, "), msg); 1377 }
1330 result = state_regex; 1378 result = state_regex;
1331 } 1379 } else {
1332 else { 1380 /* FIXME: Shouldn't that be UNKNOWN? */
1333 /* FIXME: Shouldn't that be UNKNOWN? */ 1381 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1334 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 1382 xasprintf(&msg, _("%sExecute Error: %s, "), msg, errbuf);
1335 xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf); 1383 result = STATE_CRITICAL;
1336 result = STATE_CRITICAL; 1384 }
1337 } 1385 }
1338 } 1386
1339 1387 /* make sure the page is of an appropriate size */
1340 /* make sure the page is of an appropriate size */ 1388 /* page_len = get_content_length(header); */
1341 /* page_len = get_content_length(header); */ 1389 /* FIXME: Will this work with -N ? IMHO we should use
1342 /* FIXME: Will this work with -N ? IMHO we should use 1390 * get_content_length(header) and always check if it's different than the
1343 * get_content_length(header) and always check if it's different than the 1391 * returned pagesize
1344 * returned pagesize 1392 */
1345 */ 1393 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1346 /* FIXME: IIRC pagesize returns headers - shouldn't we make 1394 * it == get_content_length(header) ??
1347 * it == get_content_length(header) ?? 1395 */
1348 */ 1396 page_len = pagesize;
1349 page_len = pagesize; 1397 if ((max_page_len > 0) && (page_len > max_page_len)) {
1350 if ((max_page_len > 0) && (page_len > max_page_len)) { 1398 xasprintf(&msg, _("%spage size %d too large, "), msg, page_len);
1351 xasprintf (&msg, _("%spage size %d too large, "), msg, page_len); 1399 result = max_state_alt(STATE_WARNING, result);
1352 result = max_state_alt(STATE_WARNING, result); 1400 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1353 } else if ((min_page_len > 0) && (page_len < min_page_len)) { 1401 xasprintf(&msg, _("%spage size %d too small, "), msg, page_len);
1354 xasprintf (&msg, _("%spage size %d too small, "), msg, page_len); 1402 result = max_state_alt(STATE_WARNING, result);
1355 result = max_state_alt(STATE_WARNING, result); 1403 }
1356 } 1404
1357 1405 /* Cut-off trailing characters */
1358 /* Cut-off trailing characters */ 1406 if (msg[strlen(msg) - 2] == ',') {
1359 if(msg[strlen(msg)-2] == ',') 1407 msg[strlen(msg) - 2] = '\0';
1360 msg[strlen(msg)-2] = '\0'; 1408 } else {
1361 else 1409 msg[strlen(msg) - 3] = '\0';
1362 msg[strlen(msg)-3] = '\0'; 1410 }
1363 1411
1364 /* check elapsed time */ 1412 /* check elapsed time */
1365 if (show_extended_perfdata) 1413 if (show_extended_perfdata) {
1366 xasprintf (&msg, 1414 xasprintf(
1367 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"), 1415 &msg, _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"), msg,
1368 msg, page_len, elapsed_time, 1416 page_len, elapsed_time, (display_html ? "</A>" : ""), perfd_time(elapsed_time),
1369 (display_html ? "</A>" : ""), 1417 perfd_size(page_len), perfd_time_connect(elapsed_time_connect),
1370 perfd_time (elapsed_time), 1418 use_ssl ? perfd_time_ssl(elapsed_time_ssl) : "",
1371 perfd_size (page_len), 1419 perfd_time_headers(elapsed_time_headers), perfd_time_firstbyte(elapsed_time_firstbyte),
1372 perfd_time_connect (elapsed_time_connect), 1420 perfd_time_transfer(elapsed_time_transfer));
1373 use_ssl == true ? perfd_time_ssl (elapsed_time_ssl) : "", 1421 } else {
1374 perfd_time_headers (elapsed_time_headers), 1422 xasprintf(&msg, _("%s - %d bytes in %.3f second response time %s|%s %s"), msg, page_len,
1375 perfd_time_firstbyte (elapsed_time_firstbyte), 1423 elapsed_time, (display_html ? "</A>" : ""), perfd_time(elapsed_time),
1376 perfd_time_transfer (elapsed_time_transfer)); 1424 perfd_size(page_len));
1377 else 1425 }
1378 xasprintf (&msg, 1426
1379 _("%s - %d bytes in %.3f second response time %s|%s %s"), 1427 if (show_body) {
1380 msg, page_len, elapsed_time, 1428 xasprintf(&msg, _("%s\n%s"), msg, page);
1381 (display_html ? "</A>" : ""), 1429 }
1382 perfd_time (elapsed_time), 1430
1383 perfd_size (page_len)); 1431 result = max_state_alt(get_status(elapsed_time, thlds), result);
1384 1432
1385 if (show_body) 1433 die(result, "HTTP %s: %s\n", state_text(result), msg);
1386 xasprintf (&msg, _("%s\n%s"), msg, page); 1434 /* die failed? */
1387 1435 return STATE_UNKNOWN;
1388 result = max_state_alt(get_status(elapsed_time, thlds), result);
1389
1390 die (result, "HTTP %s: %s\n", state_text(result), msg);
1391 /* die failed? */
1392 return STATE_UNKNOWN;
1393} 1436}
1394 1437
1395/* Receivces a pointer to the beginning of the body of a HTTP message 1438/* Receivces a pointer to the beginning of the body of a HTTP message
@@ -1398,94 +1441,95 @@ check_http (void)
1398 * The result must be freed by the caller. 1441 * The result must be freed by the caller.
1399 */ 1442 */
1400char *unchunk_content(const char *content) { 1443char *unchunk_content(const char *content) {
1401 // https://en.wikipedia.org/wiki/Chunked_transfer_encoding 1444 // https://en.wikipedia.org/wiki/Chunked_transfer_encoding
1402 // https://www.rfc-editor.org/rfc/rfc7230#section-4.1 1445 // https://www.rfc-editor.org/rfc/rfc7230#section-4.1
1403 char *result = NULL; 1446 char *result = NULL;
1404 char *start_of_chunk; 1447 char *start_of_chunk;
1405 char* end_of_chunk; 1448 char *end_of_chunk;
1406 long size_of_chunk; 1449 long size_of_chunk;
1407 const char *pointer = content; 1450 const char *pointer = content;
1408 char *endptr; 1451 char *endptr;
1409 long length_of_chunk = 0; 1452 long length_of_chunk = 0;
1410 size_t overall_size = 0; 1453 size_t overall_size = 0;
1411 1454
1412 while (true) { 1455 while (true) {
1413 size_of_chunk = strtol(pointer, &endptr, 16); 1456 size_of_chunk = strtol(pointer, &endptr, 16);
1414 if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) { 1457 if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) {
1415 // Apparently underflow or overflow, should not happen 1458 // Apparently underflow or overflow, should not happen
1416 if (verbose) { 1459 if (verbose) {
1417 printf("Got an underflow or overflow from strtol at: %u\n", __LINE__); 1460 printf("Got an underflow or overflow from strtol at: %u\n", __LINE__);
1418 } 1461 }
1419 return NULL; 1462 return NULL;
1420 } 1463 }
1421 if (endptr == pointer) { 1464 if (endptr == pointer) {
1422 // Apparently this was not a number 1465 // Apparently this was not a number
1423 if (verbose) { 1466 if (verbose) {
1424 printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__); 1467 printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__);
1425 } 1468 }
1426 return NULL; 1469 return NULL;
1427 } 1470 }
1428 1471
1429 // So, we got the length of the chunk 1472 // So, we got the length of the chunk
1430 if (*endptr == ';') { 1473 if (*endptr == ';') {
1431 // Chunk extension starts here 1474 // Chunk extension starts here
1432 while (*endptr != '\r') { 1475 while (*endptr != '\r') {
1433 endptr++; 1476 endptr++;
1434 } 1477 }
1435 } 1478 }
1436 1479
1437 start_of_chunk = endptr + 2; 1480 start_of_chunk = endptr + 2;
1438 end_of_chunk = start_of_chunk + size_of_chunk; 1481 end_of_chunk = start_of_chunk + size_of_chunk;
1439 length_of_chunk = (long)(end_of_chunk - start_of_chunk); 1482 length_of_chunk = (long)(end_of_chunk - start_of_chunk);
1440 pointer = end_of_chunk + 2; //Next number should be here 1483 pointer = end_of_chunk + 2; // Next number should be here
1441 1484
1442 if (length_of_chunk == 0) { 1485 if (length_of_chunk == 0) {
1443 // Chunk length is 0, so this is the last one 1486 // Chunk length is 0, so this is the last one
1444 break; 1487 break;
1445 } 1488 }
1446 1489
1447 overall_size += length_of_chunk; 1490 overall_size += length_of_chunk;
1448 1491
1449 if (result == NULL) { 1492 if (result == NULL) {
1450 // Size of the chunk plus the ending NULL byte 1493 // Size of the chunk plus the ending NULL byte
1451 result = (char *)malloc(length_of_chunk +1); 1494 result = (char *)malloc(length_of_chunk + 1);
1452 if (result == NULL) { 1495 if (result == NULL) {
1453 if (verbose) { 1496 if (verbose) {
1454 printf("Failed to allocate memory for unchunked body\n"); 1497 printf("Failed to allocate memory for unchunked body\n");
1455 } 1498 }
1456 return NULL; 1499 return NULL;
1457 } 1500 }
1458 } else { 1501 } else {
1459 // Enlarge memory to the new size plus the ending NULL byte 1502 // Enlarge memory to the new size plus the ending NULL byte
1460 void *tmp = realloc(result, overall_size +1); 1503 void *tmp = realloc(result, overall_size + 1);
1461 if (tmp == NULL) { 1504 if (tmp == NULL) {
1462 if (verbose) { 1505 if (verbose) {
1463 printf("Failed to allocate memory for unchunked body\n"); 1506 printf("Failed to allocate memory for unchunked body\n");
1464 } 1507 }
1465 return NULL; 1508 return NULL;
1466 } else { 1509 }
1467 result = tmp; 1510 result = tmp;
1468 } 1511 }
1469 } 1512
1470 1513 memcpy(result + (overall_size - size_of_chunk), start_of_chunk, size_of_chunk);
1471 memcpy(result + (overall_size - size_of_chunk), start_of_chunk, size_of_chunk); 1514 }
1472 } 1515
1473 1516 if (overall_size == 0 && result == NULL) {
1474 if (overall_size == 0 && result == NULL) { 1517 // We might just have received the end chunk without previous content, so result is never
1475 // We might just have received the end chunk without previous content, so result is never allocated 1518 // allocated
1476 result = calloc(1, sizeof(char)); 1519 result = calloc(1, sizeof(char));
1477 // No error handling here, we can only return NULL anyway 1520 // No error handling here, we can only return NULL anyway
1478 } else { 1521 } else {
1479 result[overall_size] = '\0'; 1522 result[overall_size] = '\0';
1480 } 1523 }
1481 return result; 1524 return result;
1482} 1525}
1483 1526
1484/* per RFC 2396 */ 1527/* per RFC 2396 */
1485#define URI_HTTP "%5[HTPShtps]" 1528#define URI_HTTP "%5[HTPShtps]"
1486#define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" 1529#define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1487#define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */ 1530#define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1488#define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" 1531#define URI_PATH \
1532 "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1489#define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH 1533#define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1490#define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH 1534#define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1491#define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT 1535#define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
@@ -1494,414 +1538,431 @@ char *unchunk_content(const char *content) {
1494#define HD5 "//" URI_HOST "/" URI_PATH 1538#define HD5 "//" URI_HOST "/" URI_PATH
1495#define HD6 URI_PATH 1539#define HD6 URI_PATH
1496 1540
1497void 1541void redir(char *pos, char *status_line) {
1498redir (char *pos, char *status_line) 1542 int i = 0;
1499{ 1543 char *x;
1500 int i = 0; 1544 char xx[2];
1501 char *x; 1545 char type[6];
1502 char xx[2]; 1546 char *addr;
1503 char type[6]; 1547 char *url;
1504 char *addr; 1548
1505 char *url; 1549 addr = malloc(MAX_IPV4_HOSTLENGTH + 1);
1506 1550 if (addr == NULL) {
1507 addr = malloc (MAX_IPV4_HOSTLENGTH + 1); 1551 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1508 if (addr == NULL) 1552 }
1509 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n")); 1553
1510 1554 memset(addr, 0, MAX_IPV4_HOSTLENGTH);
1511 memset(addr, 0, MAX_IPV4_HOSTLENGTH); 1555 url = malloc(strcspn(pos, "\r\n"));
1512 url = malloc (strcspn (pos, "\r\n")); 1556 if (url == NULL) {
1513 if (url == NULL) 1557 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1514 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 1558 }
1515 1559
1516 while (pos) { 1560 while (pos) {
1517 sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i); 1561 sscanf(pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1518 if (i == 0) { 1562 if (i == 0) {
1519 pos += (size_t) strcspn (pos, "\r\n"); 1563 pos += (size_t)strcspn(pos, "\r\n");
1520 pos += (size_t) strspn (pos, "\r\n"); 1564 pos += (size_t)strspn(pos, "\r\n");
1521 if (strlen(pos) == 0) 1565 if (strlen(pos) == 0) {
1522 die (STATE_UNKNOWN, 1566 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1523 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"), 1567 status_line, (display_html ? "</A>" : ""));
1524 status_line, (display_html ? "</A>" : "")); 1568 }
1525 continue; 1569 continue;
1526 } 1570 }
1527 1571
1528 pos += i; 1572 pos += i;
1529 pos += strspn (pos, " \t"); 1573 pos += strspn(pos, " \t");
1530 1574
1531 /* 1575 /*
1532 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by 1576 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1533 * preceding each extra line with at least one SP or HT.'' 1577 * preceding each extra line with at least one SP or HT.''
1534 */ 1578 */
1535 for (; (i = strspn (pos, "\r\n")); pos += i) { 1579 for (; (i = strspn(pos, "\r\n")); pos += i) {
1536 pos += i; 1580 pos += i;
1537 if (!(i = strspn (pos, " \t"))) { 1581 if (!(i = strspn(pos, " \t"))) {
1538 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"), 1582 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1539 display_html ? "</A>" : ""); 1583 display_html ? "</A>" : "");
1540 } 1584 }
1541 } 1585 }
1542 1586
1543 url = realloc (url, strcspn (pos, "\r\n") + 1); 1587 url = realloc(url, strcspn(pos, "\r\n") + 1);
1544 if (url == NULL) 1588 if (url == NULL) {
1545 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 1589 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1546 1590 }
1547 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ 1591
1548 if (sscanf (pos, HD1, type, addr, &i, url) == 4) { 1592 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1549 url = prepend_slash (url); 1593 if (sscanf(pos, HD1, type, addr, &i, url) == 4) {
1550 use_ssl = server_type_check (type); 1594 url = prepend_slash(url);
1551 } 1595 use_ssl = server_type_check(type);
1552 1596 }
1553 /* URI_HTTP URI_HOST URI_PATH */ 1597
1554 else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 1598 /* URI_HTTP URI_HOST URI_PATH */
1555 url = prepend_slash (url); 1599 else if (sscanf(pos, HD2, type, addr, url) == 3) {
1556 use_ssl = server_type_check (type); 1600 url = prepend_slash(url);
1557 i = server_port_check (use_ssl); 1601 use_ssl = server_type_check(type);
1558 } 1602 i = server_port_check(use_ssl);
1559 1603 }
1560 /* URI_HTTP URI_HOST URI_PORT */ 1604
1561 else if (sscanf (pos, HD3, type, addr, &i) == 3) { 1605 /* URI_HTTP URI_HOST URI_PORT */
1562 strcpy (url, HTTP_URL); 1606 else if (sscanf(pos, HD3, type, addr, &i) == 3) {
1563 use_ssl = server_type_check (type); 1607 strcpy(url, HTTP_URL);
1564 } 1608 use_ssl = server_type_check(type);
1565 1609 }
1566 /* URI_HTTP URI_HOST */ 1610
1567 else if (sscanf (pos, HD4, type, addr) == 2) { 1611 /* URI_HTTP URI_HOST */
1568 strcpy (url, HTTP_URL); 1612 else if (sscanf(pos, HD4, type, addr) == 2) {
1569 use_ssl = server_type_check (type); 1613 strcpy(url, HTTP_URL);
1570 i = server_port_check (use_ssl); 1614 use_ssl = server_type_check(type);
1571 } 1615 i = server_port_check(use_ssl);
1572 /* URI_HTTP, URI_HOST, URI_PATH */ 1616 }
1573 else if (sscanf (pos, HD5, addr, url) == 2) { 1617 /* URI_HTTP, URI_HOST, URI_PATH */
1574 if(use_ssl){ 1618 else if (sscanf(pos, HD5, addr, url) == 2) {
1575 strcpy (type,"https"); 1619 if (use_ssl) {
1576 } 1620 strcpy(type, "https");
1577 else{ 1621 } else {
1578 strcpy (type, server_type); 1622 strcpy(type, server_type);
1579 } 1623 }
1580 xasprintf (&url, "/%s", url); 1624 xasprintf(&url, "/%s", url);
1581 use_ssl = server_type_check (type); 1625 use_ssl = server_type_check(type);
1582 i = server_port_check (use_ssl); 1626 i = server_port_check(use_ssl);
1583 } 1627 }
1584 1628
1585 /* URI_PATH */ 1629 /* URI_PATH */
1586 else if (sscanf (pos, HD6, url) == 1) { 1630 else if (sscanf(pos, HD6, url) == 1) {
1587 /* relative url */ 1631 /* relative url */
1588 if ((url[0] != '/')) { 1632 if ((url[0] != '/')) {
1589 if ((x = strrchr(server_url, '/'))) 1633 if ((x = strrchr(server_url, '/'))) {
1590 *x = '\0'; 1634 *x = '\0';
1591 xasprintf (&url, "%s/%s", server_url, url); 1635 }
1592 } 1636 xasprintf(&url, "%s/%s", server_url, url);
1593 i = server_port; 1637 }
1594 strcpy (type, server_type); 1638 i = server_port;
1595 strcpy (addr, host_name ? host_name : server_address); 1639 strcpy(type, server_type);
1596 } 1640 strcpy(addr, host_name ? host_name : server_address);
1597 1641 }
1598 else { 1642
1599 die (STATE_UNKNOWN, 1643 else {
1600 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), 1644 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), pos,
1601 pos, (display_html ? "</A>" : "")); 1645 (display_html ? "</A>" : ""));
1602 } 1646 }
1603 1647
1604 break; 1648 break;
1605 1649
1606 } /* end while (pos) */ 1650 } /* end while (pos) */
1607 1651
1608 if (++redir_depth > max_depth) 1652 if (++redir_depth > max_depth) {
1609 die (STATE_WARNING, 1653 die(STATE_WARNING,
1610 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"), 1654 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"), max_depth,
1611 max_depth, type, addr, i, url, (display_html ? "</A>" : "")); 1655 type, addr, i, url, (display_html ? "</A>" : ""));
1612 1656 }
1613 if (server_port==i && 1657
1614 !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) && 1658 if (server_port == i && !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) &&
1615 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && 1659 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, url)) {
1616 !strcmp(server_url, url)) 1660 die(STATE_CRITICAL,
1617 die (STATE_CRITICAL, 1661 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), type,
1618 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), 1662 addr, i, url, (display_html ? "</A>" : ""));
1619 type, addr, i, url, (display_html ? "</A>" : "")); 1663 }
1620 1664
1621 strcpy (server_type, type); 1665 strcpy(server_type, type);
1622 1666
1623 free (host_name); 1667 free(host_name);
1624 host_name = strndup (addr, MAX_IPV4_HOSTLENGTH); 1668 host_name = strndup(addr, MAX_IPV4_HOSTLENGTH);
1625 1669
1626 if (!(followsticky & STICKY_HOST)) { 1670 if (!(followsticky & STICKY_HOST)) {
1627 free (server_address); 1671 free(server_address);
1628 server_address = strndup (addr, MAX_IPV4_HOSTLENGTH); 1672 server_address = strndup(addr, MAX_IPV4_HOSTLENGTH);
1629 } 1673 }
1630 if (!(followsticky & STICKY_PORT)) { 1674 if (!(followsticky & STICKY_PORT)) {
1631 server_port = i; 1675 server_port = i;
1632 } 1676 }
1633 1677
1634 free (server_url); 1678 free(server_url);
1635 server_url = url; 1679 server_url = url;
1636 1680
1637 if (server_port > MAX_PORT) 1681 if (server_port > MAX_PORT) {
1638 die (STATE_UNKNOWN, 1682 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1639 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"), 1683 MAX_PORT, server_type, server_address, server_port, server_url,
1640 MAX_PORT, server_type, server_address, server_port, server_url, 1684 display_html ? "</A>" : "");
1641 display_html ? "</A>" : ""); 1685 }
1642
1643 /* reset virtual port */
1644 virtual_port = server_port;
1645
1646 if (verbose)
1647 printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1648 host_name ? host_name : server_address, server_port, server_url);
1649
1650 free(addr);
1651 check_http ();
1652}
1653 1686
1687 /* reset virtual port */
1688 virtual_port = server_port;
1654 1689
1655bool 1690 if (verbose) {
1656server_type_check (const char *type) 1691 printf(_("Redirection to %s://%s:%d%s\n"), server_type,
1657{ 1692 host_name ? host_name : server_address, server_port, server_url);
1658 if (strcmp (type, "https")) 1693 }
1659 return false; 1694
1660 else 1695 free(addr);
1661 return true; 1696 check_http();
1662} 1697}
1663 1698
1664int 1699bool server_type_check(const char *type) { return (!(bool)strcmp(type, "https")); }
1665server_port_check (int ssl_flag) 1700
1666{ 1701int server_port_check(int ssl_flag) {
1667 if (ssl_flag) 1702 if (ssl_flag) {
1668 return HTTPS_PORT; 1703 return HTTPS_PORT;
1669 else 1704 }
1670 return HTTP_PORT; 1705 return HTTP_PORT;
1671} 1706}
1672 1707
1673char *perfd_time (double elapsed_time) 1708char *perfd_time(double elapsed_time) {
1674{ 1709 return fperfdata("time", elapsed_time, "s", thlds->warning,
1675 return fperfdata ("time", elapsed_time, "s", 1710 thlds->warning ? thlds->warning->end : 0, thlds->critical,
1676 thlds->warning?true:false, thlds->warning?thlds->warning->end:0, 1711 thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout);
1677 thlds->critical?true:false, thlds->critical?thlds->critical->end:0,
1678 true, 0, true, socket_timeout);
1679} 1712}
1680 1713
1681char *perfd_time_connect (double elapsed_time_connect) 1714char *perfd_time_connect(double elapsed_time_connect) {
1682{ 1715 return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true,
1683 return fperfdata ("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1716 socket_timeout);
1684} 1717}
1685 1718
1686char *perfd_time_ssl (double elapsed_time_ssl) 1719char *perfd_time_ssl(double elapsed_time_ssl) {
1687{ 1720 return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true,
1688 return fperfdata ("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1721 socket_timeout);
1689} 1722}
1690 1723
1691char *perfd_time_headers (double elapsed_time_headers) 1724char *perfd_time_headers(double elapsed_time_headers) {
1692{ 1725 return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true,
1693 return fperfdata ("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1726 socket_timeout);
1694} 1727}
1695 1728
1696char *perfd_time_firstbyte (double elapsed_time_firstbyte) 1729char *perfd_time_firstbyte(double elapsed_time_firstbyte) {
1697{ 1730 return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0,
1698 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1731 true, socket_timeout);
1699} 1732}
1700 1733
1701char *perfd_time_transfer (double elapsed_time_transfer) 1734char *perfd_time_transfer(double elapsed_time_transfer) {
1702{ 1735 return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0,
1703 return fperfdata ("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1736 true, socket_timeout);
1704} 1737}
1705 1738
1706char *perfd_size (int page_len) 1739char *perfd_size(int page_len) {
1707{ 1740 return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0,
1708 return perfdata ("size", page_len, "B", 1741 true, 0, false, 0);
1709 (min_page_len>0?true:false), min_page_len,
1710 (min_page_len>0?true:false), 0,
1711 true, 0, false, 0);
1712} 1742}
1713 1743
1714void 1744void print_help(void) {
1715print_help (void) 1745 print_revision(progname, NP_VERSION);
1716{
1717 print_revision (progname, NP_VERSION);
1718 1746
1719 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 1747 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1720 printf (COPYRIGHT, copyright, email); 1748 printf(COPYRIGHT, copyright, email);
1721 1749
1722 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); 1750 printf("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1723 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for")); 1751 printf("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1724 printf ("%s\n", _("strings and regular expressions, check connection times, and report on")); 1752 printf("%s\n", _("strings and regular expressions, check connection times, and report on"));
1725 printf ("%s\n", _("certificate expiration times.")); 1753 printf("%s\n", _("certificate expiration times."));
1726 1754
1727 printf ("\n\n"); 1755 printf("\n");
1756 printf("%s\n", _("ATTENTION!"));
1757 printf("\n");
1758 printf("%s\n", _("THIS PLUGIN IS DEPRECATED. The functionality was reimplemented by the"));
1759 printf("%s\n", _("check_curl plugin, which can be used as a drop-in replacement. You should"));
1760 printf("%s\n", _("migrate your checks over to check_curl, because check_http is going to be"));
1761 printf("%s\n", _("removed sooner than later. Just replace check_http with check_curl in your"));
1762 printf("%s\n", _("check command definitions."));
1763 printf("%s\n",
1764 _("Report issues to: https://github.com/monitoring-plugins/monitoring-plugins/issues"));
1728 1765
1729 print_usage (); 1766 printf("\n\n");
1767
1768 print_usage();
1730 1769
1731#ifdef HAVE_SSL 1770#ifdef HAVE_SSL
1732 printf (_("In the first form, make an HTTP request.")); 1771 printf(_("In the first form, make an HTTP request."));
1733 printf (_("In the second form, connect to the server and check the TLS certificate.")); 1772 printf(_("In the second form, connect to the server and check the TLS certificate."));
1734#endif 1773#endif
1735 printf (_("NOTE: One or both of -H and -I must be specified")); 1774 printf(_("NOTE: One or both of -H and -I must be specified"));
1736 1775
1737 printf ("\n"); 1776 printf("\n");
1738 1777
1739 printf (UT_HELP_VRSN); 1778 printf(UT_HELP_VRSN);
1740 printf (UT_EXTRA_OPTS); 1779 printf(UT_EXTRA_OPTS);
1741 1780
1742 printf (" %s\n", "-H, --hostname=ADDRESS"); 1781 printf(" %s\n", "-H, --hostname=ADDRESS");
1743 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)")); 1782 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1744 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); 1783 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1745 printf (" %s\n", "-I, --IP-address=ADDRESS"); 1784 printf(" %s\n", "-I, --IP-address=ADDRESS");
1746 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); 1785 printf(" %s\n",
1747 printf (" %s\n", "-p, --port=INTEGER"); 1786 _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1748 printf (" %s", _("Port number (default: ")); 1787 printf(" %s\n", "-p, --port=INTEGER");
1749 printf ("%d)\n", HTTP_PORT); 1788 printf(" %s", _("Port number (default: "));
1789 printf("%d)\n", HTTP_PORT);
1750 1790
1751 printf (UT_IPv46); 1791 printf(UT_IPv46);
1752 1792
1753#ifdef HAVE_SSL 1793#ifdef HAVE_SSL
1754 printf (" %s\n", "-S, --ssl=VERSION[+]"); 1794 printf(" %s\n", "-S, --ssl=VERSION[+]");
1755 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); 1795 printf(" %s\n",
1756 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); 1796 _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1757 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted.")); 1797 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1758 printf (" %s\n", "--sni"); 1798 printf(" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1759 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 1799 printf(" %s\n", "--sni");
1760 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); 1800 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1761 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443")); 1801 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1762 printf (" %s\n", _("(when this option is used the URL is not checked by default. You can use")); 1802 printf(" %s\n",
1763 printf (" %s\n", _(" --continue-after-certificate to override this behavior)")); 1803 _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1764 printf (" %s\n", "--continue-after-certificate"); 1804 printf(" %s\n",
1765 printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); 1805 _("(when this option is used the URL is not checked by default. You can use"));
1766 printf (" %s\n", _("Does nothing unless -C is used.")); 1806 printf(" %s\n", _(" --continue-after-certificate to override this behavior)"));
1767 printf (" %s\n", "-J, --client-cert=FILE"); 1807 printf(" %s\n", "--continue-after-certificate");
1768 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)")); 1808 printf(" %s\n",
1769 printf (" %s\n", _("to be used in establishing the SSL session")); 1809 _("Allows the HTTP check to continue after performing the certificate check."));
1770 printf (" %s\n", "-K, --private-key=FILE"); 1810 printf(" %s\n", _("Does nothing unless -C is used."));
1771 printf (" %s\n", _("Name of file containing the private key (PEM format)")); 1811 printf(" %s\n", "-J, --client-cert=FILE");
1772 printf (" %s\n", _("matching the client certificate")); 1812 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1813 printf(" %s\n", _("to be used in establishing the SSL session"));
1814 printf(" %s\n", "-K, --private-key=FILE");
1815 printf(" %s\n", _("Name of file containing the private key (PEM format)"));
1816 printf(" %s\n", _("matching the client certificate"));
1773#endif 1817#endif
1774 1818
1775 printf (" %s\n", "-e, --expect=STRING"); 1819 printf(" %s\n", "-e, --expect=STRING");
1776 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); 1820 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1777 printf (" %s", _("the first (status) line of the server response (default: ")); 1821 printf(" %s", _("the first (status) line of the server response (default: "));
1778 printf ("%s)\n", HTTP_EXPECT); 1822 printf("%s)\n", HTTP_EXPECT);
1779 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); 1823 printf(" %s\n",
1780 printf (" %s\n", "-d, --header-string=STRING"); 1824 _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1781 printf (" %s\n", _("String to expect in the response headers")); 1825 printf(" %s\n", "-d, --header-string=STRING");
1782 printf (" %s\n", "-s, --string=STRING"); 1826 printf(" %s\n", _("String to expect in the response headers"));
1783 printf (" %s\n", _("String to expect in the content")); 1827 printf(" %s\n", "-s, --string=STRING");
1784 printf (" %s\n", "-u, --url=PATH"); 1828 printf(" %s\n", _("String to expect in the content"));
1785 printf (" %s\n", _("URL to GET or POST (default: /)")); 1829 printf(" %s\n", "-u, --url=PATH");
1786 printf (" %s\n", "-P, --post=STRING"); 1830 printf(" %s\n", _("URL to GET or POST (default: /)"));
1787 printf (" %s\n", _("URL decoded http POST data")); 1831 printf(" %s\n", "-P, --post=STRING");
1788 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT, CONNECT:POST)"); 1832 printf(" %s\n", _("URL decoded http POST data"));
1789 printf (" %s\n", _("Set HTTP method.")); 1833 printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, "
1790 printf (" %s\n", "-N, --no-body"); 1834 "CONNECT, CONNECT:POST)");
1791 printf (" %s\n", _("Don't wait for document body: stop reading after headers.")); 1835 printf(" %s\n", _("Set HTTP method."));
1792 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)")); 1836 printf(" %s\n", "-N, --no-body");
1793 printf (" %s\n", "-M, --max-age=SECONDS"); 1837 printf(" %s\n", _("Don't wait for document body: stop reading after headers."));
1794 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of")); 1838 printf(" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1795 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.")); 1839 printf(" %s\n", "-M, --max-age=SECONDS");
1796 printf (" %s\n", "-T, --content-type=STRING"); 1840 printf(" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1797 printf (" %s\n", _("specify Content-Type header media type when POSTing\n")); 1841 printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1798 1842 printf(" %s\n", "-T, --content-type=STRING");
1799 printf (" %s\n", "-l, --linespan"); 1843 printf(" %s\n", _("specify Content-Type header media type when POSTing\n"));
1800 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)")); 1844
1801 printf (" %s\n", "-r, --regex, --ereg=STRING"); 1845 printf(" %s\n", "-l, --linespan");
1802 printf (" %s\n", _("Search page for regex STRING")); 1846 printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1803 printf (" %s\n", "-R, --eregi=STRING"); 1847 printf(" %s\n", "-r, --regex, --ereg=STRING");
1804 printf (" %s\n", _("Search page for case-insensitive regex STRING")); 1848 printf(" %s\n", _("Search page for regex STRING"));
1805 printf (" %s\n", "--invert-regex"); 1849 printf(" %s\n", "-R, --eregi=STRING");
1806 printf (" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); 1850 printf(" %s\n", _("Search page for case-insensitive regex STRING"));
1807 printf (" %s\n", _("can be changed with --state--regex)")); 1851 printf(" %s\n", "--invert-regex");
1808 printf (" %s\n", "--regex-state=STATE"); 1852 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
1809 printf (" %s\n", _("Return STATE if regex is found, OK if not\n")); 1853 printf(" %s\n", _("can be changed with --state--regex)"));
1810 1854 printf(" %s\n", "--state-regex=STATE");
1811 printf (" %s\n", "-a, --authorization=AUTH_PAIR"); 1855 printf(" %s\n", _("Return STATE if regex is found, OK if not\n"));
1812 printf (" %s\n", _("Username:password on sites with basic authentication")); 1856
1813 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); 1857 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1814 printf (" %s\n", _("Username:password on proxy-servers with basic authentication")); 1858 printf(" %s\n", _("Username:password on sites with basic authentication"));
1815 printf (" %s\n", "-A, --useragent=STRING"); 1859 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1816 printf (" %s\n", _("String to be sent in http header as \"User Agent\"")); 1860 printf(" %s\n", _("Username:password on proxy-servers with basic authentication"));
1817 printf (" %s\n", "-k, --header=STRING"); 1861 printf(" %s\n", "-A, --useragent=STRING");
1818 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); 1862 printf(" %s\n", _("String to be sent in http header as \"User Agent\""));
1819 printf (" %s\n", "-E, --extended-perfdata"); 1863 printf(" %s\n", "-k, --header=STRING");
1820 printf (" %s\n", _("Print additional performance data")); 1864 printf(
1821 printf (" %s\n", "-B, --show-body"); 1865 " %s\n",
1822 printf (" %s\n", _("Print body content below status line")); 1866 _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1823 printf (" %s\n", "-L, --link"); 1867 printf(" %s\n", "-E, --extended-perfdata");
1824 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); 1868 printf(" %s\n", _("Print additional performance data"));
1825 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>"); 1869 printf(" %s\n", "-B, --show-body");
1826 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); 1870 printf(" %s\n", _("Print body content below status line"));
1827 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); 1871 printf(" %s\n", "-L, --link");
1828 printf (" %s\n", "--max-redirs=INTEGER"); 1872 printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1829 printf (" %s", _("Maximal number of redirects (default: ")); 1873 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1830 printf ("%d)\n", DEFAULT_MAX_REDIRS); 1874 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1831 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); 1875 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1832 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); 1876 printf(" %s\n", "--max-redirs=INTEGER");
1833 printf (UT_WARN_CRIT); 1877 printf(" %s", _("Maximal number of redirects (default: "));
1834 1878 printf("%d)\n", DEFAULT_MAX_REDIRS);
1835 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1879 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1836 1880 printf(" %s\n",
1837 printf (UT_VERBOSE); 1881 _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1838 1882 printf(UT_WARN_CRIT);
1839 printf ("\n"); 1883
1840 printf ("%s\n", _("Notes:")); 1884 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1841 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); 1885
1842 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); 1886 printf(UT_VERBOSE);
1843 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); 1887
1844 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); 1888 printf("\n");
1845 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); 1889 printf("%s\n", _("Notes:"));
1846 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); 1890 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1891 printf(" %s\n",
1892 _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1893 printf(" %s\n",
1894 _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1895 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1896 printf(" %s\n",
1897 _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1898 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1847 1899
1848#ifdef HAVE_SSL 1900#ifdef HAVE_SSL
1849 printf ("\n"); 1901 printf("\n");
1850 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to")); 1902 printf(" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1851 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 ")); 1903 printf(" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1852 printf (" %s\n", _("certificate is still valid for the specified number of days.")); 1904 printf(" %s\n", _("certificate is still valid for the specified number of days."));
1853 printf ("\n"); 1905 printf("\n");
1854 printf (" %s\n", _("Please note that this plugin does not check if the presented server")); 1906 printf(" %s\n", _("Please note that this plugin does not check if the presented server"));
1855 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate")); 1907 printf(" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1856 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs.")); 1908 printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1857 printf ("\n"); 1909 printf("\n");
1858 printf ("%s\n", _("Examples:")); 1910 printf("%s\n", _("Examples:"));
1859 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com"); 1911 printf(" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1860 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); 1912 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1861 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1913 printf(" %s\n",
1862 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1914 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1863 printf (" %s\n", _("a STATE_CRITICAL will be returned.")); 1915 printf(" %s\n",
1864 printf ("\n"); 1916 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1865 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14"); 1917 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
1866 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); 1918 printf("\n");
1867 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1919 printf(" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1868 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); 1920 printf(" %s\n",
1869 printf (" %s\n\n", _("the certificate is expired.")); 1921 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1870 printf ("\n"); 1922 printf(" %s\n",
1871 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14"); 1923 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1872 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); 1924 printf(" %s\n",
1873 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1925 _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1874 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); 1926 printf(" %s\n\n", _("the certificate is expired."));
1875 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); 1927 printf("\n");
1876 1928 printf(" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1877 printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); 1929 printf(" %s\n",
1878 printf (" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); 1930 _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1879 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>")); 1931 printf(" %s\n",
1880 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1932 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1881 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1933 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1882 printf (" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can set the method used")); 1934 printf(" %s\n",
1883 printf (" %s\n", _("inside the proxied connection: -j CONNECT:POST")); 1935 _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1936
1937 printf(" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1938 printf(" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j "
1939 "CONNECT -H www.verisign.com "));
1940 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
1941 "-S(sl) -j CONNECT -H <webserver>"));
1942 printf(" %s\n",
1943 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1944 printf(" %s\n",
1945 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1946 printf(" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can "
1947 "set the method used"));
1948 printf(" %s\n", _("inside the proxied connection: -j CONNECT:POST"));
1884 1949
1885#endif 1950#endif
1886 1951
1887 printf (UT_SUPPORT); 1952 printf(UT_SUPPORT);
1888
1889} 1953}
1890 1954
1891 1955void print_usage(void) {
1892 1956 printf("%s\n", _("Usage:"));
1893void 1957 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
1894print_usage (void) 1958 printf(" [-J <client certificate file>] [-K <private key>]\n");
1895{ 1959 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1896 printf ("%s\n", _("Usage:")); 1960 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n");
1897 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname); 1961 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
1898 printf (" [-J <client certificate file>] [-K <private key>]\n"); 1962 "regex>]\n");
1899 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1963 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1900 printf (" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n"); 1964 printf(" [-A string] [-k string] [-S <version>] [--sni]\n");
1901 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); 1965 printf(" [-T <content-type>] [-j method]\n");
1902 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1966 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname);
1903 printf (" [-A string] [-k string] [-S <version>] [--sni]\n"); 1967 printf(" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
1904 printf (" [-T <content-type>] [-j method]\n");
1905 printf (" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n",progname);
1906 printf (" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
1907} 1968}
diff --git a/plugins/check_ide_smart.c b/plugins/check_ide_smart.c
index 3872e341..43731039 100644
--- a/plugins/check_ide_smart.c
+++ b/plugins/check_ide_smart.c
@@ -1,307 +1,287 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ide_smart plugin 3 * Monitoring check_ide_smart plugin
4* ide-smart 1.3 - IDE S.M.A.R.T. checking tool 4 * ide-smart 1.3 - IDE S.M.A.R.T. checking tool
5* 5 *
6* License: GPL 6 * License: GPL
7* Copyright (C) 1998-1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org> 7 * Copyright (C) 1998-1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org>
8* 1998 Gadi Oxman <gadio@netvision.net.il> 8 * 1998 Gadi Oxman <gadio@netvision.net.il>
9* Copyright (c) 2000 Robert Dale <rdale@digital-mission.com> 9 * Copyright (c) 2000 Robert Dale <rdale@digital-mission.com>
10* Copyright (c) 2000-2007 Monitoring Plugins Development Team 10 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
11* 11 *
12* Description: 12 * Description:
13* 13 *
14* This file contains the check_ide_smart plugin 14 * This file contains the check_ide_smart plugin
15* 15 *
16* This plugin checks a local hard drive with the (Linux specific) SMART 16 * This plugin checks a local hard drive with the (Linux specific) SMART
17* interface 17 * interface
18* 18 *
19* 19 *
20* This program is free software: you can redistribute it and/or modify 20 * This program is free software: you can redistribute it and/or modify
21* it under the terms of the GNU General Public License as published by 21 * it under the terms of the GNU General Public License as published by
22* the Free Software Foundation, either version 3 of the License, or 22 * the Free Software Foundation, either version 3 of the License, or
23* (at your option) any later version. 23 * (at your option) any later version.
24* 24 *
25* This program is distributed in the hope that it will be useful, 25 * This program is distributed in the hope that it will be useful,
26* but WITHOUT ANY WARRANTY; without even the implied warranty of 26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28* GNU General Public License for more details. 28 * GNU General Public License for more details.
29* 29 *
30* You should have received a copy of the GNU General Public License 30 * You should have received a copy of the GNU General Public License
31* along with this program. If not, see <http://www.gnu.org/licenses/>. 31 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32* 32 *
33* 33 *
34*****************************************************************************/ 34 *****************************************************************************/
35 35
36const char *progname = "check_ide_smart"; 36const char *progname = "check_ide_smart";
37const char *copyright = "1998-2007"; 37const char *copyright = "1998-2024";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "common.h" 40#include "common.h"
41#include "utils.h" 41#include "utils.h"
42#include "check_ide_smart.d/config.h"
43#include "states.h"
42 44
43void print_help (void); 45static void print_help(void);
44void print_usage (void); 46void print_usage(void);
45 47
46#include <sys/stat.h> 48#include <sys/stat.h>
47#include <sys/ioctl.h> 49#include <sys/ioctl.h>
48#include <fcntl.h> 50#include <fcntl.h>
51
49#ifdef __linux__ 52#ifdef __linux__
50#include <linux/hdreg.h> 53# include <linux/hdreg.h>
51#include <linux/types.h> 54# include <linux/types.h>
52 55
53#define OPEN_MODE O_RDONLY 56# define OPEN_MODE O_RDONLY
54#endif /* __linux__ */ 57#endif /* __linux__ */
55#ifdef __NetBSD__ 58#ifdef __NetBSD__
56#include <sys/device.h> 59# include <sys/device.h>
57#include <sys/param.h> 60# include <sys/param.h>
58#include <sys/sysctl.h> 61# include <sys/sysctl.h>
59#include <sys/videoio.h> /* for __u8 and friends */ 62# include <sys/scsiio.h>
60#include <sys/scsiio.h> 63# include <sys/ataio.h>
61#include <sys/ataio.h> 64# include <dev/ata/atareg.h>
62#include <dev/ata/atareg.h> 65# include <dev/ic/wdcreg.h>
63#include <dev/ic/wdcreg.h> 66
64 67# define SMART_ENABLE WDSM_ENABLE_OPS
65#define SMART_ENABLE WDSM_ENABLE_OPS 68# define SMART_DISABLE WDSM_DISABLE_OPS
66#define SMART_DISABLE WDSM_DISABLE_OPS 69# define SMART_IMMEDIATE_OFFLINE WDSM_EXEC_OFFL_IMM
67#define SMART_IMMEDIATE_OFFLINE WDSM_EXEC_OFFL_IMM 70# define SMART_AUTO_OFFLINE 0xdb /* undefined in NetBSD headers */
68#define SMART_AUTO_OFFLINE 0xdb /* undefined in NetBSD headers */ 71
69 72# define OPEN_MODE O_RDWR
70#define OPEN_MODE O_RDWR
71#endif /* __NetBSD__ */ 73#endif /* __NetBSD__ */
72#include <errno.h> 74#include <errno.h>
73
74#define NR_ATTRIBUTES 30
75
76#define PREFAILURE 2
77#define ADVISORY 1
78#define OPERATIONAL 0
79#define UNKNOWN -1
80 75
81typedef struct threshold_s 76#define NR_ATTRIBUTES 30
82{
83 __u8 id;
84 __u8 threshold;
85 __u8 reserved[10];
86}
87__attribute__ ((packed)) threshold_t;
88
89typedef struct thresholds_s
90{
91 __u16 revision;
92 threshold_t thresholds[NR_ATTRIBUTES];
93 __u8 reserved[18];
94 __u8 vendor[131];
95 __u8 checksum;
96}
97__attribute__ ((packed)) thresholds_t;
98
99typedef struct value_s
100{
101 __u8 id;
102 __u16 status;
103 __u8 value;
104 __u8 vendor[8];
105}
106__attribute__ ((packed)) value_t;
107
108typedef struct values_s
109{
110 __u16 revision;
111 value_t values[NR_ATTRIBUTES];
112 __u8 offline_status;
113 __u8 vendor1;
114 __u16 offline_timeout;
115 __u8 vendor2;
116 __u8 offline_capability;
117 __u16 smart_capability;
118 __u8 reserved[16];
119 __u8 vendor[125];
120 __u8 checksum;
121}
122__attribute__ ((packed)) values_t;
123 77
124struct 78#define PREFAILURE 2
125{ 79#define ADVISORY 1
126 __u8 value; 80#define OPERATIONAL 0
81#define UNKNOWN -1
82
83typedef struct {
84 uint8_t id;
85 uint8_t threshold;
86 uint8_t reserved[10];
87} __attribute__((packed)) smart_threshold;
88
89typedef struct {
90 uint16_t revision;
91 smart_threshold thresholds[NR_ATTRIBUTES];
92 uint8_t reserved[18];
93 uint8_t vendor[131];
94 uint8_t checksum;
95} __attribute__((packed)) smart_thresholds;
96
97typedef struct {
98 uint8_t id;
99 uint16_t status;
100 uint8_t value;
101 uint8_t vendor[8];
102} __attribute__((packed)) smart_value;
103
104typedef struct {
105 uint16_t revision;
106 smart_value values[NR_ATTRIBUTES];
107 uint8_t offline_status;
108 uint8_t vendor1;
109 uint16_t offline_timeout;
110 uint8_t vendor2;
111 uint8_t offline_capability;
112 uint16_t smart_capability;
113 uint8_t reserved[16];
114 uint8_t vendor[125];
115 uint8_t checksum;
116} __attribute__((packed)) smart_values;
117
118static struct {
119 uint8_t value;
127 char *text; 120 char *text;
128} 121} offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
129
130offline_status_text[] =
131 {
132 {0x00, "NeverStarted"},
133 {0x02, "Completed"},
134 {0x04, "Suspended"},
135 {0x05, "Aborted"},
136 {0x06, "Failed"},
137 {0, 0}
138 };
139 122
140struct 123static struct {
141{ 124 uint8_t value;
142 __u8 value;
143 char *text; 125 char *text;
144} 126} smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"},
145 127 {SMART_DISABLE, "SMART_DISABLE"},
146smart_command[] = 128 {SMART_IMMEDIATE_OFFLINE, "SMART_IMMEDIATE_OFFLINE"},
147 { 129 {SMART_AUTO_OFFLINE, "SMART_AUTO_OFFLINE"}};
148 {SMART_ENABLE, "SMART_ENABLE"}, 130
149 {SMART_DISABLE, "SMART_DISABLE"}, 131/* Index to smart_command table, keep in order */
150 {SMART_IMMEDIATE_OFFLINE, "SMART_IMMEDIATE_OFFLINE"}, 132enum SmartCommand {
151 {SMART_AUTO_OFFLINE, "SMART_AUTO_OFFLINE"} 133 SMART_CMD_ENABLE,
152 }; 134 SMART_CMD_DISABLE,
153 135 SMART_CMD_IMMEDIATE_OFFLINE,
154 136 SMART_CMD_AUTO_OFFLINE
155/* Index to smart_command table, keep in order */ 137};
156enum SmartCommand 138
157 { SMART_CMD_ENABLE, 139static char *get_offline_text(int /*status*/);
158 SMART_CMD_DISABLE, 140static int smart_read_values(int /*fd*/, smart_values * /*values*/);
159 SMART_CMD_IMMEDIATE_OFFLINE, 141static mp_state_enum compare_values_and_thresholds(smart_values * /*p*/, smart_thresholds * /*t*/);
160 SMART_CMD_AUTO_OFFLINE 142static void print_value(smart_value * /*p*/, smart_threshold * /*t*/);
143static void print_values(smart_values * /*p*/, smart_thresholds * /*t*/);
144static mp_state_enum smart_cmd_simple(int /*fd*/, enum SmartCommand /*command*/, uint8_t /*val0*/, bool /*show_error*/);
145static int smart_read_thresholds(int /*fd*/, smart_thresholds * /*thresholds*/);
146static int verbose = 0;
147
148typedef struct {
149 int errorcode;
150 check_ide_smart_config config;
151} check_ide_smart_config_wrapper;
152static check_ide_smart_config_wrapper process_arguments(int argc, char **argv) {
153 static struct option longopts[] = {{"device", required_argument, 0, 'd'},
154 {"immediate", no_argument, 0, 'i'},
155 {"quiet-check", no_argument, 0, 'q'},
156 {"auto-on", no_argument, 0, '1'},
157 {"auto-off", no_argument, 0, '0'},
158 {"nagios", no_argument, 0, 'n'}, /* DEPRECATED, but we still accept it */
159 {"help", no_argument, 0, 'h'},
160 {"version", no_argument, 0, 'V'},
161 {0, 0, 0, 0}};
162
163 check_ide_smart_config_wrapper result = {
164 .errorcode = OK,
165 .config = check_ide_smart_init(),
161 }; 166 };
162 167
163char *get_offline_text (int);
164int smart_read_values (int, values_t *);
165int nagios (values_t *, thresholds_t *);
166void print_value (value_t *, threshold_t *);
167void print_values (values_t *, thresholds_t *);
168int smart_cmd_simple (int, enum SmartCommand, __u8, bool);
169int smart_read_thresholds (int, thresholds_t *);
170bool verbose = false;
171
172int
173main (int argc, char *argv[])
174{
175 char *device = NULL;
176 int o, longindex;
177 int retval = 0;
178
179 thresholds_t thresholds;
180 values_t values;
181 int fd;
182
183 static struct option longopts[] = {
184 {"device", required_argument, 0, 'd'},
185 {"immediate", no_argument, 0, 'i'},
186 {"quiet-check", no_argument, 0, 'q'},
187 {"auto-on", no_argument, 0, '1'},
188 {"auto-off", no_argument, 0, '0'},
189 {"nagios", no_argument, 0, 'n'}, /* DEPRECATED, but we still accept it */
190 {"help", no_argument, 0, 'h'},
191 {"version", no_argument, 0, 'V'},
192 {0, 0, 0, 0}
193 };
194
195 /* Parse extra opts if any */
196 argv=np_extra_opts (&argc, argv, progname);
197
198 setlocale (LC_ALL, "");
199 bindtextdomain (PACKAGE, LOCALEDIR);
200 textdomain (PACKAGE);
201
202 while (true) { 168 while (true) {
203 169 int longindex = 0;
204 o = getopt_long (argc, argv, "+d:iq10nhVv", longopts, &longindex); 170 int option_index = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex);
205 171
206 if (o == -1 || o == EOF || o == 1) 172 if (CHECK_EOF(option_index) || option_index == 1) {
207 break; 173 break;
174 }
208 175
209 switch (o) { 176 switch (option_index) {
210 case 'd': 177 case 'd':
211 device = optarg; 178 result.config.device = optarg;
212 break; 179 break;
213 case 'q': 180 case 'q':
214 fprintf (stderr, "%s\n", _("DEPRECATION WARNING: the -q switch (quiet output) is no longer \"quiet\".")); 181 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -q switch (quiet output) is no longer \"quiet\"."));
215 fprintf (stderr, "%s\n", _("Nagios-compatible output is now always returned.")); 182 fprintf(stderr, "%s\n", _("Nagios-compatible output is now always returned."));
216 break; 183 break;
217 case 'i': 184 case 'i':
218 case '1': 185 case '1':
219 case '0': 186 case '0':
220 printf ("%s\n", _("SMART commands are broken and have been disabled (See Notes in --help).")); 187 printf("%s\n", _("SMART commands are broken and have been disabled (See Notes in --help)."));
221 return STATE_CRITICAL; 188 result.errorcode = ERROR;
189 return result;
222 break; 190 break;
223 case 'n': 191 case 'n':
224 fprintf (stderr, "%s\n", _("DEPRECATION WARNING: the -n switch (Nagios-compatible output) is now the")); 192 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -n switch (Nagios-compatible output) is now the"));
225 fprintf (stderr, "%s\n", _("default and will be removed from future releases.")); 193 fprintf(stderr, "%s\n", _("default and will be removed from future releases."));
226 break; 194 break;
227 case 'v': /* verbose */ 195 case 'v': /* verbose */
228 verbose = true; 196 verbose++;
229 break; 197 break;
230 case 'h': 198 case 'h':
231 print_help (); 199 print_help();
232 return STATE_UNKNOWN; 200 exit(STATE_UNKNOWN);
233 case 'V': 201 case 'V':
234 print_revision (progname, NP_VERSION); 202 print_revision(progname, NP_VERSION);
235 return STATE_UNKNOWN; 203 exit(STATE_UNKNOWN);
236 default: 204 default:
237 usage5 (); 205 usage5();
238 } 206 }
239 } 207 }
240 208
241 if (optind < argc) { 209 if (optind < argc) {
242 device = argv[optind]; 210 result.config.device = argv[optind];
243 } 211 }
244 212
245 if (!device) { 213 if (result.config.device == NULL) {
246 print_help (); 214 print_help();
247 return STATE_UNKNOWN; 215 exit(STATE_UNKNOWN);
248 } 216 }
249 217
250 fd = open (device, OPEN_MODE); 218 return result;
219}
220
221int main(int argc, char *argv[]) {
222 setlocale(LC_ALL, "");
223 bindtextdomain(PACKAGE, LOCALEDIR);
224 textdomain(PACKAGE);
225
226 /* Parse extra opts if any */
227 argv = np_extra_opts(&argc, argv, progname);
228
229 check_ide_smart_config_wrapper tmp_config = process_arguments(argc, argv);
251 230
252 if (fd < 0) { 231 if (tmp_config.errorcode != OK) {
253 printf (_("CRITICAL - Couldn't open device %s: %s\n"), device, strerror (errno)); 232 die(STATE_UNKNOWN, _("Failed to parse commandline"));
233 }
234
235 check_ide_smart_config config = tmp_config.config;
236
237 int device_file_descriptor = open(config.device, OPEN_MODE);
238
239 if (device_file_descriptor < 0) {
240 printf(_("CRITICAL - Couldn't open device %s: %s\n"), config.device, strerror(errno));
254 return STATE_CRITICAL; 241 return STATE_CRITICAL;
255 } 242 }
256 243
257 if (smart_cmd_simple (fd, SMART_CMD_ENABLE, 0, false)) { 244 if (smart_cmd_simple(device_file_descriptor, SMART_CMD_ENABLE, 0, false)) {
258 printf (_("CRITICAL - SMART_CMD_ENABLE\n")); 245 printf(_("CRITICAL - SMART_CMD_ENABLE\n"));
259 return STATE_CRITICAL; 246 return STATE_CRITICAL;
260 } 247 }
261 248
262 smart_read_values (fd, &values); 249 smart_values values;
263 smart_read_thresholds (fd, &thresholds); 250 smart_read_values(device_file_descriptor, &values);
264 retval = nagios (&values, &thresholds); 251 smart_thresholds thresholds;
265 if (verbose) print_values (&values, &thresholds); 252 smart_read_thresholds(device_file_descriptor, &thresholds);
253 mp_state_enum retval = compare_values_and_thresholds(&values, &thresholds);
254 if (verbose) {
255 print_values(&values, &thresholds);
256 }
266 257
267 close (fd); 258 close(device_file_descriptor);
268 return retval; 259 return retval;
269} 260}
270 261
271 262char *get_offline_text(int status) {
272 263 for (int index = 0; offline_status_text[index].text; index++) {
273char * 264 if (offline_status_text[index].value == status) {
274get_offline_text (int status) 265 return offline_status_text[index].text;
275{
276 int i;
277 for (i = 0; offline_status_text[i].text; i++) {
278 if (offline_status_text[i].value == status) {
279 return offline_status_text[i].text;
280 } 266 }
281 } 267 }
282 return "UNKNOWN"; 268 return "UNKNOWN";
283} 269}
284 270
285 271int smart_read_values(int file_descriptor, smart_values *values) {
286
287int
288smart_read_values (int fd, values_t * values)
289{
290#ifdef __linux__ 272#ifdef __linux__
291 int e; 273 uint8_t args[4 + 512];
292 __u8 args[4 + 512];
293 args[0] = WIN_SMART; 274 args[0] = WIN_SMART;
294 args[1] = 0; 275 args[1] = 0;
295 args[2] = SMART_READ_VALUES; 276 args[2] = SMART_READ_VALUES;
296 args[3] = 1; 277 args[3] = 1;
297 if (ioctl (fd, HDIO_DRIVE_CMD, &args)) { 278 if (ioctl(file_descriptor, HDIO_DRIVE_CMD, &args)) {
298 e = errno; 279 int errno_storage = errno;
299 printf (_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror (errno)); 280 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno));
300 return e; 281 return errno_storage;
301 } 282 }
302 memcpy (values, args + 4, 512); 283 memcpy(values, args + 4, 512);
303#endif /* __linux__ */ 284#elif defined __NetBSD__
304#ifdef __NetBSD__
305 struct atareq req; 285 struct atareq req;
306 unsigned char inbuf[DEV_BSIZE]; 286 unsigned char inbuf[DEV_BSIZE];
307 287
@@ -316,50 +296,48 @@ smart_read_values (int fd, values_t * values)
316 req.datalen = sizeof(inbuf); 296 req.datalen = sizeof(inbuf);
317 req.cylinder = WDSMART_CYL; 297 req.cylinder = WDSMART_CYL;
318 298
319 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 299 if (ioctl(file_descriptor, ATAIOCCOMMAND, &req) == 0) {
320 if (req.retsts != ATACMD_OK) 300 if (req.retsts != ATACMD_OK) {
321 errno = ENODEV; 301 errno = ENODEV;
302 }
322 } 303 }
323 304
324 if (errno != 0) { 305 if (errno != 0) {
325 int e = errno; 306 int errno_storage = errno;
326 printf (_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror (errno)); 307 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno));
327 return e; 308 return errno_storage;
328 } 309 }
329 310
330 (void)memcpy(values, inbuf, 512); 311 (void)memcpy(values, inbuf, 512);
331#endif /* __NetBSD__ */ 312#else // __linux__ || __NetBSD__
313# error Not implemented for this OS
314#endif
315
332 return 0; 316 return 0;
333} 317}
334 318
319mp_state_enum compare_values_and_thresholds(smart_values *values, smart_thresholds *thresholds) {
320 smart_value *value = values->values;
321 smart_threshold *threshold = thresholds->thresholds;
335 322
336
337int
338nagios (values_t * p, thresholds_t * t)
339{
340 value_t * value = p->values;
341 threshold_t * threshold = t->thresholds;
342 int status = OPERATIONAL; 323 int status = OPERATIONAL;
343 int prefailure = 0; 324 int prefailure = 0;
344 int advisory = 0; 325 int advisory = 0;
345 int failed = 0; 326 int failed = 0;
346 int passed = 0; 327 int passed = 0;
347 int total = 0; 328 int total = 0;
348 int i; 329 for (int i = 0; i < NR_ATTRIBUTES; i++) {
349 for (i = 0; i < NR_ATTRIBUTES; i++) {
350 if (value->id && threshold->id && value->id == threshold->id) { 330 if (value->id && threshold->id && value->id == threshold->id) {
351 if (value->value < threshold->threshold) { 331 if (value->value < threshold->threshold) {
352 ++failed; 332 ++failed;
353 if (value->status & 1) { 333 if (value->status & 1) {
354 status = PREFAILURE; 334 status = PREFAILURE;
355 ++prefailure; 335 ++prefailure;
356 } 336 } else {
357 else {
358 status = ADVISORY; 337 status = ADVISORY;
359 ++advisory; 338 ++advisory;
360 } 339 }
361 } 340 } else {
362 else {
363 ++passed; 341 ++passed;
364 } 342 }
365 ++total; 343 ++total;
@@ -367,102 +345,74 @@ nagios (values_t * p, thresholds_t * t)
367 ++value; 345 ++value;
368 ++threshold; 346 ++threshold;
369 } 347 }
348
370 switch (status) { 349 switch (status) {
371 case PREFAILURE: 350 case PREFAILURE:
372 printf (_("CRITICAL - %d Harddrive PreFailure%cDetected! %d/%d tests failed.\n"), 351 printf(_("CRITICAL - %d Harddrive PreFailure%cDetected! %d/%d tests failed.\n"), prefailure, prefailure > 1 ? 's' : ' ', failed,
373 prefailure, 352 total);
374 prefailure > 1 ? 's' : ' ', 353 status = STATE_CRITICAL;
375 failed,
376 total);
377 status=STATE_CRITICAL;
378 break; 354 break;
379 case ADVISORY: 355 case ADVISORY:
380 printf (_("WARNING - %d Harddrive Advisor%s Detected. %d/%d tests failed.\n"), 356 printf(_("WARNING - %d Harddrive Advisor%s Detected. %d/%d tests failed.\n"), advisory, advisory > 1 ? "ies" : "y", failed, total);
381 advisory, 357 status = STATE_WARNING;
382 advisory > 1 ? "ies" : "y",
383 failed,
384 total);
385 status=STATE_WARNING;
386 break; 358 break;
387 case OPERATIONAL: 359 case OPERATIONAL:
388 printf (_("OK - Operational (%d/%d tests passed)\n"), passed, total); 360 printf(_("OK - Operational (%d/%d tests passed)\n"), passed, total);
389 status=STATE_OK; 361 status = STATE_OK;
390 break; 362 break;
391 default: 363 default:
392 printf (_("ERROR - Status '%d' unknown. %d/%d tests passed\n"), status, 364 printf(_("ERROR - Status '%d' unknown. %d/%d tests passed\n"), status, passed, total);
393 passed, total);
394 status = STATE_UNKNOWN; 365 status = STATE_UNKNOWN;
395 break; 366 break;
396 } 367 }
397 return status; 368 return status;
398} 369}
399 370
400 371void print_value(smart_value *value_pointer, smart_threshold *threshold_pointer) {
401 372 printf("Id=%3d, Status=%2d {%s , %s}, Value=%3d, Threshold=%3d, %s\n", value_pointer->id, value_pointer->status,
402void 373 value_pointer->status & 1 ? "PreFailure" : "Advisory ", value_pointer->status & 2 ? "OnLine " : "OffLine",
403print_value (value_t * p, threshold_t * t) 374 value_pointer->value, threshold_pointer->threshold, value_pointer->value >= threshold_pointer->threshold ? "Passed" : "Failed");
404{
405 printf ("Id=%3d, Status=%2d {%s , %s}, Value=%3d, Threshold=%3d, %s\n",
406 p->id, p->status, p->status & 1 ? "PreFailure" : "Advisory ",
407 p->status & 2 ? "OnLine " : "OffLine", p->value, t->threshold,
408 p->value >= t->threshold ? "Passed" : "Failed");
409} 375}
410 376
411 377void print_values(smart_values *values, smart_thresholds *thresholds) {
412 378 smart_value *value = values->values;
413void 379 smart_threshold *threshold = thresholds->thresholds;
414print_values (values_t * p, thresholds_t * t) 380 for (int i = 0; i < NR_ATTRIBUTES; i++) {
415{
416 value_t * value = p->values;
417 threshold_t * threshold = t->thresholds;
418 int i;
419 for (i = 0; i < NR_ATTRIBUTES; i++) {
420 if (value->id && threshold->id && value->id == threshold->id) { 381 if (value->id && threshold->id && value->id == threshold->id) {
421 print_value (value++, threshold++); 382 print_value(value++, threshold++);
422 } 383 }
423 } 384 }
424 printf 385 printf(_("OffLineStatus=%d {%s}, AutoOffLine=%s, OffLineTimeout=%d minutes\n"), values->offline_status,
425 (_("OffLineStatus=%d {%s}, AutoOffLine=%s, OffLineTimeout=%d minutes\n"), 386 get_offline_text(values->offline_status & 0x7f), (values->offline_status & 0x80 ? "Yes" : "No"), values->offline_timeout / 60);
426 p->offline_status, 387 printf(_("OffLineCapability=%d {%s %s %s}\n"), values->offline_capability, values->offline_capability & 1 ? "Immediate" : "",
427 get_offline_text (p->offline_status & 0x7f), 388 values->offline_capability & 2 ? "Auto" : "", values->offline_capability & 4 ? "AbortOnCmd" : "SuspendOnCmd");
428 (p->offline_status & 0x80 ? "Yes" : "No"), 389 printf(_("SmartRevision=%d, CheckSum=%d, SmartCapability=%d {%s %s}\n"), values->revision, values->checksum, values->smart_capability,
429 p->offline_timeout / 60); 390 values->smart_capability & 1 ? "SaveOnStandBy" : "", values->smart_capability & 2 ? "AutoSave" : "");
430 printf
431 (_("OffLineCapability=%d {%s %s %s}\n"),
432 p->offline_capability,
433 p->offline_capability & 1 ? "Immediate" : "",
434 p->offline_capability & 2 ? "Auto" : "",
435 p->offline_capability & 4 ? "AbortOnCmd" : "SuspendOnCmd");
436 printf
437 (_("SmartRevision=%d, CheckSum=%d, SmartCapability=%d {%s %s}\n"),
438 p->revision,
439 p->checksum,
440 p->smart_capability,
441 p->smart_capability & 1 ? "SaveOnStandBy" : "",
442 p->smart_capability & 2 ? "AutoSave" : "");
443} 391}
444 392
445 393mp_state_enum smart_cmd_simple(int file_descriptor, enum SmartCommand command, uint8_t val0, bool show_error) {
446int smart_cmd_simple (int fd, enum SmartCommand command, __u8 val0, bool show_error) { 394 mp_state_enum result = STATE_UNKNOWN;
447 int e = STATE_UNKNOWN;
448#ifdef __linux__ 395#ifdef __linux__
449 __u8 args[4]; 396 uint8_t args[4] = {
450 args[0] = WIN_SMART; 397 WIN_SMART,
451 args[1] = val0; 398 val0,
452 args[2] = smart_command[command].value; 399 smart_command[command].value,
453 args[3] = 0; 400 0,
454 if (ioctl (fd, HDIO_DRIVE_CMD, &args)) { 401 };
455 e = STATE_CRITICAL; 402
456 if (show_error) 403 if (ioctl(file_descriptor, HDIO_DRIVE_CMD, &args)) {
457 printf (_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror (errno)); 404 result = STATE_CRITICAL;
405 if (show_error) {
406 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
407 }
458 } else { 408 } else {
459 e = STATE_OK; 409 result = STATE_OK;
460 if (show_error) 410 if (show_error) {
461 printf (_("OK - Command sent (%s)\n"), smart_command[command].text); 411 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
412 }
462 } 413 }
463 414
464#endif /* __linux__ */ 415#elif defined __NetBSD__
465#ifdef __NetBSD__
466 struct atareq req; 416 struct atareq req;
467 417
468 memset(&req, 0, sizeof(req)); 418 memset(&req, 0, sizeof(req));
@@ -473,52 +423,52 @@ int smart_cmd_simple (int fd, enum SmartCommand command, __u8 val0, bool show_er
473 req.cylinder = WDSMART_CYL; 423 req.cylinder = WDSMART_CYL;
474 req.sec_count = val0; 424 req.sec_count = val0;
475 425
476 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 426 if (ioctl(file_descriptor, ATAIOCCOMMAND, &req) == 0) {
477 if (req.retsts != ATACMD_OK) 427 if (req.retsts != ATACMD_OK) {
478 errno = ENODEV; 428 errno = ENODEV;
479 if (req.cylinder != WDSMART_CYL) 429 }
430 if (req.cylinder != WDSMART_CYL) {
480 errno = ENODEV; 431 errno = ENODEV;
432 }
481 } 433 }
482 434
483 if (errno != 0) { 435 if (errno != 0) {
484 e = STATE_CRITICAL; 436 result = STATE_CRITICAL;
485 if (show_error) 437 if (show_error) {
486 printf (_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror (errno)); 438 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
439 }
487 } else { 440 } else {
488 e = STATE_OK; 441 result = STATE_OK;
489 if (show_error) 442 if (show_error) {
490 printf (_("OK - Command sent (%s)\n"), smart_command[command].text); 443 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
444 }
491 } 445 }
492 446#else
447# error Not implemented for this OS
493#endif /* __NetBSD__ */ 448#endif /* __NetBSD__ */
494 return e;
495}
496
497 449
450 return result;
451}
498 452
499int 453int smart_read_thresholds(int file_descriptor, smart_thresholds *thresholds) {
500smart_read_thresholds (int fd, thresholds_t * thresholds)
501{
502#ifdef __linux__ 454#ifdef __linux__
503 int e; 455 uint8_t args[4 + 512];
504 __u8 args[4 + 512];
505 args[0] = WIN_SMART; 456 args[0] = WIN_SMART;
506 args[1] = 0; 457 args[1] = 0;
507 args[2] = SMART_READ_THRESHOLDS; 458 args[2] = SMART_READ_THRESHOLDS;
508 args[3] = 1; 459 args[3] = 1;
509 if (ioctl (fd, HDIO_DRIVE_CMD, &args)) { 460 if (ioctl(file_descriptor, HDIO_DRIVE_CMD, &args)) {
510 e = errno; 461 int errno_storage = errno;
511 printf (_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror (errno)); 462 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno));
512 return e; 463 return errno_storage;
513 } 464 }
514 memcpy (thresholds, args + 4, 512); 465 memcpy(thresholds, args + 4, 512);
515#endif /* __linux__ */ 466#elif defined __NetBSD__
516#ifdef __NetBSD__
517 struct atareq req; 467 struct atareq req;
518 unsigned char inbuf[DEV_BSIZE];
519
520 memset(&req, 0, sizeof(req)); 468 memset(&req, 0, sizeof(req));
521 req.timeout = 1000; 469 req.timeout = 1000;
470
471 unsigned char inbuf[DEV_BSIZE];
522 memset(&inbuf, 0, sizeof(inbuf)); 472 memset(&inbuf, 0, sizeof(inbuf));
523 473
524 req.flags = ATACMD_READ; 474 req.flags = ATACMD_READ;
@@ -528,61 +478,63 @@ smart_read_thresholds (int fd, thresholds_t * thresholds)
528 req.datalen = sizeof(inbuf); 478 req.datalen = sizeof(inbuf);
529 req.cylinder = WDSMART_CYL; 479 req.cylinder = WDSMART_CYL;
530 480
531 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 481 if (ioctl(file_descriptor, ATAIOCCOMMAND, &req) == 0) {
532 if (req.retsts != ATACMD_OK) 482 if (req.retsts != ATACMD_OK) {
533 errno = ENODEV; 483 errno = ENODEV;
484 }
534 } 485 }
535 486
536 if (errno != 0) { 487 if (errno != 0) {
537 int e = errno; 488 int errno_storage = errno;
538 printf (_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror (errno)); 489 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno));
539 return e; 490 return errno_storage;
540 } 491 }
541 492
542 (void)memcpy(thresholds, inbuf, 512); 493 (void)memcpy(thresholds, inbuf, 512);
494#else
495# error Not implemented for this OS
543#endif /* __NetBSD__ */ 496#endif /* __NetBSD__ */
497
544 return 0; 498 return 0;
545} 499}
546 500
501void print_help(void) {
502 print_revision(progname, NP_VERSION);
547 503
548void 504 printf("(C) 1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org>\n");
549print_help (void) 505 printf("Plugin implementation - 1999 Robert Dale <rdale@digital-mission.com>\n");
550{ 506 printf(COPYRIGHT, copyright, email);
551 print_revision (progname, NP_VERSION);
552 507
553 printf ("(C) 1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org>\n"); 508 printf(_("This plugin checks a local hard drive with the (Linux specific) SMART interface "
554 printf ("Plugin implementation - 1999 Robert Dale <rdale@digital-mission.com>\n"); 509 "[http://smartlinux.sourceforge.net/smart/index.php]."));
555 printf (COPYRIGHT, copyright, email);
556 510
557 printf (_("This plugin checks a local hard drive with the (Linux specific) SMART interface [http://smartlinux.sourceforge.net/smart/index.php].")); 511 printf("\n\n");
558 512
559 printf ("\n\n"); 513 print_usage();
560 514
561 print_usage (); 515 printf(UT_HELP_VRSN);
516 printf(UT_EXTRA_OPTS);
562 517
563 printf (UT_HELP_VRSN); 518 printf(" %s\n", "-d, --device=DEVICE");
564 printf (UT_EXTRA_OPTS); 519 printf(" %s\n", _("Select device DEVICE"));
520 printf(" %s\n", _("Note: if the device is specified without this option, any further option will"));
521 printf(" %s\n", _("be ignored."));
565 522
566 printf (" %s\n", "-d, --device=DEVICE"); 523 printf(UT_VERBOSE);
567 printf (" %s\n", _("Select device DEVICE"));
568 printf (" %s\n", _("Note: if the device is specified without this option, any further option will"));
569 printf (" %s\n", _("be ignored."));
570 524
571 printf (UT_VERBOSE); 525 printf("\n");
526 printf("%s\n", _("Notes:"));
527 printf(" %s\n", _("The SMART command modes (-i/--immediate, -0/--auto-off and -1/--auto-on) were"));
528 printf(" %s\n", _("broken in an underhand manner and have been disabled. You can use smartctl"));
529 printf(" %s\n", _("instead:"));
530 printf(" %s\n", _("-0/--auto-off: use \"smartctl --offlineauto=off\""));
531 printf(" %s\n", _("-1/--auto-on: use \"smartctl --offlineauto=on\""));
532 printf(" %s\n", _("-i/--immediate: use \"smartctl --test=offline\""));
572 533
573 printf ("\n"); 534 printf(UT_SUPPORT);
574 printf ("%s\n", _("Notes:"));
575 printf (" %s\n", _("The SMART command modes (-i/--immediate, -0/--auto-off and -1/--auto-on) were"));
576 printf (" %s\n", _("broken in an underhand manner and have been disabled. You can use smartctl"));
577 printf (" %s\n", _("instead:"));
578 printf (" %s\n", _("-0/--auto-off: use \"smartctl --offlineauto=off\""));
579 printf (" %s\n", _("-1/--auto-on: use \"smartctl --offlineauto=on\""));
580 printf (" %s\n", _("-i/--immediate: use \"smartctl --test=offline\""));
581
582 printf (UT_SUPPORT);
583} 535}
584 536
585 /* todo : add to the long nanual as example 537/* todo : add to the long nanual as example
586 * 538 *
587 * Run with: check_ide-smart --nagios [-d] <DRIVE> 539 * Run with: check_ide-smart --nagios [-d] <DRIVE>
588 * Where DRIVE is an IDE drive, ie. /dev/hda, /dev/hdb, /dev/hdc 540 * Where DRIVE is an IDE drive, ie. /dev/hda, /dev/hdb, /dev/hdc
@@ -593,10 +545,7 @@ print_help (void)
593 * - Returns -1 not too often 545 * - Returns -1 not too often
594 */ 546 */
595 547
596 548void print_usage(void) {
597void 549 printf("%s\n", _("Usage:"));
598print_usage (void) 550 printf("%s [-d <device>] [-v]", progname);
599{
600 printf ("%s\n", _("Usage:"));
601 printf ("%s [-d <device>] [-v]", progname);
602} 551}
diff --git a/plugins/check_ide_smart.d/config.h b/plugins/check_ide_smart.d/config.h
new file mode 100644
index 00000000..36899abe
--- /dev/null
+++ b/plugins/check_ide_smart.d/config.h
@@ -0,0 +1,15 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6typedef struct {
7 char *device;
8} check_ide_smart_config;
9
10check_ide_smart_config check_ide_smart_init() {
11 check_ide_smart_config tmp = {
12 .device = NULL,
13 };
14 return tmp;
15}
diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c
index 868ffc1e..0e8c5804 100644
--- a/plugins/check_ldap.c
+++ b/plugins/check_ldap.c
@@ -1,517 +1,600 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ldap plugin 3 * Monitoring check_ldap plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2008 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_ldap plugin 10 * This file contains the check_ldap plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 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 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 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 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/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29/* progname may be check_ldaps */ 29/* progname may be check_ldaps */
30char *progname = "check_ldap"; 30#include "output.h"
31const char *copyright = "2000-2008";
32const char *email = "devel@monitoring-plugins.org";
33
34#include "common.h" 31#include "common.h"
35#include "netutils.h" 32#include "netutils.h"
33#include "perfdata.h"
34#include "thresholds.h"
36#include "utils.h" 35#include "utils.h"
36#include "check_ldap.d/config.h"
37 37
38#include "states.h"
38#include <lber.h> 39#include <lber.h>
39#define LDAP_DEPRECATED 1 40#define LDAP_DEPRECATED 1
40#include <ldap.h> 41#include <ldap.h>
41 42
43char *progname = "check_ldap";
44const char *copyright = "2000-2024";
45const char *email = "devel@monitoring-plugins.org";
46
42enum { 47enum {
43 UNDEFINED = 0,
44#ifdef HAVE_LDAP_SET_OPTION
45 DEFAULT_PROTOCOL = 2,
46#endif
47 DEFAULT_PORT = 389 48 DEFAULT_PORT = 389
48}; 49};
49 50
50int process_arguments (int, char **); 51typedef struct {
51int validate_arguments (void); 52 int errorcode;
52void print_help (void); 53 check_ldap_config config;
53void print_usage (void); 54} check_ldap_config_wrapper;
54 55static check_ldap_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
55char ld_defattr[] = "(objectclass=*)"; 56static check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper /*config_wrapper*/);
56char *ld_attr = ld_defattr;
57char *ld_host = NULL;
58char *ld_base = NULL;
59char *ld_passwd = NULL;
60char *ld_binddn = NULL;
61int ld_port = -1;
62#ifdef HAVE_LDAP_SET_OPTION
63int ld_protocol = DEFAULT_PROTOCOL;
64#endif
65#ifndef LDAP_OPT_SUCCESS
66# define LDAP_OPT_SUCCESS LDAP_SUCCESS
67#endif
68double warn_time = UNDEFINED;
69double crit_time = UNDEFINED;
70thresholds *entries_thresholds = NULL;
71struct timeval tv;
72char* warn_entries = NULL;
73char* crit_entries = NULL;
74bool starttls = false;
75bool ssl_on_connect = false;
76bool verbose = false;
77 57
78/* for ldap tls */ 58static void print_help(void);
59void print_usage(void);
79 60
80char *SERVICE = "LDAP"; 61#ifndef LDAP_OPT_SUCCESS
81 62# define LDAP_OPT_SUCCESS LDAP_SUCCESS
82int 63#endif
83main (int argc, char *argv[]) 64static int verbose = 0;
84{
85
86 LDAP *ld;
87 LDAPMessage *result;
88
89 /* should be int result = STATE_UNKNOWN; */
90
91 int status = STATE_UNKNOWN;
92 long microsec;
93 double elapsed_time;
94
95 /* for ldap tls */
96
97 int tls;
98 int version=3;
99
100 int status_entries = STATE_OK;
101 int num_entries = 0;
102 65
103 setlocale (LC_ALL, ""); 66int main(int argc, char *argv[]) {
104 bindtextdomain (PACKAGE, LOCALEDIR); 67 setlocale(LC_ALL, "");
105 textdomain (PACKAGE); 68 bindtextdomain(PACKAGE, LOCALEDIR);
69 textdomain(PACKAGE);
106 70
107 if (strstr(argv[0],"check_ldaps")) { 71 if (strstr(argv[0], "check_ldaps")) {
108 xasprintf (&progname, "check_ldaps"); 72 xasprintf(&progname, "check_ldaps");
109 } 73 }
110 74
111 /* Parse extra opts if any */ 75 /* Parse extra opts if any */
112 argv=np_extra_opts (&argc, argv, progname); 76 argv = np_extra_opts(&argc, argv, progname);
77
78 check_ldap_config_wrapper tmp_config = process_arguments(argc, argv);
79 if (tmp_config.errorcode == ERROR) {
80 usage4(_("Could not parse arguments"));
81 }
113 82
114 if (process_arguments (argc, argv) == ERROR) 83 const check_ldap_config config = tmp_config.config;
115 usage4 (_("Could not parse arguments"));
116 84
117 if (strstr(argv[0],"check_ldaps") && ! starttls && ! ssl_on_connect) 85 if (config.output_format_is_set) {
118 starttls = true; 86 mp_set_format(config.output_format);
87 }
119 88
120 /* initialize alarm signal handling */ 89 /* initialize alarm signal handling */
121 signal (SIGALRM, socket_timeout_alarm_handler); 90 signal(SIGALRM, socket_timeout_alarm_handler);
122 91
123 /* set socket timeout */ 92 /* set socket timeout */
124 alarm (socket_timeout); 93 alarm(socket_timeout);
125 94
126 /* get the start time */ 95 /* get the start time */
127 gettimeofday (&tv, NULL); 96 struct timeval start_time;
97 gettimeofday(&start_time, NULL);
98
99 mp_check overall = mp_check_init();
128 100
101 LDAP *ldap_connection;
129 /* initialize ldap */ 102 /* initialize ldap */
103 {
130#ifdef HAVE_LDAP_INIT 104#ifdef HAVE_LDAP_INIT
131 if (!(ld = ldap_init (ld_host, ld_port))) { 105 mp_subcheck sc_ldap_init = mp_subcheck_init();
132 printf ("Could not connect to the server at port %i\n", ld_port); 106 if (!(ldap_connection = ldap_init(config.ld_host, config.ld_port))) {
133 return STATE_CRITICAL; 107 xasprintf(&sc_ldap_init.output, "could not connect to the server at port %i",
134 } 108 config.ld_port);
109 sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_CRITICAL);
110 mp_add_subcheck_to_check(&overall, sc_ldap_init);
111 mp_exit(overall);
112 } else {
113 xasprintf(&sc_ldap_init.output, "connected to the server at port %i", config.ld_port);
114 sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_OK);
115 mp_add_subcheck_to_check(&overall, sc_ldap_init);
116 }
135#else 117#else
136 if (!(ld = ldap_open (ld_host, ld_port))) { 118 mp_subcheck sc_ldap_init = mp_subcheck_init();
137 if (verbose) 119 if (!(ld = ldap_open(config.ld_host, config.ld_port))) {
138 ldap_perror(ld, "ldap_open"); 120 if (verbose) {
139 printf (_("Could not connect to the server at port %i\n"), ld_port); 121 ldap_perror(ldap_connection, "ldap_open");
140 return STATE_CRITICAL; 122 }
141 } 123 xasprintf(&sc_ldap_init.output, "Could not connect to the server at port %i"), config.ld_port);
124 sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_CRITICAL);
125 mp_add_subcheck_to_check(&overall, sc_ldap_init);
126 mp_exit(overall);
127 } else {
128 xasprintf(&sc_ldap_init.output, "connected to the server at port %i", config.ld_port);
129 sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_OK);
130 mp_add_subcheck_to_check(&overall, sc_ldap_init);
131 }
142#endif /* HAVE_LDAP_INIT */ 132#endif /* HAVE_LDAP_INIT */
133 }
143 134
144#ifdef HAVE_LDAP_SET_OPTION 135#ifdef HAVE_LDAP_SET_OPTION
145 /* set ldap options */ 136 /* set ldap options */
146 if (ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &ld_protocol) != 137 mp_subcheck sc_ldap_set_opts = mp_subcheck_init();
147 LDAP_OPT_SUCCESS ) { 138 if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) !=
148 printf(_("Could not set protocol version %d\n"), ld_protocol); 139 LDAP_OPT_SUCCESS) {
149 return STATE_CRITICAL; 140 xasprintf(&sc_ldap_set_opts.output, "Could not set protocol version %d",
141 config.ld_protocol);
142 sc_ldap_set_opts = mp_set_subcheck_state(sc_ldap_set_opts, STATE_CRITICAL);
143 mp_add_subcheck_to_check(&overall, sc_ldap_set_opts);
144 mp_exit(overall);
145 } else {
146 xasprintf(&sc_ldap_set_opts.output, "set protocol version %d", config.ld_protocol);
147 sc_ldap_set_opts = mp_set_subcheck_state(sc_ldap_set_opts, STATE_OK);
148 mp_add_subcheck_to_check(&overall, sc_ldap_set_opts);
150 } 149 }
151#endif 150#endif
152 151
153 if (ld_port == LDAPS_PORT || ssl_on_connect) { 152 int version = 3;
154 xasprintf (&SERVICE, "LDAPS"); 153 int tls;
154 {
155 if (config.ld_port == LDAPS_PORT || config.ssl_on_connect) {
155#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS) 156#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)
156 /* ldaps: set option tls */ 157 /* ldaps: set option tls */
157 tls = LDAP_OPT_X_TLS_HARD; 158 tls = LDAP_OPT_X_TLS_HARD;
158 159
159 if (ldap_set_option (ld, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) 160 mp_subcheck sc_ldap_tls_init = mp_subcheck_init();
160 { 161 if (ldap_set_option(ldap_connection, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) {
161 if (verbose) 162 if (verbose) {
162 ldap_perror(ld, "ldaps_option"); 163 ldap_perror(ldap_connection, "ldaps_option");
163 printf (_("Could not init TLS at port %i!\n"), ld_port); 164 }
164 return STATE_CRITICAL; 165 xasprintf(&sc_ldap_tls_init.output, "could not init TLS at port %i!",
165 } 166 config.ld_port);
167 sc_ldap_tls_init = mp_set_subcheck_state(sc_ldap_tls_init, STATE_CRITICAL);
168 mp_add_subcheck_to_check(&overall, sc_ldap_tls_init);
169 mp_exit(overall);
170 } else {
171 xasprintf(&sc_ldap_tls_init.output, "initiated TLS at port %i!", config.ld_port);
172 sc_ldap_tls_init = mp_set_subcheck_state(sc_ldap_tls_init, STATE_OK);
173 mp_add_subcheck_to_check(&overall, sc_ldap_tls_init);
174 }
166#else 175#else
167 printf (_("TLS not supported by the libraries!\n")); 176 printf(_("TLS not supported by the libraries!\n"));
168 return STATE_CRITICAL; 177 exit(STATE_CRITICAL);
169#endif /* LDAP_OPT_X_TLS */ 178#endif /* LDAP_OPT_X_TLS */
170 } else if (starttls) { 179 } else if (config.starttls) {
171 xasprintf (&SERVICE, "LDAP-TLS");
172#if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S) 180#if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S)
173 /* ldap with startTLS: set option version */ 181 /* ldap with startTLS: set option version */
174 if (ldap_get_option(ld,LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS ) 182 if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) ==
175 { 183 LDAP_OPT_SUCCESS) {
176 if (version < LDAP_VERSION3) 184 if (version < LDAP_VERSION3) {
177 { 185 version = LDAP_VERSION3;
178 version = LDAP_VERSION3; 186 ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version);
179 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version); 187 }
188 }
189 /* call start_tls */
190 mp_subcheck sc_ldap_starttls = mp_subcheck_init();
191 if (ldap_start_tls_s(ldap_connection, NULL, NULL) != LDAP_SUCCESS) {
192 if (verbose) {
193 ldap_perror(ldap_connection, "ldap_start_tls");
194 }
195 xasprintf(&sc_ldap_starttls.output, "could not init STARTTLS at port %i!",
196 config.ld_port);
197 sc_ldap_starttls = mp_set_subcheck_state(sc_ldap_starttls, STATE_CRITICAL);
198 mp_add_subcheck_to_check(&overall, sc_ldap_starttls);
199 mp_exit(overall);
200 } else {
201 xasprintf(&sc_ldap_starttls.output, "initiated STARTTLS at port %i!",
202 config.ld_port);
203 sc_ldap_starttls = mp_set_subcheck_state(sc_ldap_starttls, STATE_OK);
204 mp_add_subcheck_to_check(&overall, sc_ldap_starttls);
180 } 205 }
181 }
182 /* call start_tls */
183 if (ldap_start_tls_s(ld, NULL, NULL) != LDAP_SUCCESS)
184 {
185 if (verbose)
186 ldap_perror(ld, "ldap_start_tls");
187 printf (_("Could not init startTLS at port %i!\n"), ld_port);
188 return STATE_CRITICAL;
189 }
190#else 206#else
191 printf (_("startTLS not supported by the library, needs LDAPv3!\n")); 207 printf(_("startTLS not supported by the library, needs LDAPv3!\n"));
192 return STATE_CRITICAL; 208 exit(STATE_CRITICAL);
193#endif /* HAVE_LDAP_START_TLS_S */ 209#endif /* HAVE_LDAP_START_TLS_S */
210 }
194 } 211 }
195 212
196 /* bind to the ldap server */ 213 /* bind to the ldap server */
197 if (ldap_bind_s (ld, ld_binddn, ld_passwd, LDAP_AUTH_SIMPLE) != 214 {
198 LDAP_SUCCESS) { 215 mp_subcheck sc_ldap_bind = mp_subcheck_init();
199 if (verbose) 216 int ldap_error =
200 ldap_perror(ld, "ldap_bind"); 217 ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE);
201 printf (_("Could not bind to the LDAP server\n")); 218 if (ldap_error != LDAP_SUCCESS) {
202 return STATE_CRITICAL; 219 if (verbose) {
220 ldap_perror(ldap_connection, "ldap_bind");
221 }
222
223 xasprintf(&sc_ldap_bind.output, "could not bind to the LDAP server: %s",
224 ldap_err2string(ldap_error));
225 sc_ldap_bind = mp_set_subcheck_state(sc_ldap_bind, STATE_CRITICAL);
226 mp_add_subcheck_to_check(&overall, sc_ldap_bind);
227 mp_exit(overall);
228 } else {
229 xasprintf(&sc_ldap_bind.output, "execute bind to the LDAP server");
230 sc_ldap_bind = mp_set_subcheck_state(sc_ldap_bind, STATE_OK);
231 mp_add_subcheck_to_check(&overall, sc_ldap_bind);
232 }
203 } 233 }
204 234
235 LDAPMessage *result;
205 /* do a search of all objectclasses in the base dn */ 236 /* do a search of all objectclasses in the base dn */
206 if (ldap_search_s (ld, ld_base, (crit_entries!=NULL || warn_entries!=NULL) ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_BASE, ld_attr, NULL, 0, &result) 237 {
207 != LDAP_SUCCESS) { 238 mp_subcheck sc_ldap_search = mp_subcheck_init();
208 if (verbose) 239 int ldap_error = ldap_search_s(
209 ldap_perror(ld, "ldap_search"); 240 ldap_connection, config.ld_base,
210 printf (_("Could not search/find objectclasses in %s\n"), ld_base); 241 (config.entries_thresholds.warning_is_set || config.entries_thresholds.critical_is_set)
211 return STATE_CRITICAL; 242 ? LDAP_SCOPE_SUBTREE
212 } else if (crit_entries!=NULL || warn_entries!=NULL) { 243 : LDAP_SCOPE_BASE,
213 num_entries = ldap_count_entries(ld, result); 244 config.ld_attr, NULL, 0, &result);
245
246 if (ldap_error != LDAP_SUCCESS) {
247 if (verbose) {
248 ldap_perror(ldap_connection, "ldap_search");
249 }
250 xasprintf(&sc_ldap_search.output, "could not search/find objectclasses in %s: %s",
251 config.ld_base, ldap_err2string(ldap_error));
252 sc_ldap_search = mp_set_subcheck_state(sc_ldap_search, STATE_CRITICAL);
253 mp_add_subcheck_to_check(&overall, sc_ldap_search);
254 mp_exit(overall);
255 } else {
256 xasprintf(&sc_ldap_search.output, "search/find objectclasses in %s", config.ld_base);
257 sc_ldap_search = mp_set_subcheck_state(sc_ldap_search, STATE_OK);
258 mp_add_subcheck_to_check(&overall, sc_ldap_search);
259 }
260 }
261
262 int num_entries = ldap_count_entries(ldap_connection, result);
263 if (verbose) {
264 printf("entries found: %d\n", num_entries);
214 } 265 }
215 266
216 /* unbind from the ldap server */ 267 /* unbind from the ldap server */
217 ldap_unbind (ld); 268 ldap_unbind(ldap_connection);
218 269
219 /* reset the alarm handler */ 270 /* reset the alarm handler */
220 alarm (0); 271 alarm(0);
221 272
222 /* calculate the elapsed time and compare to thresholds */ 273 /* calculate the elapsed time and compare to thresholds */
274 long microsec = deltime(start_time);
275 double elapsed_time = (double)microsec / 1.0e6;
276 mp_perfdata pd_connection_time = perfdata_init();
277 pd_connection_time.label = "time";
278 pd_connection_time.value = mp_create_pd_value(elapsed_time);
279 pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.connection_time_threshold);
280
281 mp_subcheck sc_connection_time = mp_subcheck_init();
282 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time);
283
284 mp_state_enum connection_time_state = mp_get_pd_status(pd_connection_time);
285 sc_connection_time = mp_set_subcheck_state(sc_connection_time, connection_time_state);
286
287 if (connection_time_state == STATE_OK) {
288 xasprintf(&sc_connection_time.output, "connection time %.3fs is within thresholds",
289 elapsed_time);
290 } else {
291 xasprintf(&sc_connection_time.output, "connection time %.3fs is violating thresholds",
292 elapsed_time);
293 }
223 294
224 microsec = deltime (tv); 295 mp_add_subcheck_to_check(&overall, sc_connection_time);
225 elapsed_time = (double)microsec / 1.0e6;
226 296
227 if (crit_time!=UNDEFINED && elapsed_time>crit_time) 297 mp_perfdata pd_num_entries = perfdata_init();
228 status = STATE_CRITICAL; 298 pd_num_entries.label = "entries";
229 else if (warn_time!=UNDEFINED && elapsed_time>warn_time) 299 pd_num_entries.value = mp_create_pd_value(num_entries);
230 status = STATE_WARNING; 300 pd_num_entries = mp_pd_set_thresholds(pd_num_entries, config.entries_thresholds);
231 else
232 status = STATE_OK;
233 301
234 if(entries_thresholds != NULL) { 302 mp_subcheck sc_num_entries = mp_subcheck_init();
235 if (verbose) { 303 mp_add_perfdata_to_subcheck(&sc_num_entries, pd_num_entries);
236 printf ("entries found: %d\n", num_entries); 304 xasprintf(&sc_num_entries.output, "found %d entries", num_entries);
237 print_thresholds("entry thresholds", entries_thresholds); 305 sc_num_entries = mp_set_subcheck_state(sc_num_entries, mp_get_pd_status(pd_num_entries));
238 }
239 status_entries = get_status(num_entries, entries_thresholds);
240 if (status_entries == STATE_CRITICAL) {
241 status = STATE_CRITICAL;
242 } else if (status != STATE_CRITICAL) {
243 status = status_entries;
244 }
245 }
246 306
247 /* print out the result */ 307 mp_add_subcheck_to_check(&overall, sc_num_entries);
248 if (crit_entries!=NULL || warn_entries!=NULL) {
249 printf (_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"),
250 state_text (status),
251 num_entries,
252 elapsed_time,
253 fperfdata ("time", elapsed_time, "s",
254 (int)warn_time, warn_time,
255 (int)crit_time, crit_time,
256 true, 0, false, 0),
257 sperfdata ("entries", (double)num_entries, "",
258 warn_entries,
259 crit_entries,
260 true, 0.0, false, 0.0));
261 } else {
262 printf (_("LDAP %s - %.3f seconds response time|%s\n"),
263 state_text (status),
264 elapsed_time,
265 fperfdata ("time", elapsed_time, "s",
266 (int)warn_time, warn_time,
267 (int)crit_time, crit_time,
268 true, 0, false, 0));
269 }
270 308
271 return status; 309 mp_exit(overall);
272} 310}
273 311
274/* process command-line arguments */ 312/* process command-line arguments */
275int 313check_ldap_config_wrapper process_arguments(int argc, char **argv) {
276process_arguments (int argc, char **argv) 314 enum {
277{ 315 output_format_index = CHAR_MAX + 1,
278 int c; 316 };
279 317
280 int option = 0;
281 /* initialize the long option struct */ 318 /* initialize the long option struct */
282 static struct option longopts[] = { 319 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
283 {"help", no_argument, 0, 'h'}, 320 {"version", no_argument, 0, 'V'},
284 {"version", no_argument, 0, 'V'}, 321 {"timeout", required_argument, 0, 't'},
285 {"timeout", required_argument, 0, 't'}, 322 {"hostname", required_argument, 0, 'H'},
286 {"hostname", required_argument, 0, 'H'}, 323 {"base", required_argument, 0, 'b'},
287 {"base", required_argument, 0, 'b'}, 324 {"attr", required_argument, 0, 'a'},
288 {"attr", required_argument, 0, 'a'}, 325 {"bind", required_argument, 0, 'D'},
289 {"bind", required_argument, 0, 'D'}, 326 {"pass", required_argument, 0, 'P'},
290 {"pass", required_argument, 0, 'P'},
291#ifdef HAVE_LDAP_SET_OPTION 327#ifdef HAVE_LDAP_SET_OPTION
292 {"ver2", no_argument, 0, '2'}, 328 {"ver2", no_argument, 0, '2'},
293 {"ver3", no_argument, 0, '3'}, 329 {"ver3", no_argument, 0, '3'},
294#endif 330#endif
295 {"starttls", no_argument, 0, 'T'}, 331 {"starttls", no_argument, 0, 'T'},
296 {"ssl", no_argument, 0, 'S'}, 332 {"ssl", no_argument, 0, 'S'},
297 {"use-ipv4", no_argument, 0, '4'}, 333 {"use-ipv4", no_argument, 0, '4'},
298 {"use-ipv6", no_argument, 0, '6'}, 334 {"use-ipv6", no_argument, 0, '6'},
299 {"port", required_argument, 0, 'p'}, 335 {"port", required_argument, 0, 'p'},
300 {"warn", required_argument, 0, 'w'}, 336 {"warn", required_argument, 0, 'w'},
301 {"crit", required_argument, 0, 'c'}, 337 {"crit", required_argument, 0, 'c'},
302 {"warn-entries", required_argument, 0, 'W'}, 338 {"warn-entries", required_argument, 0, 'W'},
303 {"crit-entries", required_argument, 0, 'C'}, 339 {"crit-entries", required_argument, 0, 'C'},
304 {"verbose", no_argument, 0, 'v'}, 340 {"verbose", no_argument, 0, 'v'},
305 {0, 0, 0, 0} 341 {"output-format", required_argument, 0, output_format_index},
342 {0, 0, 0, 0}};
343
344 check_ldap_config_wrapper result = {
345 .errorcode = OK,
346 .config = check_ldap_config_init(),
306 }; 347 };
307 348
308 if (argc < 2) 349 if (argc < 2) {
309 return ERROR; 350 result.errorcode = ERROR;
351 return result;
352 }
310 353
311 for (c = 1; c < argc; c++) { 354 for (int index = 1; index < argc; index++) {
312 if (strcmp ("-to", argv[c]) == 0) 355 if (strcmp("-to", argv[index]) == 0) {
313 strcpy (argv[c], "-t"); 356 strcpy(argv[index], "-t");
357 }
314 } 358 }
315 359
360 int option = 0;
316 while (true) { 361 while (true) {
317 c = getopt_long (argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option); 362 int option_index =
363 getopt_long(argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option);
318 364
319 if (c == -1 || c == EOF) 365 if (CHECK_EOF(option_index)) {
320 break; 366 break;
367 }
321 368
322 switch (c) { 369 switch (option_index) {
323 case 'h': /* help */ 370 case 'h': /* help */
324 print_help (); 371 print_help();
325 exit (STATE_UNKNOWN); 372 exit(STATE_UNKNOWN);
326 case 'V': /* version */ 373 case 'V': /* version */
327 print_revision (progname, NP_VERSION); 374 print_revision(progname, NP_VERSION);
328 exit (STATE_UNKNOWN); 375 exit(STATE_UNKNOWN);
329 case 't': /* timeout period */ 376 case 't': /* timeout period */
330 if (!is_intnonneg (optarg)) 377 if (!is_intnonneg(optarg)) {
331 usage2 (_("Timeout interval must be a positive integer"), optarg); 378 usage2(_("Timeout interval must be a positive integer"), optarg);
332 else 379 } else {
333 socket_timeout = atoi (optarg); 380 socket_timeout = atoi(optarg);
381 }
334 break; 382 break;
335 case 'H': 383 case 'H':
336 ld_host = optarg; 384 result.config.ld_host = optarg;
337 break; 385 break;
338 case 'b': 386 case 'b':
339 ld_base = optarg; 387 result.config.ld_base = optarg;
340 break; 388 break;
341 case 'p': 389 case 'p':
342 ld_port = atoi (optarg); 390 result.config.ld_port = atoi(optarg);
343 break; 391 break;
344 case 'a': 392 case 'a':
345 ld_attr = optarg; 393 result.config.ld_attr = optarg;
346 break; 394 break;
347 case 'D': 395 case 'D':
348 ld_binddn = optarg; 396 result.config.ld_binddn = optarg;
349 break; 397 break;
350 case 'P': 398 case 'P':
351 ld_passwd = optarg; 399 result.config.ld_passwd = optarg;
352 break;
353 case 'w':
354 warn_time = strtod (optarg, NULL);
355 break;
356 case 'c':
357 crit_time = strtod (optarg, NULL);
358 break;
359 case 'W':
360 warn_entries = optarg;
361 break;
362 case 'C':
363 crit_entries = optarg;
364 break; 400 break;
401 case 'w': {
402 mp_range_parsed tmp = mp_parse_range_string(optarg);
403 if (tmp.error != MP_PARSING_SUCCESS) {
404 die(STATE_UNKNOWN, "failed to parse warning connection time threshold");
405 }
406 result.config.connection_time_threshold =
407 mp_thresholds_set_warn(result.config.connection_time_threshold, tmp.range);
408 } break;
409 case 'c': {
410 mp_range_parsed tmp = mp_parse_range_string(optarg);
411 if (tmp.error != MP_PARSING_SUCCESS) {
412 die(STATE_UNKNOWN, "failed to parse critical connection time threshold");
413 }
414 result.config.connection_time_threshold =
415 mp_thresholds_set_crit(result.config.connection_time_threshold, tmp.range);
416 } break;
417 case 'W': {
418 mp_range_parsed tmp = mp_parse_range_string(optarg);
419 if (tmp.error != MP_PARSING_SUCCESS) {
420 die(STATE_UNKNOWN, "failed to parse number of entries warning threshold");
421 }
422 result.config.entries_thresholds =
423 mp_thresholds_set_warn(result.config.entries_thresholds, tmp.range);
424 } break;
425 case 'C': {
426 mp_range_parsed tmp = mp_parse_range_string(optarg);
427 if (tmp.error != MP_PARSING_SUCCESS) {
428 die(STATE_UNKNOWN, "failed to parse number of entries critical threshold");
429 }
430 result.config.entries_thresholds =
431 mp_thresholds_set_crit(result.config.entries_thresholds, tmp.range);
432 } break;
365#ifdef HAVE_LDAP_SET_OPTION 433#ifdef HAVE_LDAP_SET_OPTION
366 case '2': 434 case '2':
367 ld_protocol = 2; 435 result.config.ld_protocol = 2;
368 break; 436 break;
369 case '3': 437 case '3':
370 ld_protocol = 3; 438 result.config.ld_protocol = 3;
371 break; 439 break;
372#endif 440#endif // HAVE_LDAP_SET_OPTION
373 case '4': 441 case '4':
374 address_family = AF_INET; 442 address_family = AF_INET;
375 break; 443 break;
376 case 'v': 444 case 'v':
377 verbose = true; 445 verbose++;
378 break; 446 break;
379 case 'T': 447 case 'T':
380 if (! ssl_on_connect) 448 if (!result.config.ssl_on_connect) {
381 starttls = true; 449 result.config.starttls = true;
382 else 450 } else {
383 usage_va(_("%s cannot be combined with %s"), "-T/--starttls", "-S/--ssl"); 451 usage_va(_("%s cannot be combined with %s"), "-T/--starttls", "-S/--ssl");
452 }
384 break; 453 break;
385 case 'S': 454 case 'S':
386 if (! starttls) { 455 if (!result.config.starttls) {
387 ssl_on_connect = true; 456 result.config.ssl_on_connect = true;
388 if (ld_port == -1) 457 if (result.config.ld_port == -1) {
389 ld_port = LDAPS_PORT; 458 result.config.ld_port = LDAPS_PORT;
390 } else 459 }
460 } else {
391 usage_va(_("%s cannot be combined with %s"), "-S/--ssl", "-T/--starttls"); 461 usage_va(_("%s cannot be combined with %s"), "-S/--ssl", "-T/--starttls");
462 }
392 break; 463 break;
393 case '6': 464 case '6':
394#ifdef USE_IPV6
395 address_family = AF_INET6; 465 address_family = AF_INET6;
396#else
397 usage (_("IPv6 support not available\n"));
398#endif
399 break; 466 break;
467 case output_format_index: {
468 parsed_output_format parser = mp_parse_output_format(optarg);
469 if (!parser.parsing_success) {
470 // TODO List all available formats here, maybe add anothoer usage function
471 printf("Invalid output format: %s\n", optarg);
472 exit(STATE_UNKNOWN);
473 }
474
475 result.config.output_format_is_set = true;
476 result.config.output_format = parser.output_format;
477 break;
478 }
400 default: 479 default:
401 usage5 (); 480 usage5();
402 } 481 }
403 } 482 }
404 483
405 c = optind; 484 int index = optind;
406 if (ld_host == NULL && is_host(argv[c])) 485 if ((result.config.ld_host == NULL) && is_host(argv[index])) {
407 ld_host = strdup (argv[c++]); 486 result.config.ld_host = strdup(argv[index++]);
487 }
408 488
409 if (ld_base == NULL && argv[c]) 489 if ((result.config.ld_base == NULL) && argv[index]) {
410 ld_base = strdup (argv[c++]); 490 result.config.ld_base = strdup(argv[index++]);
491 }
411 492
412 if (ld_port == -1) 493 if (result.config.ld_port == -1) {
413 ld_port = DEFAULT_PORT; 494 result.config.ld_port = DEFAULT_PORT;
495 }
414 496
415 return validate_arguments (); 497 if (strstr(argv[0], "check_ldaps") && !result.config.starttls &&
416} 498 !result.config.ssl_on_connect) {
499 result.config.starttls = true;
500 }
417 501
502 return validate_arguments(result);
503}
418 504
419int 505check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper config_wrapper) {
420validate_arguments () 506 if (config_wrapper.config.ld_host == NULL || strlen(config_wrapper.config.ld_host) == 0) {
421{ 507 usage4(_("Please specify the host name\n"));
422 if (ld_host==NULL || strlen(ld_host)==0) 508 }
423 usage4 (_("Please specify the host name\n"));
424 509
425 if (ld_base==NULL) 510 if (config_wrapper.config.ld_base == NULL) {
426 usage4 (_("Please specify the LDAP base\n")); 511 usage4(_("Please specify the LDAP base\n"));
512 }
427 513
428 if (crit_entries!=NULL || warn_entries!=NULL) { 514 if (config_wrapper.config.ld_passwd == NULL) {
429 set_thresholds(&entries_thresholds, 515 config_wrapper.config.ld_passwd = getenv("LDAP_PASSWORD");
430 warn_entries, crit_entries);
431 } 516 }
432 if (ld_passwd==NULL)
433 ld_passwd = getenv("LDAP_PASSWORD");
434 517
435 return OK; 518 return config_wrapper;
436} 519}
437 520
438 521void print_help(void) {
439void
440print_help (void)
441{
442 char *myport; 522 char *myport;
443 xasprintf (&myport, "%d", DEFAULT_PORT); 523 xasprintf(&myport, "%d", DEFAULT_PORT);
444 524
445 print_revision (progname, NP_VERSION); 525 print_revision(progname, NP_VERSION);
446 526
447 printf ("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n"); 527 printf("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n");
448 printf (COPYRIGHT, copyright, email); 528 printf(COPYRIGHT, copyright, email);
449 529
450 printf ("\n\n"); 530 printf("\n\n");
451 531
452 print_usage (); 532 print_usage();
453 533
454 printf (UT_HELP_VRSN); 534 printf(UT_HELP_VRSN);
455 printf (UT_EXTRA_OPTS); 535 printf(UT_EXTRA_OPTS);
456 536
457 printf (UT_HOST_PORT, 'p', myport); 537 printf(UT_HOST_PORT, 'p', myport);
458 538
459 printf (UT_IPv46); 539 printf(UT_IPv46);
460 540
461 printf (" %s\n", "-a [--attr]"); 541 printf(" %s\n", "-a [--attr]");
462 printf (" %s\n", _("ldap attribute to search (default: \"(objectclass=*)\"")); 542 printf(" %s\n", _("ldap attribute to search (default: \"(objectclass=*)\""));
463 printf (" %s\n", "-b [--base]"); 543 printf(" %s\n", "-b [--base]");
464 printf (" %s\n", _("ldap base (eg. ou=my unit, o=my org, c=at")); 544 printf(" %s\n", _("ldap base (eg. ou=my unit, o=my org, c=at"));
465 printf (" %s\n", "-D [--bind]"); 545 printf(" %s\n", "-D [--bind]");
466 printf (" %s\n", _("ldap bind DN (if required)")); 546 printf(" %s\n", _("ldap bind DN (if required)"));
467 printf (" %s\n", "-P [--pass]"); 547 printf(" %s\n", "-P [--pass]");
468 printf (" %s\n", _("ldap password (if required, or set the password through environment variable 'LDAP_PASSWORD')")); 548 printf(" %s\n", _("ldap password (if required, or set the password through environment "
469 printf (" %s\n", "-T [--starttls]"); 549 "variable 'LDAP_PASSWORD')"));
470 printf (" %s\n", _("use starttls mechanism introduced in protocol version 3")); 550 printf(" %s\n", "-T [--starttls]");
471 printf (" %s\n", "-S [--ssl]"); 551 printf(" %s\n", _("use starttls mechanism introduced in protocol version 3"));
472 printf (" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"), LDAPS_PORT); 552 printf(" %s\n", "-S [--ssl]");
553 printf(" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"),
554 LDAPS_PORT);
473 555
474#ifdef HAVE_LDAP_SET_OPTION 556#ifdef HAVE_LDAP_SET_OPTION
475 printf (" %s\n", "-2 [--ver2]"); 557 printf(" %s\n", "-2 [--ver2]");
476 printf (" %s\n", _("use ldap protocol version 2")); 558 printf(" %s\n", _("use ldap protocol version 2"));
477 printf (" %s\n", "-3 [--ver3]"); 559 printf(" %s\n", "-3 [--ver3]");
478 printf (" %s\n", _("use ldap protocol version 3")); 560 printf(" %s\n", _("use ldap protocol version 3"));
479 printf (" (%s %d)\n", _("default protocol version:"), DEFAULT_PROTOCOL); 561 printf(" (%s %d)\n", _("default protocol version:"), DEFAULT_PROTOCOL);
480#endif 562#endif
481 563
482 printf (UT_WARN_CRIT); 564 printf(UT_WARN_CRIT);
483 565
484 printf (" %s\n", "-W [--warn-entries]"); 566 printf(" %s\n", "-W [--warn-entries]");
485 printf (" %s\n", _("Number of found entries to result in warning status")); 567 printf(" %s\n", _("Number of found entries to result in warning status"));
486 printf (" %s\n", "-C [--crit-entries]"); 568 printf(" %s\n", "-C [--crit-entries]");
487 printf (" %s\n", _("Number of found entries to result in critical status")); 569 printf(" %s\n", _("Number of found entries to result in critical status"));
488 570
489 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 571 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
490 572
491 printf (UT_VERBOSE); 573 printf(UT_VERBOSE);
574 printf(UT_OUTPUT_FORMAT);
492 575
493 printf ("\n"); 576 printf("\n");
494 printf ("%s\n", _("Notes:")); 577 printf("%s\n", _("Notes:"));
495 printf (" %s\n", _("If this plugin is called via 'check_ldaps', method 'STARTTLS' will be")); 578 printf(" %s\n", _("If this plugin is called via 'check_ldaps', method 'STARTTLS' will be"));
496 printf (_(" implied (using default port %i) unless --port=636 is specified. In that case\n"), DEFAULT_PORT); 579 printf(_(" implied (using default port %i) unless --port=636 is specified. In that case\n"),
497 printf (" %s\n", _("'SSL on connect' will be used no matter how the plugin was called.")); 580 DEFAULT_PORT);
498 printf (" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' or '--ssl' flags")); 581 printf(" %s\n", _("'SSL on connect' will be used no matter how the plugin was called."));
499 printf (" %s\n", _("to define the behaviour explicitly instead.")); 582 printf(" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' "
500 printf (" %s\n", _("The parameters --warn-entries and --crit-entries are optional.")); 583 "or '--ssl' flags"));
584 printf(" %s\n", _("to define the behaviour explicitly instead."));
585 printf(" %s\n", _("The parameters --warn-entries and --crit-entries are optional."));
501 586
502 printf (UT_SUPPORT); 587 printf(UT_SUPPORT);
503} 588}
504 589
505void 590void print_usage(void) {
506print_usage (void) 591 printf("%s\n", _("Usage:"));
507{ 592 printf(" %s -H <host> -b <base_dn> [-p <port>] [-a <attr>] [-D <binddn>]", progname);
508 printf ("%s\n", _("Usage:")); 593 printf("\n [-P <password>] [-w <warn_time>] [-c <crit_time>] [-t timeout]%s\n",
509 printf (" %s -H <host> -b <base_dn> [-p <port>] [-a <attr>] [-D <binddn>]",progname);
510 printf ("\n [-P <password>] [-w <warn_time>] [-c <crit_time>] [-t timeout]%s\n",
511#ifdef HAVE_LDAP_SET_OPTION 594#ifdef HAVE_LDAP_SET_OPTION
512 "\n [-2|-3] [-4|-6]" 595 "\n [-2|-3] [-4|-6]"
513#else 596#else
514 "" 597 ""
515#endif 598#endif
516 ); 599 );
517} 600}
diff --git a/plugins/check_ldap.d/config.h b/plugins/check_ldap.d/config.h
new file mode 100644
index 00000000..50191725
--- /dev/null
+++ b/plugins/check_ldap.d/config.h
@@ -0,0 +1,56 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
6#include <stddef.h>
7
8static char ld_defattr[] = "(objectclass=*)";
9
10enum {
11#ifdef HAVE_LDAP_SET_OPTION
12 DEFAULT_PROTOCOL = 2,
13#endif
14};
15
16typedef struct {
17 char *ld_host;
18 char *ld_base;
19 char *ld_passwd;
20 char *ld_binddn;
21 char *ld_attr;
22 int ld_port;
23 bool starttls;
24 bool ssl_on_connect;
25#ifdef HAVE_LDAP_SET_OPTION
26 int ld_protocol;
27#endif
28
29 mp_thresholds entries_thresholds;
30 mp_thresholds connection_time_threshold;
31
32 bool output_format_is_set;
33 mp_output_format output_format;
34} check_ldap_config;
35
36check_ldap_config check_ldap_config_init() {
37 check_ldap_config tmp = {
38 .ld_host = NULL,
39 .ld_base = NULL,
40 .ld_passwd = NULL,
41 .ld_binddn = NULL,
42 .ld_attr = ld_defattr,
43 .ld_port = -1,
44 .starttls = false,
45 .ssl_on_connect = false,
46#ifdef HAVE_LDAP_SET_OPTION
47 .ld_protocol = DEFAULT_PROTOCOL,
48#endif
49
50 .entries_thresholds = mp_thresholds_init(),
51 .connection_time_threshold = mp_thresholds_init(),
52
53 .output_format_is_set = false,
54 };
55 return tmp;
56}
diff --git a/plugins/check_load.c b/plugins/check_load.c
index 1431d130..60fa646f 100644
--- a/plugins/check_load.c
+++ b/plugins/check_load.c
@@ -1,350 +1,430 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_load plugin 3 * Monitoring check_load plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2007 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_load plugin 10 * This file contains the check_load plugin
11* 11 *
12* This plugin tests the current system load average. 12 * This plugin tests the current system load average.
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_load"; 31const char *progname = "check_load";
32const char *copyright = "1999-2022"; 32const char *copyright = "1999-2022";
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 <string.h>
36#include "./runcmd.h" 37#include "./runcmd.h"
37#include "./utils.h" 38#include "./utils.h"
38#include "./popen.h" 39#include "./popen.h"
40#include "../lib/states.h"
41#include "../lib/output.h"
42#include "../lib/perfdata.h"
43#include "../lib/thresholds.h"
44#include "check_load.d/config.h"
39 45
40#include <string.h> 46// getloadavg comes from gnulib
41 47#include "../gl/stdlib.h"
42#ifdef HAVE_SYS_LOADAVG_H
43#include <sys/loadavg.h>
44#endif
45 48
46/* needed for compilation under NetBSD, as suggested by Andy Doran */ 49/* needed for compilation under NetBSD, as suggested by Andy Doran */
47#ifndef LOADAVG_1MIN 50#ifndef LOADAVG_1MIN
48#define LOADAVG_1MIN 0 51# define LOADAVG_1MIN 0
49#define LOADAVG_5MIN 1 52# define LOADAVG_5MIN 1
50#define LOADAVG_15MIN 2 53# define LOADAVG_15MIN 2
51#endif /* !defined LOADAVG_1MIN */ 54#endif /* !defined LOADAVG_1MIN */
52 55
56typedef struct {
57 int errorcode;
58 check_load_config config;
59} check_load_config_wrapper;
60static check_load_config_wrapper process_arguments(int argc, char **argv);
61
62void print_help(void);
63void print_usage(void);
64typedef struct {
65 int errorcode;
66 char **top_processes;
67} top_processes_result;
68static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show);
69
70typedef struct {
71 mp_range load[3];
72} parsed_thresholds;
73static parsed_thresholds get_threshold(char *arg) {
74 size_t index;
75 char *str = arg;
76 char *tmp_pointer;
77 bool valid = false;
78
79 parsed_thresholds result = {
80 .load =
81 {
82 mp_range_init(),
83 mp_range_init(),
84 mp_range_init(),
85 },
86 };
53 87
54static int process_arguments (int argc, char **argv); 88 size_t arg_length = strlen(arg);
55static int validate_arguments (void); 89 for (index = 0; index < 3; index++) {
56void print_help (void); 90 double tmp = strtod(str, &tmp_pointer);
57void print_usage (void); 91 if (tmp_pointer == str) {
58static int print_top_consuming_processes(); 92 break;
59 93 }
60static int n_procs_to_show = 0;
61
62/* strictly for pretty-print usage in loops */
63static const int nums[3] = { 1, 5, 15 };
64
65/* provide some fairly sane defaults */
66double wload[3] = { 0.0, 0.0, 0.0 };
67double cload[3] = { 0.0, 0.0, 0.0 };
68#define la1 la[0]
69#define la5 la[1]
70#define la15 la[2]
71
72char *status_line;
73bool take_into_account_cpus = false;
74
75static void
76get_threshold(char *arg, double *th)
77{
78 size_t i, n;
79 int valid = 0;
80 char *str = arg, *p;
81 94
82 n = strlen(arg); 95 result.load[index] = mp_range_set_end(result.load[index], mp_create_pd_value(tmp));
83 for(i = 0; i < 3; i++) {
84 th[i] = strtod(str, &p);
85 if(p == str) break;
86 96
87 valid = 1; 97 valid = true;
88 str = p + 1; 98 str = tmp_pointer + 1;
89 if(n <= (size_t)(str - arg)) break; 99 if (arg_length <= (size_t)(str - arg)) {
100 break;
101 }
90 } 102 }
91 103
92 /* empty argument or non-floatish, so warn about it and die */ 104 /* empty argument or non-floatish, so warn about it and die */
93 if(!i && !valid) usage (_("Warning threshold must be float or float triplet!\n")); 105 if (!index && !valid) {
106 usage(_("Warning threshold must be float or float triplet!\n"));
107 }
94 108
95 if(i != 2) { 109 if (index != 2) {
96 /* one or more numbers were given, so fill array with last 110 /* one or more numbers were given, so fill array with last
97 * we got (most likely to NOT produce the least expected result) */ 111 * we got (most likely to NOT produce the least expected result) */
98 for(n = i; n < 3; n++) th[n] = th[i]; 112 for (size_t tmp_index = index; tmp_index < 3; tmp_index++) {
113 result.load[tmp_index] = result.load[index];
114 }
99 } 115 }
116 return result;
100} 117}
101 118
102 119int main(int argc, char **argv) {
103int 120 setlocale(LC_ALL, "");
104main (int argc, char **argv) 121 bindtextdomain(PACKAGE, LOCALEDIR);
105{ 122 textdomain(PACKAGE);
106 int result = -1;
107 int i;
108 long numcpus;
109
110 double la[3] = { 0.0, 0.0, 0.0 }; /* NetBSD complains about uninitialized arrays */
111#ifndef HAVE_GETLOADAVG
112 char input_buffer[MAX_INPUT_BUFFER];
113#endif
114
115 setlocale (LC_ALL, "");
116 bindtextdomain (PACKAGE, LOCALEDIR);
117 textdomain (PACKAGE);
118 setlocale(LC_NUMERIC, "POSIX"); 123 setlocale(LC_NUMERIC, "POSIX");
119 124
120 /* Parse extra opts if any */ 125 /* Parse extra opts if any */
121 argv = np_extra_opts (&argc, argv, progname); 126 argv = np_extra_opts(&argc, argv, progname);
122 127
123 if (process_arguments (argc, argv) == ERROR) 128 check_load_config_wrapper tmp_config = process_arguments(argc, argv);
124 usage4 (_("Could not parse arguments")); 129 if (tmp_config.errorcode == ERROR) {
125 130 usage4(_("Could not parse arguments"));
126#ifdef HAVE_GETLOADAVG
127 result = getloadavg (la, 3);
128 if (result != 3)
129 return STATE_UNKNOWN;
130#else
131 child_process = spopen (PATH_TO_UPTIME);
132 if (child_process == NULL) {
133 printf (_("Error opening %s\n"), PATH_TO_UPTIME);
134 return STATE_UNKNOWN;
135 }
136 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
137 if (child_stderr == NULL) {
138 printf (_("Could not open stderr for %s\n"), PATH_TO_UPTIME);
139 }
140 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
141 if(strstr(input_buffer, "load average:")) {
142 sscanf (input_buffer, "%*[^l]load average: %lf, %lf, %lf", &la1, &la5, &la15);
143 }
144 else if(strstr(input_buffer, "load averages:")) {
145 sscanf (input_buffer, "%*[^l]load averages: %lf, %lf, %lf", &la1, &la5, &la15);
146 }
147 else {
148 printf (_("could not parse load from uptime %s: %d\n"), PATH_TO_UPTIME, result);
149 return STATE_UNKNOWN;
150 }
151
152 result = spclose (child_process);
153 if (result) {
154 printf (_("Error code %d returned in %s\n"), result, PATH_TO_UPTIME);
155 return STATE_UNKNOWN;
156 }
157#endif
158
159 if ((la[0] < 0.0) || (la[1] < 0.0) || (la[2] < 0.0)) {
160#ifdef HAVE_GETLOADAVG
161 printf (_("Error in getloadavg()\n"));
162#else
163 printf (_("Error processing %s\n"), PATH_TO_UPTIME);
164#endif
165 return STATE_UNKNOWN;
166 } 131 }
167 132
168 /* we got this far, so assume OK until we've measured */ 133 const check_load_config config = tmp_config.config;
169 result = STATE_OK; 134
135 double load_values[3] = {0, 0, 0};
170 136
171 xasprintf(&status_line, _("load average: %.2f, %.2f, %.2f"), la1, la5, la15); 137 // this should be getloadavg from gnulib, should work everywhereâ„¢
172 xasprintf(&status_line, ("total %s"), status_line); 138 int error = getloadavg(load_values, 3);
139 if (error != 3) {
140 die(STATE_UNKNOWN, _("Failed to retrieve load values"));
141 }
173 142
143 mp_check overall = mp_check_init();
144 if (config.output_format_set) {
145 mp_set_format(config.output_format);
146 }
174 147
175 double scaled_la[3] = { 0.0, 0.0, 0.0 };
176 bool is_using_scaled_load_values = false; 148 bool is_using_scaled_load_values = false;
177 149 long numcpus;
178 if (take_into_account_cpus == true && (numcpus = GET_NUMBER_OF_CPUS()) > 0) { 150 if (config.take_into_account_cpus && ((numcpus = GET_NUMBER_OF_CPUS()) > 0)) {
179 is_using_scaled_load_values = true; 151 is_using_scaled_load_values = true;
180 152
181 scaled_la[0] = la[0] / numcpus; 153 double scaled_la[3] = {
182 scaled_la[1] = la[1] / numcpus; 154 load_values[0] / numcpus,
183 scaled_la[2] = la[2] / numcpus; 155 load_values[1] / numcpus,
156 load_values[2] / numcpus,
157 };
158
159 mp_subcheck scaled_load_sc = mp_subcheck_init();
160 scaled_load_sc = mp_set_subcheck_default_state(scaled_load_sc, STATE_OK);
161 scaled_load_sc.output = "Scaled Load (divided by number of CPUs";
162
163 mp_perfdata pd_scaled_load1 = perfdata_init();
164 pd_scaled_load1.label = "scaled_load1";
165 pd_scaled_load1 = mp_set_pd_value(pd_scaled_load1, scaled_la[0]);
166 pd_scaled_load1 = mp_pd_set_thresholds(pd_scaled_load1, config.th_load[0]);
167
168 mp_subcheck scaled_load_sc1 = mp_subcheck_init();
169 scaled_load_sc1 = mp_set_subcheck_state(scaled_load_sc1, mp_get_pd_status(pd_scaled_load1));
170 mp_add_perfdata_to_subcheck(&scaled_load_sc1, pd_scaled_load1);
171 xasprintf(&scaled_load_sc1.output, "1 Minute: %s",
172 pd_value_to_string(pd_scaled_load1.value));
173 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc1);
174
175 mp_perfdata pd_scaled_load5 = perfdata_init();
176 pd_scaled_load5.label = "scaled_load5";
177 pd_scaled_load5 = mp_set_pd_value(pd_scaled_load5, scaled_la[1]);
178 pd_scaled_load5 = mp_pd_set_thresholds(pd_scaled_load5, config.th_load[1]);
179
180 mp_subcheck scaled_load_sc5 = mp_subcheck_init();
181 scaled_load_sc5 = mp_set_subcheck_state(scaled_load_sc5, mp_get_pd_status(pd_scaled_load5));
182 mp_add_perfdata_to_subcheck(&scaled_load_sc5, pd_scaled_load5);
183 xasprintf(&scaled_load_sc5.output, "5 Minutes: %s",
184 pd_value_to_string(pd_scaled_load5.value));
185 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc5);
186
187 mp_perfdata pd_scaled_load15 = perfdata_init();
188 pd_scaled_load15.label = "scaled_load15";
189 pd_scaled_load15 = mp_set_pd_value(pd_scaled_load15, scaled_la[2]);
190 pd_scaled_load15 = mp_pd_set_thresholds(pd_scaled_load15, config.th_load[2]);
191
192 mp_subcheck scaled_load_sc15 = mp_subcheck_init();
193 scaled_load_sc15 =
194 mp_set_subcheck_state(scaled_load_sc15, mp_get_pd_status(pd_scaled_load15));
195 mp_add_perfdata_to_subcheck(&scaled_load_sc15, pd_scaled_load15);
196 xasprintf(&scaled_load_sc15.output, "15 Minutes: %s",
197 pd_value_to_string(pd_scaled_load15.value));
198 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc15);
199
200 mp_add_subcheck_to_check(&overall, scaled_load_sc);
201 }
202
203 mp_subcheck load_sc = mp_subcheck_init();
204 load_sc = mp_set_subcheck_default_state(load_sc, STATE_OK);
205 load_sc.output = "Total Load";
184 206
185 char *tmp = NULL; 207 mp_perfdata pd_load1 = perfdata_init();
186 xasprintf(&tmp, _("load average: %.2f, %.2f, %.2f"), scaled_la[0], scaled_la[1], scaled_la[2]); 208 pd_load1.label = "load1";
187 xasprintf(&status_line, "scaled %s - %s", tmp, status_line); 209 pd_load1 = mp_set_pd_value(pd_load1, load_values[0]);
210 if (!is_using_scaled_load_values) {
211 pd_load1 = mp_pd_set_thresholds(pd_load1, config.th_load[0]);
188 } 212 }
189 213
190 for(i = 0; i < 3; i++) { 214 mp_subcheck load_sc1 = mp_subcheck_init();
191 if (is_using_scaled_load_values) { 215 load_sc1 = mp_set_subcheck_state(load_sc1, mp_get_pd_status(pd_load1));
192 if(scaled_la[i] > cload[i]) { 216 mp_add_perfdata_to_subcheck(&load_sc1, pd_load1);
193 result = STATE_CRITICAL; 217 xasprintf(&load_sc1.output, "1 Minute: %s", pd_value_to_string(pd_load1.value));
194 break; 218 mp_add_subcheck_to_subcheck(&load_sc, load_sc1);
195 } 219
196 else if(scaled_la[i] > wload[i]) result = STATE_WARNING; 220 mp_perfdata pd_load5 = perfdata_init();
197 } else { 221 pd_load5.label = "load5";
198 if(la[i] > cload[i]) { 222 pd_load5 = mp_set_pd_value(pd_load5, load_values[1]);
199 result = STATE_CRITICAL; 223 if (!is_using_scaled_load_values) {
200 break; 224 pd_load5 = mp_pd_set_thresholds(pd_load5, config.th_load[1]);
201 }
202 else if(la[i] > wload[i]) result = STATE_WARNING;
203 }
204 } 225 }
205 226
206 printf("LOAD %s - %s|", state_text(result), status_line); 227 mp_subcheck load_sc5 = mp_subcheck_init();
207 for(i = 0; i < 3; i++) { 228 load_sc5 = mp_set_subcheck_state(load_sc5, mp_get_pd_status(pd_load5));
208 if (is_using_scaled_load_values) { 229 mp_add_perfdata_to_subcheck(&load_sc5, pd_load5);
209 printf("load%d=%.3f;;;0; ", nums[i], la[i]); 230 xasprintf(&load_sc5.output, "5 Minutes: %s", pd_value_to_string(pd_load5.value));
210 printf("scaled_load%d=%.3f;%.3f;%.3f;0; ", nums[i], scaled_la[i], wload[i], cload[i]); 231 mp_add_subcheck_to_subcheck(&load_sc, load_sc5);
211 } else { 232
212 printf("load%d=%.3f;%.3f;%.3f;0; ", nums[i], la[i], wload[i], cload[i]); 233 mp_perfdata pd_load15 = perfdata_init();
213 } 234 pd_load15.label = "load15";
235 pd_load15 = mp_set_pd_value(pd_load15, load_values[2]);
236 if (!is_using_scaled_load_values) {
237 pd_load15 = mp_pd_set_thresholds(pd_load15, config.th_load[2]);
214 } 238 }
215 239
216 putchar('\n'); 240 mp_subcheck load_sc15 = mp_subcheck_init();
217 if (n_procs_to_show > 0) { 241 load_sc15 = mp_set_subcheck_state(load_sc15, mp_get_pd_status(pd_load15));
218 print_top_consuming_processes(); 242 mp_add_perfdata_to_subcheck(&load_sc15, pd_load15);
243 xasprintf(&load_sc15.output, "15 Minutes: %s", pd_value_to_string(pd_load15.value));
244 mp_add_subcheck_to_subcheck(&load_sc, load_sc15);
245
246 mp_add_subcheck_to_check(&overall, load_sc);
247
248 if (config.n_procs_to_show > 0) {
249 mp_subcheck top_proc_sc = mp_subcheck_init();
250 top_proc_sc = mp_set_subcheck_state(top_proc_sc, STATE_OK);
251 top_processes_result top_proc = print_top_consuming_processes(config.n_procs_to_show);
252 xasprintf(&top_proc_sc.output, "Top %lu CPU time consuming processes",
253 config.n_procs_to_show);
254
255 if (top_proc.errorcode == OK) {
256 for (unsigned long i = 0; i < config.n_procs_to_show; i++) {
257 xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output,
258 top_proc.top_processes[i]);
259 }
260 }
261
262 mp_add_subcheck_to_check(&overall, top_proc_sc);
219 } 263 }
220 return result;
221}
222 264
265 mp_exit(overall);
266}
223 267
224/* process command-line arguments */ 268/* process command-line arguments */
225static int 269static check_load_config_wrapper process_arguments(int argc, char **argv) {
226process_arguments (int argc, char **argv) 270
227{ 271 enum {
228 int c = 0; 272 output_format_index = CHAR_MAX + 1,
229
230 int option = 0;
231 static struct option longopts[] = {
232 {"warning", required_argument, 0, 'w'},
233 {"critical", required_argument, 0, 'c'},
234 {"percpu", no_argument, 0, 'r'},
235 {"version", no_argument, 0, 'V'},
236 {"help", no_argument, 0, 'h'},
237 {"procs-to-show", required_argument, 0, 'n'},
238 {0, 0, 0, 0}
239 }; 273 };
240 274
241 if (argc < 2) 275 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
242 return ERROR; 276 {"critical", required_argument, 0, 'c'},
277 {"percpu", no_argument, 0, 'r'},
278 {"version", no_argument, 0, 'V'},
279 {"help", no_argument, 0, 'h'},
280 {"procs-to-show", required_argument, 0, 'n'},
281 {"output-format", required_argument, 0, output_format_index},
282 {0, 0, 0, 0}};
283
284 check_load_config_wrapper result = {
285 .errorcode = OK,
286 .config = check_load_config_init(),
287 };
243 288
244 while (1) { 289 if (argc < 2) {
245 c = getopt_long (argc, argv, "Vhrc:w:n:", longopts, &option); 290 result.errorcode = ERROR;
291 return result;
292 }
246 293
247 if (c == -1 || c == EOF) 294 while (true) {
248 break; 295 int option = 0;
296 int option_index = getopt_long(argc, argv, "Vhrc:w:n:", longopts, &option);
249 297
250 switch (c) { 298 if (CHECK_EOF(option_index)) {
251 case 'w': /* warning time threshold */
252 get_threshold(optarg, wload);
253 break; 299 break;
254 case 'c': /* critical time threshold */ 300 }
255 get_threshold(optarg, cload); 301
302 switch (option_index) {
303 case output_format_index: {
304 parsed_output_format parser = mp_parse_output_format(optarg);
305 if (!parser.parsing_success) {
306 printf("Invalid output format: %s\n", optarg);
307 exit(STATE_UNKNOWN);
308 }
309
310 result.config.output_format_set = true;
311 result.config.output_format = parser.output_format;
256 break; 312 break;
313 }
314 case 'w': /* warning time threshold */ {
315 parsed_thresholds warning_range = get_threshold(optarg);
316 result.config.th_load[0].warning = warning_range.load[0];
317 result.config.th_load[0].warning_is_set = true;
318
319 result.config.th_load[1].warning = warning_range.load[1];
320 result.config.th_load[1].warning_is_set = true;
321
322 result.config.th_load[2].warning = warning_range.load[2];
323 result.config.th_load[2].warning_is_set = true;
324 } break;
325 case 'c': /* critical time threshold */ {
326 parsed_thresholds critical_range = get_threshold(optarg);
327 result.config.th_load[0].critical = critical_range.load[0];
328 result.config.th_load[0].critical_is_set = true;
329
330 result.config.th_load[1].critical = critical_range.load[1];
331 result.config.th_load[1].critical_is_set = true;
332
333 result.config.th_load[2].critical = critical_range.load[2];
334 result.config.th_load[2].critical_is_set = true;
335 } break;
257 case 'r': /* Divide load average by number of CPUs */ 336 case 'r': /* Divide load average by number of CPUs */
258 take_into_account_cpus = true; 337 result.config.take_into_account_cpus = true;
259 break; 338 break;
260 case 'V': /* version */ 339 case 'V': /* version */
261 print_revision (progname, NP_VERSION); 340 print_revision(progname, NP_VERSION);
262 exit (STATE_UNKNOWN); 341 exit(STATE_UNKNOWN);
263 case 'h': /* help */ 342 case 'h': /* help */
264 print_help (); 343 print_help();
265 exit (STATE_UNKNOWN); 344 exit(STATE_UNKNOWN);
266 case 'n': 345 case 'n':
267 n_procs_to_show = atoi(optarg); 346 result.config.n_procs_to_show = (unsigned long)atol(optarg);
268 break; 347 break;
269 case '?': /* help */ 348 case '?': /* help */
270 usage5 (); 349 usage5();
271 } 350 }
272 } 351 }
273 352
274 c = optind; 353 int index = optind;
275 if (c == argc) 354 if (index == argc) {
276 return validate_arguments (); 355 return result;
356 }
277 357
278 /* handle the case if both arguments are missing, 358 /* handle the case if both arguments are missing,
279 * but not if only one is given without -c or -w flag */ 359 * but not if only one is given without -c or -w flag */
280 if(c - argc == 2) { 360 if (index - argc == 2) {
281 get_threshold(argv[c++], wload); 361 parsed_thresholds warning_range = get_threshold(argv[index++]);
282 get_threshold(argv[c++], cload); 362 result.config.th_load[0].warning = warning_range.load[0];
283 } 363 result.config.th_load[0].warning_is_set = true;
284 else if(c - argc == 1) { 364
285 get_threshold(argv[c++], cload); 365 result.config.th_load[1].warning = warning_range.load[1];
286 } 366 result.config.th_load[1].warning_is_set = true;
287 367
288 return validate_arguments (); 368 result.config.th_load[2].warning = warning_range.load[2];
289} 369 result.config.th_load[2].warning_is_set = true;
290 370 parsed_thresholds critical_range = get_threshold(argv[index++]);
291 371 result.config.th_load[0].critical = critical_range.load[0];
292static int 372 result.config.th_load[0].critical_is_set = true;
293validate_arguments (void) 373
294{ 374 result.config.th_load[1].critical = critical_range.load[1];
295 int i = 0; 375 result.config.th_load[1].critical_is_set = true;
296 376
297 /* match cload first, as it will give the most friendly error message 377 result.config.th_load[2].critical = critical_range.load[2];
298 * if user hasn't given the -c switch properly */ 378 result.config.th_load[2].critical_is_set = true;
299 for(i = 0; i < 3; i++) { 379 } else if (index - argc == 1) {
300 if(cload[i] < 0) 380 parsed_thresholds critical_range = get_threshold(argv[index++]);
301 die (STATE_UNKNOWN, _("Critical threshold for %d-minute load average is not specified\n"), nums[i]); 381 result.config.th_load[0].critical = critical_range.load[0];
302 if(wload[i] < 0) 382 result.config.th_load[0].critical_is_set = true;
303 die (STATE_UNKNOWN, _("Warning threshold for %d-minute load average is not specified\n"), nums[i]); 383
304 if(wload[i] > cload[i]) 384 result.config.th_load[1].critical = critical_range.load[1];
305 die (STATE_UNKNOWN, _("Parameter inconsistency: %d-minute \"warning load\" is greater than \"critical load\"\n"), nums[i]); 385 result.config.th_load[1].critical_is_set = true;
386
387 result.config.th_load[2].critical = critical_range.load[2];
388 result.config.th_load[2].critical_is_set = true;
306 } 389 }
307 390
308 return OK; 391 return result;
309} 392}
310 393
394void print_help(void) {
395 print_revision(progname, NP_VERSION);
311 396
312void 397 printf("Copyright (c) 1999 Felipe Gustavo de Almeida <galmeida@linux.ime.usp.br>\n");
313print_help (void) 398 printf(COPYRIGHT, copyright, email);
314{
315 print_revision (progname, NP_VERSION);
316 399
317 printf ("Copyright (c) 1999 Felipe Gustavo de Almeida <galmeida@linux.ime.usp.br>\n"); 400 printf(_("This plugin tests the current system load average."));
318 printf (COPYRIGHT, copyright, email);
319 401
320 printf (_("This plugin tests the current system load average.")); 402 printf("\n\n");
321 403
322 printf ("\n\n"); 404 print_usage();
323 405
324 print_usage (); 406 printf(UT_HELP_VRSN);
407 printf(UT_EXTRA_OPTS);
325 408
326 printf (UT_HELP_VRSN); 409 printf(" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15");
327 printf (UT_EXTRA_OPTS); 410 printf(" %s\n", _("Exit with WARNING status if load average exceeds WLOADn"));
411 printf(" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15");
412 printf(" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn"));
413 printf(" %s\n", _("the load average format is the same used by \"uptime\" and \"w\""));
414 printf(" %s\n", "-r, --percpu");
415 printf(" %s\n", _("Divide the load averages by the number of CPUs (when possible)"));
416 printf(" %s\n", "-n, --procs-to-show=NUMBER_OF_PROCS");
417 printf(" %s\n", _("Number of processes to show when printing the top consuming processes."));
418 printf(" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0"));
328 419
329 printf (" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15"); 420 printf(UT_OUTPUT_FORMAT);
330 printf (" %s\n", _("Exit with WARNING status if load average exceeds WLOADn")); 421 printf(UT_SUPPORT);
331 printf (" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15");
332 printf (" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn"));
333 printf (" %s\n", _("the load average format is the same used by \"uptime\" and \"w\""));
334 printf (" %s\n", "-r, --percpu");
335 printf (" %s\n", _("Divide the load averages by the number of CPUs (when possible)"));
336 printf (" %s\n", "-n, --procs-to-show=NUMBER_OF_PROCS");
337 printf (" %s\n", _("Number of processes to show when printing the top consuming processes."));
338 printf (" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0"));
339
340 printf (UT_SUPPORT);
341} 422}
342 423
343void 424void print_usage(void) {
344print_usage (void) 425 printf("%s\n", _("Usage:"));
345{ 426 printf("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n",
346 printf ("%s\n", _("Usage:")); 427 progname);
347 printf ("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", progname);
348} 428}
349 429
350#ifdef PS_USES_PROCPCPU 430#ifdef PS_USES_PROCPCPU
@@ -356,36 +436,52 @@ int cmpstringp(const void *p1, const void *p2) {
356 int procrss = 0; 436 int procrss = 0;
357 float procpcpu = 0; 437 float procpcpu = 0;
358 char procstat[8]; 438 char procstat[8];
359#ifdef PS_USES_PROCETIME 439# ifdef PS_USES_PROCETIME
360 char procetime[MAX_INPUT_BUFFER]; 440 char procetime[MAX_INPUT_BUFFER];
361#endif /* PS_USES_PROCETIME */ 441# endif /* PS_USES_PROCETIME */
362 char procprog[MAX_INPUT_BUFFER]; 442 char procprog[MAX_INPUT_BUFFER];
363 int pos; 443 int pos;
364 sscanf (* (char * const *) p1, PS_FORMAT, PS_VARLIST); 444 sscanf(*(char *const *)p1, PS_FORMAT, PS_VARLIST);
365 float procpcpu1 = procpcpu; 445 float procpcpu1 = procpcpu;
366 sscanf (* (char * const *) p2, PS_FORMAT, PS_VARLIST); 446 sscanf(*(char *const *)p2, PS_FORMAT, PS_VARLIST);
367 return procpcpu1 < procpcpu; 447 return procpcpu1 < procpcpu;
368} 448}
369#endif /* PS_USES_PROCPCPU */ 449#endif /* PS_USES_PROCPCPU */
370 450
371static int print_top_consuming_processes() { 451static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show) {
372 int i = 0; 452 top_processes_result result = {
373 struct output chld_out, chld_err; 453 .errorcode = OK,
374 if(np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0){ 454 };
455 output chld_out;
456 output chld_err;
457 if (np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0) {
375 fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND); 458 fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND);
376 return STATE_UNKNOWN; 459 result.errorcode = ERROR;
460 return result;
377 } 461 }
462
378 if (chld_out.lines < 2) { 463 if (chld_out.lines < 2) {
379 fprintf(stderr, _("some error occurred getting procs list.\n")); 464 fprintf(stderr, _("some error occurred getting procs list.\n"));
380 return STATE_UNKNOWN; 465 result.errorcode = ERROR;
466 return result;
381 } 467 }
468
382#ifdef PS_USES_PROCPCPU 469#ifdef PS_USES_PROCPCPU
383 qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char*), cmpstringp); 470 qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char *), cmpstringp);
384#endif /* PS_USES_PROCPCPU */ 471#endif /* PS_USES_PROCPCPU */
385 int lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) 472 unsigned long lines_to_show =
386 ? (int)chld_out.lines : n_procs_to_show + 1; 473 chld_out.lines < (size_t)(n_procs_to_show + 1) ? chld_out.lines : n_procs_to_show + 1;
387 for (i = 0; i < lines_to_show; i += 1) { 474
388 printf("%s\n", chld_out.line[i]); 475 result.top_processes = calloc(lines_to_show, sizeof(char *));
476 if (result.top_processes == NULL) {
477 // Failed allocation
478 result.errorcode = ERROR;
479 return result;
389 } 480 }
390 return OK; 481
482 for (unsigned long i = 0; i < lines_to_show; i += 1) {
483 xasprintf(&result.top_processes[i], "%s", chld_out.line[i]);
484 }
485
486 return result;
391} 487}
diff --git a/plugins/check_load.d/config.h b/plugins/check_load.d/config.h
new file mode 100644
index 00000000..fd735455
--- /dev/null
+++ b/plugins/check_load.d/config.h
@@ -0,0 +1,30 @@
1#pragma once
2
3#include "output.h"
4#include "thresholds.h"
5typedef struct {
6 mp_thresholds th_load[3];
7
8 bool take_into_account_cpus;
9 unsigned long n_procs_to_show;
10
11 mp_output_format output_format;
12 bool output_format_set;
13} check_load_config;
14
15check_load_config check_load_config_init() {
16 check_load_config tmp = {
17 .th_load =
18 {
19 mp_thresholds_init(),
20 mp_thresholds_init(),
21 mp_thresholds_init(),
22 },
23
24 .take_into_account_cpus = false,
25 .n_procs_to_show = 0,
26
27 .output_format_set = false,
28 };
29 return tmp;
30}
diff --git a/plugins/check_mrtg.c b/plugins/check_mrtg.c
index 826b77e9..bb38fcc5 100644
--- a/plugins/check_mrtg.c
+++ b/plugins/check_mrtg.c
@@ -1,385 +1,451 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_mrtg plugin 3 * Monitoring check_mrtg plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 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_mrtg plugin 10 * This file contains the check_mrtg plugin
11* 11 *
12* This plugin will check either the average or maximum value of one of the 12 * This plugin will check either the average or maximum value of one of the
13* two variables recorded in an MRTG log file. 13 * two variables recorded in an MRTG log file.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31
32#include "common.h"
33#include "output.h"
34#include "perfdata.h"
35#include "states.h"
36#include "thresholds.h"
37#include "utils.h"
38#include "check_mrtg.d/config.h"
31 39
32const char *progname = "check_mrtg"; 40const char *progname = "check_mrtg";
33const char *copyright = "1999-2007"; 41const char *copyright = "1999-2024";
34const char *email = "devel@monitoring-plugins.org"; 42const char *email = "devel@monitoring-plugins.org";
35 43
36#include "common.h" 44typedef struct {
37#include "utils.h" 45 int errorcode;
46 check_mrtg_config config;
47} check_mrtg_config_wrapper;
48static check_mrtg_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
49static check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper /*config_wrapper*/);
38 50
39int process_arguments (int, char **); 51static void print_help(void);
40int validate_arguments (void); 52void print_usage(void);
41void print_help (void);
42void print_usage (void);
43
44char *log_file = NULL;
45int expire_minutes = 0;
46bool use_average = true;
47int variable_number = -1;
48unsigned long value_warning_threshold = 0L;
49unsigned long value_critical_threshold = 0L;
50char *label;
51char *units;
52
53int
54main (int argc, char **argv)
55{
56 int result = STATE_OK;
57 FILE *fp;
58 int line;
59 char input_buffer[MAX_INPUT_BUFFER];
60 char *temp_buffer;
61 time_t current_time;
62 time_t timestamp = 0L;
63 unsigned long average_value_rate = 0L;
64 unsigned long maximum_value_rate = 0L;
65 unsigned long rate = 0L;
66 53
67 setlocale (LC_ALL, ""); 54int main(int argc, char **argv) {
68 bindtextdomain (PACKAGE, LOCALEDIR); 55 setlocale(LC_ALL, "");
69 textdomain (PACKAGE); 56 bindtextdomain(PACKAGE, LOCALEDIR);
57 textdomain(PACKAGE);
70 58
71 /* Parse extra opts if any */ 59 /* Parse extra opts if any */
72 argv=np_extra_opts (&argc, argv, progname); 60 argv = np_extra_opts(&argc, argv, progname);
61
62 check_mrtg_config_wrapper tmp_config = process_arguments(argc, argv);
63 if (tmp_config.errorcode == ERROR) {
64 usage4(_("Could not parse arguments\n"));
65 }
73 66
74 if (process_arguments (argc, argv) == ERROR) 67 const check_mrtg_config config = tmp_config.config;
75 usage4 (_("Could not parse arguments\n"));
76 68
77 /* open the MRTG log file for reading */ 69 if (config.output_format_is_set) {
78 fp = fopen (log_file, "r"); 70 mp_set_format(config.output_format);
79 if (fp == NULL) {
80 printf (_("Unable to open MRTG log file\n"));
81 return STATE_UNKNOWN;
82 } 71 }
83 72
84 line = 0; 73 mp_check overall = mp_check_init();
85 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) {
86 74
75 /* open the MRTG log file for reading */
76 mp_subcheck sc_open_mrtg_log_file = mp_subcheck_init();
77 FILE *mtrg_log_file = fopen(config.log_file, "r");
78 if (mtrg_log_file == NULL) {
79 xasprintf(&sc_open_mrtg_log_file.output, "unable to open MRTG log file");
80 sc_open_mrtg_log_file = mp_set_subcheck_state(sc_open_mrtg_log_file, STATE_UNKNOWN);
81 mp_add_subcheck_to_check(&overall, sc_open_mrtg_log_file);
82 mp_exit(overall);
83 } else {
84 xasprintf(&sc_open_mrtg_log_file.output, "opened MRTG log file");
85 sc_open_mrtg_log_file = mp_set_subcheck_state(sc_open_mrtg_log_file, STATE_OK);
86 mp_add_subcheck_to_check(&overall, sc_open_mrtg_log_file);
87 }
88
89 time_t timestamp = 0;
90 unsigned long average_value_rate = 0;
91 unsigned long maximum_value_rate = 0;
92 char input_buffer[MAX_INPUT_BUFFER];
93 int line = 0;
94 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mtrg_log_file)) {
87 line++; 95 line++;
88 96
89 /* skip the first line of the log file */ 97 /* skip the first line of the log file */
90 if (line == 1) 98 if (line == 1) {
91 continue; 99 continue;
100 }
92 101
93 /* break out of read loop if we've passed the number of entries we want to read */ 102 /* break out of read loop if we've passed the number of entries we want to read */
94 if (line > 2) 103 if (line > 2) {
95 break; 104 break;
105 }
96 106
97 /* grab the timestamp */ 107 /* grab the timestamp */
98 temp_buffer = strtok (input_buffer, " "); 108 char *temp_buffer = strtok(input_buffer, " ");
99 timestamp = strtoul (temp_buffer, NULL, 10); 109 timestamp = strtoul(temp_buffer, NULL, 10);
100 110
101 /* grab the average value 1 rate */ 111 /* grab the average value 1 rate */
102 temp_buffer = strtok (NULL, " "); 112 temp_buffer = strtok(NULL, " ");
103 if (variable_number == 1) 113 if (config.variable_number == 1) {
104 average_value_rate = strtoul (temp_buffer, NULL, 10); 114 average_value_rate = strtoul(temp_buffer, NULL, 10);
115 }
105 116
106 /* grab the average value 2 rate */ 117 /* grab the average value 2 rate */
107 temp_buffer = strtok (NULL, " "); 118 temp_buffer = strtok(NULL, " ");
108 if (variable_number == 2) 119 if (config.variable_number == 2) {
109 average_value_rate = strtoul (temp_buffer, NULL, 10); 120 average_value_rate = strtoul(temp_buffer, NULL, 10);
121 }
110 122
111 /* grab the maximum value 1 rate */ 123 /* grab the maximum value 1 rate */
112 temp_buffer = strtok (NULL, " "); 124 temp_buffer = strtok(NULL, " ");
113 if (variable_number == 1) 125 if (config.variable_number == 1) {
114 maximum_value_rate = strtoul (temp_buffer, NULL, 10); 126 maximum_value_rate = strtoul(temp_buffer, NULL, 10);
127 }
115 128
116 /* grab the maximum value 2 rate */ 129 /* grab the maximum value 2 rate */
117 temp_buffer = strtok (NULL, " "); 130 temp_buffer = strtok(NULL, " ");
118 if (variable_number == 2) 131 if (config.variable_number == 2) {
119 maximum_value_rate = strtoul (temp_buffer, NULL, 10); 132 maximum_value_rate = strtoul(temp_buffer, NULL, 10);
133 }
120 } 134 }
121 135
122 /* close the log file */ 136 /* close the log file */
123 fclose (fp); 137 fclose(mtrg_log_file);
124 138
125 /* if we couldn't read enough data, return an unknown error */ 139 /* if we couldn't read enough data, return an unknown error */
140 mp_subcheck sc_process_mrtg_log_file = mp_subcheck_init();
126 if (line <= 2) { 141 if (line <= 2) {
127 printf (_("Unable to process MRTG log file\n")); 142 xasprintf(&sc_process_mrtg_log_file.output, "unable to process MRTG log file");
128 return STATE_UNKNOWN; 143 sc_process_mrtg_log_file = mp_set_subcheck_state(sc_process_mrtg_log_file, STATE_UNKNOWN);
144 mp_exit(overall);
145 } else {
146 xasprintf(&sc_process_mrtg_log_file.output, "processed MRTG log file");
147 sc_process_mrtg_log_file = mp_set_subcheck_state(sc_process_mrtg_log_file, STATE_OK);
148 mp_add_subcheck_to_check(&overall, sc_process_mrtg_log_file);
129 } 149 }
130 150
131 /* make sure the MRTG data isn't too old */ 151 /* make sure the MRTG data isn't too old */
132 time (&current_time); 152 time_t current_time;
133 if (expire_minutes > 0 153 time(&current_time);
134 && (current_time - timestamp) > (expire_minutes * 60)) { 154 mp_subcheck sc_data_expired = mp_subcheck_init();
135 printf (_("MRTG data has expired (%d minutes old)\n"), 155 if (config.expire_minutes > 0 && (current_time - timestamp) > (config.expire_minutes * 60)) {
136 (int) ((current_time - timestamp) / 60)); 156 xasprintf(&sc_data_expired.output, "MRTG data has expired (%d minutes old)",
137 return STATE_WARNING; 157 (int)((current_time - timestamp) / 60));
158 sc_data_expired = mp_set_subcheck_state(sc_data_expired, STATE_WARNING);
159 mp_add_subcheck_to_check(&overall, sc_data_expired);
160 mp_exit(overall);
161 } else {
162 xasprintf(&sc_data_expired.output, "MRTG data should be valid (%d minutes old)",
163 (int)((current_time - timestamp) / 60));
164 sc_data_expired = mp_set_subcheck_state(sc_data_expired, STATE_OK);
165 mp_add_subcheck_to_check(&overall, sc_data_expired);
138 } 166 }
139 167
168 unsigned long rate = 0L;
140 /* else check the incoming/outgoing rates */ 169 /* else check the incoming/outgoing rates */
141 if (use_average) 170 if (config.use_average) {
142 rate = average_value_rate; 171 rate = average_value_rate;
143 else 172 } else {
144 rate = maximum_value_rate; 173 rate = maximum_value_rate;
174 }
145 175
146 if (rate > value_critical_threshold) 176 mp_subcheck sc_values = mp_subcheck_init();
147 result = STATE_CRITICAL; 177 mp_perfdata pd_value = perfdata_init();
148 else if (rate > value_warning_threshold) 178 pd_value = mp_set_pd_value(pd_value, rate);
149 result = STATE_WARNING; 179 pd_value.label = config.label;
150 180 pd_value = mp_pd_set_thresholds(pd_value, config.values_threshold);
151 printf("%s. %s = %lu %s|%s\n",
152 (use_average) ? _("Avg") : _("Max"),
153 label, rate, units,
154 perfdata(label, (long) rate, units,
155 (int) value_warning_threshold, (long) value_warning_threshold,
156 (int) value_critical_threshold, (long) value_critical_threshold,
157 0, 0, 0, 0));
158 181
159 return result; 182 sc_values = mp_set_subcheck_state(sc_values, mp_get_pd_status(pd_value));
160} 183 xasprintf(&sc_values.output, "%s. %s = %lu %s", (config.use_average) ? _("Avg") : _("Max"),
184 config.label, rate, config.units);
161 185
186 mp_add_subcheck_to_check(&overall, sc_values);
162 187
188 mp_exit(overall);
189}
163 190
164/* process command-line arguments */ 191/* process command-line arguments */
165int 192check_mrtg_config_wrapper process_arguments(int argc, char **argv) {
166process_arguments (int argc, char **argv) 193 enum {
167{ 194 output_format_index,
168 int c; 195 };
169 196
170 int option = 0; 197 static struct option longopts[] = {{"logfile", required_argument, 0, 'F'},
171 static struct option longopts[] = { 198 {"expires", required_argument, 0, 'e'},
172 {"logfile", required_argument, 0, 'F'}, 199 {"aggregation", required_argument, 0, 'a'},
173 {"expires", required_argument, 0, 'e'}, 200 {"variable", required_argument, 0, 'v'},
174 {"aggregation", required_argument, 0, 'a'}, 201 {"critical", required_argument, 0, 'c'},
175 {"variable", required_argument, 0, 'v'}, 202 {"warning", required_argument, 0, 'w'},
176 {"critical", required_argument, 0, 'c'}, 203 {"label", required_argument, 0, 'l'},
177 {"warning", required_argument, 0, 'w'}, 204 {"units", required_argument, 0, 'u'},
178 {"label", required_argument, 0, 'l'}, 205 {"variable", required_argument, 0, 'v'},
179 {"units", required_argument, 0, 'u'}, 206 {"version", no_argument, 0, 'V'},
180 {"variable", required_argument, 0, 'v'}, 207 {"help", no_argument, 0, 'h'},
181 {"version", no_argument, 0, 'V'}, 208 {"output-format", required_argument, 0, output_format_index},
182 {"help", no_argument, 0, 'h'}, 209 {0, 0, 0, 0}};
183 {0, 0, 0, 0} 210
211 check_mrtg_config_wrapper result = {
212 .errorcode = OK,
213 .config = check_mrtg_config_init(),
184 }; 214 };
185 215
186 if (argc < 2) 216 if (argc < 2) {
187 return ERROR; 217 result.errorcode = ERROR;
218 return result;
219 }
188 220
189 for (c = 1; c < argc; c++) { 221 for (int i = 1; i < argc; i++) {
190 if (strcmp ("-to", argv[c]) == 0) 222 if (strcmp("-to", argv[i]) == 0) {
191 strcpy (argv[c], "-t"); 223 strcpy(argv[i], "-t");
192 else if (strcmp ("-wt", argv[c]) == 0) 224 } else if (strcmp("-wt", argv[i]) == 0) {
193 strcpy (argv[c], "-w"); 225 strcpy(argv[i], "-w");
194 else if (strcmp ("-ct", argv[c]) == 0) 226 } else if (strcmp("-ct", argv[i]) == 0) {
195 strcpy (argv[c], "-c"); 227 strcpy(argv[i], "-c");
228 }
196 } 229 }
197 230
231 int option_char;
232 int option = 0;
198 while (1) { 233 while (1) {
199 c = getopt_long (argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, 234 option_char = getopt_long(argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, &option);
200 &option);
201 235
202 if (c == -1 || c == EOF) 236 if (option_char == -1 || option_char == EOF) {
203 break; 237 break;
238 }
204 239
205 switch (c) { 240 switch (option_char) {
206 case 'F': /* input file */ 241 case 'F': /* input file */
207 log_file = optarg; 242 result.config.log_file = optarg;
208 break; 243 break;
209 case 'e': /* ups name */ 244 case 'e': /* ups name */
210 expire_minutes = atoi (optarg); 245 result.config.expire_minutes = atoi(optarg);
211 break; 246 break;
212 case 'a': /* port */ 247 case 'a': /* port */
213 if (!strcmp (optarg, "MAX")) 248 result.config.use_average = (bool)(strcmp(optarg, "MAX"));
214 use_average = false;
215 else
216 use_average = true;
217 break; 249 break;
218 case 'v': 250 case 'v':
219 variable_number = atoi (optarg); 251 result.config.variable_number = atoi(optarg);
220 if (variable_number < 1 || variable_number > 2) 252 if (result.config.variable_number < 1 || result.config.variable_number > 2) {
221 usage4 (_("Invalid variable number")); 253 usage4(_("Invalid variable number"));
222 break; 254 }
223 case 'w': /* critical time threshold */
224 value_warning_threshold = strtoul (optarg, NULL, 10);
225 break; 255 break;
226 case 'c': /* warning time threshold */ 256 case 'w': /* critical time threshold */ {
227 value_critical_threshold = strtoul (optarg, NULL, 10); 257 mp_range_parsed tmp = mp_parse_range_string(optarg);
258 if (tmp.error != MP_PARSING_SUCCESS) {
259 die(STATE_UNKNOWN, "failed to parse warning threshold");
260 }
261 result.config.values_threshold =
262 mp_thresholds_set_warn(result.config.values_threshold, tmp.range);
263 } break;
264 case 'c': /* warning time threshold */ {
265 mp_range_parsed tmp = mp_parse_range_string(optarg);
266 if (tmp.error != MP_PARSING_SUCCESS) {
267 die(STATE_UNKNOWN, "failed to parse critical threshold");
268 }
269 result.config.values_threshold =
270 mp_thresholds_set_crit(result.config.values_threshold, tmp.range);
271 } break;
272 case 'l': /* label */
273 result.config.label = optarg;
228 break; 274 break;
229 case 'l': /* label */ 275 case 'u': /* timeout */
230 label = optarg; 276 result.config.units = optarg;
231 break; 277 break;
232 case 'u': /* timeout */ 278 case 'V': /* version */
233 units = optarg; 279 print_revision(progname, NP_VERSION);
280 exit(STATE_UNKNOWN);
281 case 'h': /* help */
282 print_help();
283 exit(STATE_UNKNOWN);
284 case '?': /* help */
285 usage5();
286 case output_format_index: {
287 parsed_output_format parser = mp_parse_output_format(optarg);
288 if (!parser.parsing_success) {
289 printf("Invalid output format: %s\n", optarg);
290 exit(STATE_UNKNOWN);
291 }
292
293 result.config.output_format_is_set = true;
294 result.config.output_format = parser.output_format;
234 break; 295 break;
235 case 'V': /* version */ 296 }
236 print_revision (progname, NP_VERSION);
237 exit (STATE_UNKNOWN);
238 case 'h': /* help */
239 print_help ();
240 exit (STATE_UNKNOWN);
241 case '?': /* help */
242 usage5 ();
243 } 297 }
244 } 298 }
245 299
246 c = optind; 300 option_char = optind;
247 if (log_file == NULL && argc > c) { 301 if (result.config.log_file == NULL && argc > option_char) {
248 log_file = argv[c++]; 302 result.config.log_file = argv[option_char++];
249 } 303 }
250 304
251 if (expire_minutes <= 0 && argc > c) { 305 if (result.config.expire_minutes <= 0 && argc > option_char) {
252 if (is_intpos (argv[c])) 306 if (is_intpos(argv[option_char])) {
253 expire_minutes = atoi (argv[c++]); 307 result.config.expire_minutes = atoi(argv[option_char++]);
254 else 308 } else {
255 die (STATE_UNKNOWN, 309 die(STATE_UNKNOWN,
256 _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"), 310 _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"),
257 argv[c], progname); 311 argv[option_char], progname);
312 }
258 } 313 }
259 314
260 if (argc > c && strcmp (argv[c], "MAX") == 0) { 315 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) {
261 use_average = false; 316 result.config.use_average = false;
262 c++; 317 option_char++;
263 } 318 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) {
264 else if (argc > c && strcmp (argv[c], "AVG") == 0) { 319 result.config.use_average = true;
265 use_average = true; 320 option_char++;
266 c++;
267 } 321 }
268 322
269 if (argc > c && variable_number == -1) { 323 if (argc > option_char && result.config.variable_number == -1) {
270 variable_number = atoi (argv[c++]); 324 result.config.variable_number = atoi(argv[option_char++]);
271 if (variable_number < 1 || variable_number > 2) { 325 if (result.config.variable_number < 1 || result.config.variable_number > 2) {
272 printf ("%s :", argv[c]); 326 printf("%s :", argv[option_char]);
273 usage (_("Invalid variable number\n")); 327 usage(_("Invalid variable number\n"));
274 } 328 }
275 } 329 }
276 330
277 if (argc > c && value_warning_threshold == 0) { 331 if (argc > option_char && !result.config.values_threshold.warning_is_set) {
278 value_warning_threshold = strtoul (argv[c++], NULL, 10); 332 mp_range_parsed tmp = mp_parse_range_string(argv[option_char++]);
333 if (tmp.error != MP_PARSING_SUCCESS) {
334 die(STATE_UNKNOWN, "failed to parse warning threshold");
335 }
336 result.config.values_threshold =
337 mp_thresholds_set_warn(result.config.values_threshold, tmp.range);
279 } 338 }
280 339
281 if (argc > c && value_critical_threshold == 0) { 340 if (argc > option_char && !result.config.values_threshold.critical_is_set) {
282 value_critical_threshold = strtoul (argv[c++], NULL, 10); 341 mp_range_parsed tmp = mp_parse_range_string(argv[option_char++]);
342 if (tmp.error != MP_PARSING_SUCCESS) {
343 die(STATE_UNKNOWN, "failed to parse critical threshold");
344 }
345 result.config.values_threshold =
346 mp_thresholds_set_crit(result.config.values_threshold, tmp.range);
283 } 347 }
284 348
285 if (argc > c && strlen (label) == 0) { 349 if (argc > option_char && strlen(result.config.label) == 0) {
286 label = argv[c++]; 350 result.config.label = argv[option_char++];
287 } 351 }
288 352
289 if (argc > c && strlen (units) == 0) { 353 if (argc > option_char && strlen(result.config.units) == 0) {
290 units = argv[c++]; 354 result.config.units = argv[option_char++];
291 } 355 }
292 356
293 return validate_arguments (); 357 return validate_arguments(result);
294} 358}
295 359
296int 360check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper config_wrapper) {
297validate_arguments (void) 361 if (config_wrapper.config.variable_number == -1) {
298{ 362 usage4(_("You must supply the variable number"));
299 if (variable_number == -1) 363 }
300 usage4 (_("You must supply the variable number"));
301 364
302 if (label == NULL) 365 if (config_wrapper.config.label == NULL) {
303 label = strdup ("value"); 366 config_wrapper.config.label = strdup("value");
367 }
304 368
305 if (units == NULL) 369 if (config_wrapper.config.units == NULL) {
306 units = strdup (""); 370 config_wrapper.config.units = strdup("");
371 }
307 372
308 return OK; 373 return config_wrapper;
309} 374}
310 375
311 376void print_help(void) {
312 377 print_revision(progname, NP_VERSION);
313void 378
314print_help (void) 379 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
315{ 380 printf(COPYRIGHT, copyright, email);
316 print_revision (progname, NP_VERSION); 381
317 382 printf("%s\n", _("This plugin will check either the average or maximum value of one of the"));
318 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 383 printf("%s\n", _("two variables recorded in an MRTG log file."));
319 printf (COPYRIGHT, copyright, email); 384
320 385 printf("\n\n");
321 printf ("%s\n", _("This plugin will check either the average or maximum value of one of the")); 386
322 printf ("%s\n", _("two variables recorded in an MRTG log file.")); 387 print_usage();
323 388
324 printf ("\n\n"); 389 printf(UT_HELP_VRSN);
325 390 printf(UT_EXTRA_OPTS);
326 print_usage (); 391
327 392 printf(" %s\n", "-F, --logfile=FILE");
328 printf (UT_HELP_VRSN); 393 printf(" %s\n", _("The MRTG log file containing the data you want to monitor"));
329 printf (UT_EXTRA_OPTS); 394 printf(" %s\n", "-e, --expires=MINUTES");
330 395 printf(" %s\n", _("Minutes before MRTG data is considered to be too old"));
331 printf (" %s\n", "-F, --logfile=FILE"); 396 printf(" %s\n", "-a, --aggregation=AVG|MAX");
332 printf (" %s\n", _("The MRTG log file containing the data you want to monitor")); 397 printf(" %s\n", _("Should we check average or maximum values?"));
333 printf (" %s\n", "-e, --expires=MINUTES"); 398 printf(" %s\n", "-v, --variable=INTEGER");
334 printf (" %s\n", _("Minutes before MRTG data is considered to be too old")); 399 printf(" %s\n", _("Which variable set should we inspect? (1 or 2)"));
335 printf (" %s\n", "-a, --aggregation=AVG|MAX"); 400 printf(" %s\n", "-w, --warning=INTEGER");
336 printf (" %s\n", _("Should we check average or maximum values?")); 401 printf(" %s\n", _("Threshold value for data to result in WARNING status"));
337 printf (" %s\n", "-v, --variable=INTEGER"); 402 printf(" %s\n", "-c, --critical=INTEGER");
338 printf (" %s\n", _("Which variable set should we inspect? (1 or 2)")); 403 printf(" %s\n", _("Threshold value for data to result in CRITICAL status"));
339 printf (" %s\n", "-w, --warning=INTEGER"); 404 printf(" %s\n", "-l, --label=STRING");
340 printf (" %s\n", _("Threshold value for data to result in WARNING status")); 405 printf(" %s\n", _("Type label for data (Examples: Conns, \"Processor Load\", In, Out)"));
341 printf (" %s\n", "-c, --critical=INTEGER"); 406 printf(" %s\n", "-u, --units=STRING");
342 printf (" %s\n", _("Threshold value for data to result in CRITICAL status")); 407 printf(" %s\n", _("Option units label for data (Example: Packets/Sec, Errors/Sec,"));
343 printf (" %s\n", "-l, --label=STRING"); 408 printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")"));
344 printf (" %s\n", _("Type label for data (Examples: Conns, \"Processor Load\", In, Out)")); 409
345 printf (" %s\n", "-u, --units=STRING"); 410 printf(UT_OUTPUT_FORMAT);
346 printf (" %s\n", _("Option units label for data (Example: Packets/Sec, Errors/Sec,")); 411
347 printf (" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")")); 412 printf("\n");
348 413 printf(" %s\n",
349 printf ("\n"); 414 _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If"));
350 printf (" %s\n", _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If")); 415 printf(" %s\n", _("the value exceeds the <vcl> threshold, a CRITICAL status is returned. If"));
351 printf (" %s\n", _("the value exceeds the <vcl> threshold, a CRITICAL status is returned. If")); 416 printf(" %s\n", _("the data in the log file is older than <expire_minutes> old, a WARNING"));
352 printf (" %s\n", _("the data in the log file is older than <expire_minutes> old, a WARNING")); 417 printf(" %s\n", _("status is returned and a warning message is printed."));
353 printf (" %s\n", _("status is returned and a warning message is printed.")); 418
354 419 printf("\n");
355 printf ("\n"); 420 printf(" %s\n",
356 printf (" %s\n", _("This plugin is useful for monitoring MRTG data that does not correspond to")); 421 _("This plugin is useful for monitoring MRTG data that does not correspond to"));
357 printf (" %s\n", _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth).")); 422 printf(" %s\n",
358 printf (" %s\n", _("It can be used to monitor any kind of data that MRTG is monitoring - errors,")); 423 _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth)."));
359 printf (" %s\n", _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows")); 424 printf(" %s\n",
360 printf (" %s\n", _("me to track processor utilization, user connections, drive space, etc and")); 425 _("It can be used to monitor any kind of data that MRTG is monitoring - errors,"));
361 printf (" %s\n\n", _("this plugin works well for monitoring that kind of data as well.")); 426 printf(" %s\n",
362 427 _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows"));
363 printf ("%s\n", _("Notes:")); 428 printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and"));
364 printf (" %s\n", _("- This plugin only monitors one of the two variables stored in the MRTG log")); 429 printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well."));
365 printf (" %s\n", _("file. If you want to monitor both values you will have to define two")); 430
366 printf (" %s\n", _("commands with different values for the <variable> argument. Of course,")); 431 printf("%s\n", _("Notes:"));
367 printf (" %s\n", _("you can always hack the code to make this plugin work for you...")); 432 printf(" %s\n",
368 printf (" %s\n", _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from")); 433 _("- This plugin only monitors one of the two variables stored in the MRTG log"));
369 printf (" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"); 434 printf(" %s\n", _("file. If you want to monitor both values you will have to define two"));
370 435 printf(" %s\n", _("commands with different values for the <variable> argument. Of course,"));
371 printf (UT_SUPPORT); 436 printf(" %s\n", _("you can always hack the code to make this plugin work for you..."));
437 printf(" %s\n",
438 _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from"));
439 printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html");
440
441 printf(UT_SUPPORT);
372} 442}
373 443
374
375
376/* original command line: 444/* original command line:
377 <log_file> <expire_minutes> <AVG|MAX> <variable> <vwl> <vcl> <label> [units] */ 445 <log_file> <expire_minutes> <AVG|MAX> <variable> <vwl> <vcl> <label> [units] */
378 446
379void 447void print_usage(void) {
380print_usage (void) 448 printf("%s\n", _("Usage:"));
381{ 449 printf("%s -F log_file -a <AVG | MAX> -v variable -w warning -c critical\n", progname);
382 printf ("%s\n", _("Usage:")); 450 printf("[-l label] [-u units] [-e expire_minutes] [-t timeout] [-v]\n");
383 printf ("%s -F log_file -a <AVG | MAX> -v variable -w warning -c critical\n",progname);
384 printf ("[-l label] [-u units] [-e expire_minutes] [-t timeout] [-v]\n");
385} 451}
diff --git a/plugins/check_mrtg.d/config.h b/plugins/check_mrtg.d/config.h
new file mode 100644
index 00000000..4a5b5595
--- /dev/null
+++ b/plugins/check_mrtg.d/config.h
@@ -0,0 +1,37 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
6#include <stddef.h>
7#include <stdlib.h>
8
9typedef struct {
10 bool use_average;
11 int variable_number;
12 int expire_minutes;
13 char *label;
14 char *units;
15 char *log_file;
16
17 mp_thresholds values_threshold;
18
19 bool output_format_is_set;
20 mp_output_format output_format;
21} check_mrtg_config;
22
23check_mrtg_config check_mrtg_config_init() {
24 check_mrtg_config tmp = {
25 .use_average = true,
26 .variable_number = -1,
27 .expire_minutes = 0,
28 .label = NULL,
29 .units = NULL,
30 .log_file = NULL,
31
32 .values_threshold = mp_thresholds_init(),
33
34 .output_format_is_set = false,
35 };
36 return tmp;
37}
diff --git a/plugins/check_mrtgtraf.c b/plugins/check_mrtgtraf.c
index bd25d47d..46b94f57 100644
--- a/plugins/check_mrtgtraf.c
+++ b/plugins/check_mrtgtraf.c
@@ -1,381 +1,457 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_mrtgtraf plugin 3 * Monitoring check_mrtgtraf plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 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_mtrgtraf plugin 10 * This file contains the check_mtrgtraf plugin
11* 11 *
12* This plugin will check the incoming/outgoing transfer rates of a router 12 * This plugin will check the incoming/outgoing transfer rates of a router
13* switch, etc recorded in an MRTG log. 13 * switch, etc recorded in an MRTG log.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "check_mrtgtraf.d/config.h"
32#include "common.h" 33#include "common.h"
34#include "output.h"
35#include "perfdata.h"
36#include "states.h"
37#include "thresholds.h"
33#include "utils.h" 38#include "utils.h"
34 39
35const char *progname = "check_mrtgtraf"; 40const char *progname = "check_mrtgtraf";
36const char *copyright = "1999-2007"; 41const char *copyright = "1999-2024";
37const char *email = "devel@monitoring-plugins.org"; 42const char *email = "devel@monitoring-plugins.org";
38 43
39int process_arguments (int, char **); 44typedef struct {
40int validate_arguments (void); 45 int errorcode;
41void print_help(void); 46 check_mrtgtraf_config config;
42void print_usage(void); 47} check_mrtgtraf_config_wrapper;
43 48
44char *log_file = NULL; 49static check_mrtgtraf_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
45int expire_minutes = -1; 50static void print_help(void);
46bool use_average = true; 51void print_usage(void);
47unsigned long incoming_warning_threshold = 0L;
48unsigned long incoming_critical_threshold = 0L;
49unsigned long outgoing_warning_threshold = 0L;
50unsigned long outgoing_critical_threshold = 0L;
51
52
53int
54main (int argc, char **argv)
55{
56 int result = STATE_OK;
57 FILE *fp;
58 int line;
59 char input_buffer[MAX_INPUT_BUFFER];
60 char *temp_buffer;
61 time_t current_time;
62 char *error_message;
63 time_t timestamp = 0L;
64 unsigned long average_incoming_rate = 0L;
65 unsigned long average_outgoing_rate = 0L;
66 unsigned long maximum_incoming_rate = 0L;
67 unsigned long maximum_outgoing_rate = 0L;
68 unsigned long incoming_rate = 0L;
69 unsigned long outgoing_rate = 0L;
70 double adjusted_incoming_rate = 0.0;
71 double adjusted_outgoing_rate = 0.0;
72 char incoming_speed_rating[8];
73 char outgoing_speed_rating[8];
74 52
75 setlocale (LC_ALL, ""); 53int main(int argc, char **argv) {
76 bindtextdomain (PACKAGE, LOCALEDIR); 54 setlocale(LC_ALL, "");
77 textdomain (PACKAGE); 55 bindtextdomain(PACKAGE, LOCALEDIR);
56 textdomain(PACKAGE);
78 57
79 /* Parse extra opts if any */ 58 /* Parse extra opts if any */
80 argv=np_extra_opts (&argc, argv, progname); 59 argv = np_extra_opts(&argc, argv, progname);
81 60
82 if (process_arguments (argc, argv) == ERROR) 61 check_mrtgtraf_config_wrapper tmp_config = process_arguments(argc, argv);
83 usage4 (_("Could not parse arguments")); 62 if (tmp_config.errorcode == ERROR) {
63 usage4(_("Could not parse arguments"));
64 }
84 65
85 /* open the MRTG log file for reading */ 66 const check_mrtgtraf_config config = tmp_config.config;
86 fp = fopen (log_file, "r"); 67
87 if (fp == NULL) 68 if (config.output_format_is_set) {
88 usage4 (_("Unable to open MRTG log file")); 69 mp_set_format(config.output_format);
70 }
89 71
90 line = 0; 72 mp_check overall = mp_check_init();
91 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) { 73 mp_subcheck sc_open_mrtg_log_file = mp_subcheck_init();
92 74
75 /* open the MRTG log file for reading */
76 FILE *mrtg_log_file_ptr = fopen(config.log_file, "r");
77 if (mrtg_log_file_ptr == NULL) {
78 sc_open_mrtg_log_file = mp_set_subcheck_state(sc_open_mrtg_log_file, STATE_UNKNOWN);
79 xasprintf(&sc_open_mrtg_log_file.output, "unable to open MRTG log file");
80 mp_add_subcheck_to_check(&overall, sc_open_mrtg_log_file);
81 mp_exit(overall);
82 } else {
83 sc_open_mrtg_log_file = mp_set_subcheck_state(sc_open_mrtg_log_file, STATE_OK);
84 xasprintf(&sc_open_mrtg_log_file.output, "opened MRTG log file");
85 mp_add_subcheck_to_check(&overall, sc_open_mrtg_log_file);
86 }
87
88 time_t timestamp = 0L;
89 char input_buffer[MAX_INPUT_BUFFER];
90 unsigned long average_incoming_rate = 0L;
91 unsigned long average_outgoing_rate = 0L;
92 unsigned long maximum_incoming_rate = 0L;
93 unsigned long maximum_outgoing_rate = 0L;
94 int line = 0;
95 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mrtg_log_file_ptr)) {
93 line++; 96 line++;
94 97
95 /* skip the first line of the log file */ 98 /* skip the first line of the log file */
96 if (line == 1) 99 if (line == 1) {
97 continue; 100 continue;
101 }
98 102
99 /* break out of read loop */ 103 /* break out of read loop */
100 /* if we've passed the number of entries we want to read */ 104 /* if we've passed the number of entries we want to read */
101 if (line > 2) 105 if (line > 2) {
102 break; 106 break;
107 }
103 108
104 /* grab the timestamp */ 109 /* grab the timestamp */
105 temp_buffer = strtok (input_buffer, " "); 110 char *temp_buffer = strtok(input_buffer, " ");
106 timestamp = strtoul (temp_buffer, NULL, 10); 111 timestamp = strtoul(temp_buffer, NULL, 10);
107 112
108 /* grab the average incoming transfer rate */ 113 /* grab the average incoming transfer rate */
109 temp_buffer = strtok (NULL, " "); 114 temp_buffer = strtok(NULL, " ");
110 average_incoming_rate = strtoul (temp_buffer, NULL, 10); 115 average_incoming_rate = strtoul(temp_buffer, NULL, 10);
111 116
112 /* grab the average outgoing transfer rate */ 117 /* grab the average outgoing transfer rate */
113 temp_buffer = strtok (NULL, " "); 118 temp_buffer = strtok(NULL, " ");
114 average_outgoing_rate = strtoul (temp_buffer, NULL, 10); 119 average_outgoing_rate = strtoul(temp_buffer, NULL, 10);
115 120
116 /* grab the maximum incoming transfer rate */ 121 /* grab the maximum incoming transfer rate */
117 temp_buffer = strtok (NULL, " "); 122 temp_buffer = strtok(NULL, " ");
118 maximum_incoming_rate = strtoul (temp_buffer, NULL, 10); 123 maximum_incoming_rate = strtoul(temp_buffer, NULL, 10);
119 124
120 /* grab the maximum outgoing transfer rate */ 125 /* grab the maximum outgoing transfer rate */
121 temp_buffer = strtok (NULL, " "); 126 temp_buffer = strtok(NULL, " ");
122 maximum_outgoing_rate = strtoul (temp_buffer, NULL, 10); 127 maximum_outgoing_rate = strtoul(temp_buffer, NULL, 10);
123 } 128 }
124 129
125 /* close the log file */ 130 /* close the log file */
126 fclose (fp); 131 fclose(mrtg_log_file_ptr);
127 132
128 /* if we couldn't read enough data, return an unknown error */ 133 /* if we couldn't read enough data, return an unknown error */
129 if (line <= 2) 134 if (line <= 2) {
130 usage4 (_("Unable to process MRTG log file")); 135 usage4(_("Unable to process MRTG log file"));
136 }
131 137
132 /* make sure the MRTG data isn't too old */ 138 /* make sure the MRTG data isn't too old */
133 time (&current_time); 139 time_t current_time;
134 if ((expire_minutes > 0) && 140 time(&current_time);
135 (current_time - timestamp) > (expire_minutes * 60)) 141 mp_subcheck sc_expired = mp_subcheck_init();
136 die (STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"), 142 if ((config.expire_minutes > 0) && (current_time - timestamp) > (config.expire_minutes * 60)) {
137 (int) ((current_time - timestamp) / 60)); 143 xasprintf(&sc_expired.output, "MRTG data has expired (%d minutes old)",
144 (int)((current_time - timestamp) / 60));
145 sc_expired = mp_set_subcheck_state(sc_expired, STATE_WARNING);
146 mp_add_subcheck_to_check(&overall, sc_expired);
147 mp_exit(overall);
148 }
149
150 xasprintf(&sc_expired.output, "MRTG data should be valid (%d minutes old)",
151 (int)((current_time - timestamp) / 60));
152 sc_expired = mp_set_subcheck_state(sc_expired, STATE_WARNING);
153 mp_add_subcheck_to_check(&overall, sc_expired);
138 154
155 unsigned long incoming_rate = 0L;
156 unsigned long outgoing_rate = 0L;
139 /* else check the incoming/outgoing rates */ 157 /* else check the incoming/outgoing rates */
140 if (use_average) { 158 if (config.use_average) {
141 incoming_rate = average_incoming_rate; 159 incoming_rate = average_incoming_rate;
142 outgoing_rate = average_outgoing_rate; 160 outgoing_rate = average_outgoing_rate;
143 } 161 } else {
144 else {
145 incoming_rate = maximum_incoming_rate; 162 incoming_rate = maximum_incoming_rate;
146 outgoing_rate = maximum_outgoing_rate; 163 outgoing_rate = maximum_outgoing_rate;
147 } 164 }
148 165
166 double adjusted_incoming_rate = 0.0;
167 char incoming_speed_rating[8];
149 /* report incoming traffic in Bytes/sec */ 168 /* report incoming traffic in Bytes/sec */
150 if (incoming_rate < 1024) { 169 if (incoming_rate < 1024) {
151 strcpy (incoming_speed_rating, "B"); 170 strcpy(incoming_speed_rating, "B");
152 adjusted_incoming_rate = (double) incoming_rate; 171 adjusted_incoming_rate = (double)incoming_rate;
153 } 172 }
154 173
155 /* report incoming traffic in KBytes/sec */ 174 /* report incoming traffic in KBytes/sec */
156 else if (incoming_rate < (1024 * 1024)) { 175 else if (incoming_rate < (1024 * 1024)) {
157 strcpy (incoming_speed_rating, "KB"); 176 strcpy(incoming_speed_rating, "KB");
158 adjusted_incoming_rate = (double) (incoming_rate / 1024.0); 177 adjusted_incoming_rate = ((double)incoming_rate / 1024.0);
159 } 178 }
160 179
161 /* report incoming traffic in MBytes/sec */ 180 /* report incoming traffic in MBytes/sec */
162 else { 181 else {
163 strcpy (incoming_speed_rating, "MB"); 182 strcpy(incoming_speed_rating, "MB");
164 adjusted_incoming_rate = (double) (incoming_rate / 1024.0 / 1024.0); 183 adjusted_incoming_rate = ((double)incoming_rate / 1024.0 / 1024.0);
165 } 184 }
166 185
167 /* report outgoing traffic in Bytes/sec */ 186 double adjusted_outgoing_rate = 0.0;
187 char outgoing_speed_rating[8];
168 if (outgoing_rate < 1024) { 188 if (outgoing_rate < 1024) {
169 strcpy (outgoing_speed_rating, "B"); 189 /* report outgoing traffic in Bytes/sec */
170 adjusted_outgoing_rate = (double) outgoing_rate; 190 strcpy(outgoing_speed_rating, "B");
171 } 191 adjusted_outgoing_rate = (double)outgoing_rate;
172 192 } else if (outgoing_rate < (1024 * 1024)) {
173 /* report outgoing traffic in KBytes/sec */ 193 /* report outgoing traffic in KBytes/sec */
174 else if (outgoing_rate < (1024 * 1024)) { 194 strcpy(outgoing_speed_rating, "KB");
175 strcpy (outgoing_speed_rating, "KB"); 195 adjusted_outgoing_rate = ((double)outgoing_rate / 1024.0);
176 adjusted_outgoing_rate = (double) (outgoing_rate / 1024.0); 196 } else {
177 } 197 /* report outgoing traffic in MBytes/sec */
178 198 strcpy(outgoing_speed_rating, "MB");
179 /* report outgoing traffic in MBytes/sec */ 199 adjusted_outgoing_rate = ((double)outgoing_rate / 1024.0 / 1024.0);
180 else {
181 strcpy (outgoing_speed_rating, "MB");
182 adjusted_outgoing_rate = (double) (outgoing_rate / 1024.0 / 1024.0);
183 }
184
185 if (incoming_rate > incoming_critical_threshold
186 || outgoing_rate > outgoing_critical_threshold) {
187 result = STATE_CRITICAL;
188 }
189 else if (incoming_rate > incoming_warning_threshold
190 || outgoing_rate > outgoing_warning_threshold) {
191 result = STATE_WARNING;
192 } 200 }
193 201
194 xasprintf (&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"), 202 mp_perfdata pd_rate_in = perfdata_init();
195 (use_average) ? _("Avg") : _("Max"), adjusted_incoming_rate, 203 pd_rate_in.label = "in";
196 incoming_speed_rating, (use_average) ? _("Avg") : _("Max"), 204 pd_rate_in = mp_set_pd_value(pd_rate_in, incoming_rate);
197 adjusted_outgoing_rate, outgoing_speed_rating, 205 pd_rate_in.uom = "B";
198 fperfdata("in", adjusted_incoming_rate, incoming_speed_rating, 206 pd_rate_in = mp_pd_set_thresholds(pd_rate_in, config.incoming_thresholds);
199 (int)incoming_warning_threshold, incoming_warning_threshold, 207
200 (int)incoming_critical_threshold, incoming_critical_threshold, 208 mp_perfdata pd_rate_out = perfdata_init();
201 true, 0, false, 0), 209 pd_rate_out.label = "out";
202 fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating, 210 pd_rate_out = mp_set_pd_value(pd_rate_out, outgoing_rate);
203 (int)outgoing_warning_threshold, outgoing_warning_threshold, 211 pd_rate_out.uom = "B";
204 (int)outgoing_critical_threshold, outgoing_critical_threshold, 212 pd_rate_out = mp_pd_set_thresholds(pd_rate_out, config.outgoing_thresholds);
205 true, 0, false, 0)); 213
206 214 mp_subcheck sc_rate_in = mp_subcheck_init();
207 printf (_("Traffic %s - %s\n"), state_text(result), error_message); 215 sc_rate_in = mp_set_subcheck_state(sc_rate_in, mp_get_pd_status(pd_rate_in));
208 216 mp_add_perfdata_to_subcheck(&sc_rate_in, pd_rate_in);
209 return result; 217 xasprintf(&sc_rate_in.output, "%s. In = %0.1f %s/s", (config.use_average) ? _("Avg") : _("Max"),
218 adjusted_incoming_rate, incoming_speed_rating);
219
220 mp_subcheck sc_rate_out = mp_subcheck_init();
221 sc_rate_out = mp_set_subcheck_state(sc_rate_out, mp_get_pd_status(pd_rate_out));
222 mp_add_perfdata_to_subcheck(&sc_rate_out, pd_rate_out);
223 xasprintf(&sc_rate_out.output, "%s. Out = %0.1f %s/s",
224 (config.use_average) ? _("Avg") : _("Max"), adjusted_outgoing_rate,
225 outgoing_speed_rating);
226
227 mp_subcheck sc_rate = mp_subcheck_init();
228 xasprintf(&sc_rate.output, "Traffic");
229 mp_add_subcheck_to_subcheck(&sc_rate, sc_rate_in);
230 mp_add_subcheck_to_subcheck(&sc_rate, sc_rate_out);
231
232 mp_add_subcheck_to_check(&overall, sc_rate);
233
234 mp_exit(overall);
210} 235}
211 236
212
213
214/* process command-line arguments */ 237/* process command-line arguments */
215int 238check_mrtgtraf_config_wrapper process_arguments(int argc, char **argv) {
216process_arguments (int argc, char **argv) 239 enum {
217{ 240 output_format_index = CHAR_MAX + 1,
218 int c;
219
220 int option = 0;
221 static struct option longopts[] = {
222 {"filename", required_argument, 0, 'F'},
223 {"expires", required_argument, 0, 'e'},
224 {"aggregation", required_argument, 0, 'a'},
225 {"critical", required_argument, 0, 'c'},
226 {"warning", required_argument, 0, 'w'},
227 {"version", no_argument, 0, 'V'},
228 {"help", no_argument, 0, 'h'},
229 {0, 0, 0, 0}
230 }; 241 };
231 242
232 if (argc < 2) 243 static struct option longopts[] = {{"filename", required_argument, 0, 'F'},
233 return ERROR; 244 {"expires", required_argument, 0, 'e'},
234 245 {"aggregation", required_argument, 0, 'a'},
235 for (c = 1; c < argc; c++) { 246 {"critical", required_argument, 0, 'c'},
236 if (strcmp ("-to", argv[c]) == 0) 247 {"warning", required_argument, 0, 'w'},
237 strcpy (argv[c], "-t"); 248 {"version", no_argument, 0, 'V'},
238 else if (strcmp ("-wt", argv[c]) == 0) 249 {"help", no_argument, 0, 'h'},
239 strcpy (argv[c], "-w"); 250 {"output-format", required_argument, 0, output_format_index},
240 else if (strcmp ("-ct", argv[c]) == 0) 251 {0, 0, 0, 0}};
241 strcpy (argv[c], "-c"); 252
253 check_mrtgtraf_config_wrapper result = {
254 .errorcode = OK,
255 .config = check_mrtgtraf_config_init(),
256 };
257 if (argc < 2) {
258 result.errorcode = ERROR;
259 return result;
242 } 260 }
243 261
244 while (1) { 262 for (int i = 1; i < argc; i++) {
245 c = getopt_long (argc, argv, "hVF:e:a:c:w:", longopts, &option); 263 if (strcmp("-to", argv[i]) == 0) {
264 strcpy(argv[i], "-t");
265 } else if (strcmp("-wt", argv[i]) == 0) {
266 strcpy(argv[i], "-w");
267 } else if (strcmp("-ct", argv[i]) == 0) {
268 strcpy(argv[i], "-c");
269 }
270 }
246 271
247 if (c == -1 || c == EOF) 272 int option_char;
273 int option = 0;
274 unsigned long incoming_warning_threshold = 0;
275 unsigned long incoming_critical_threshold = 0;
276 unsigned long outgoing_warning_threshold = 0;
277 unsigned long outgoing_critical_threshold = 0;
278 bool incoming_warning_set = false;
279 bool incoming_critical_set = false;
280 bool outgoing_warning_set = false;
281 bool outgoing_critical_set = false;
282 while (true) {
283 option_char = getopt_long(argc, argv, "hVF:e:a:c:w:", longopts, &option);
284
285 if (option_char == -1 || option_char == EOF) {
248 break; 286 break;
287 }
249 288
250 switch (c) { 289 switch (option_char) {
251 case 'F': /* input file */ 290 case 'F': /* input file */
252 log_file = optarg; 291 result.config.log_file = optarg;
292 break;
293 case 'e': /* expiration time */
294 result.config.expire_minutes = atoi(optarg);
253 break; 295 break;
254 case 'e': /* expiration time */ 296 case 'a': /* aggregation (AVE or MAX) */
255 expire_minutes = atoi (optarg); 297 result.config.use_average = (bool)(strcmp(optarg, "MAX"));
256 break; 298 break;
257 case 'a': /* aggregation (AVE or MAX) */ 299 case 'c': /* critical threshold */
258 if (!strcmp (optarg, "MAX")) 300 sscanf(optarg, "%lu,%lu", &incoming_critical_threshold, &outgoing_critical_threshold);
259 use_average = false; 301 incoming_critical_set = true;
260 else 302 outgoing_critical_set = true;
261 use_average = true;
262 break; 303 break;
263 case 'c': /* warning threshold */ 304 case 'w': /* warning threshold */
264 sscanf (optarg, "%lu,%lu", &incoming_critical_threshold, 305 sscanf(optarg, "%lu,%lu", &incoming_warning_threshold, &outgoing_warning_threshold);
265 &outgoing_critical_threshold); 306 incoming_warning_set = true;
307 incoming_critical_set = true;
266 break; 308 break;
267 case 'w': /* critical threshold */ 309 case 'V': /* version */
268 sscanf (optarg, "%lu,%lu", &incoming_warning_threshold, 310 print_revision(progname, NP_VERSION);
269 &outgoing_warning_threshold); 311 exit(STATE_UNKNOWN);
312 case 'h': /* help */
313 print_help();
314 exit(STATE_UNKNOWN);
315 case '?': /* help */
316 usage5();
317 case output_format_index: {
318 parsed_output_format parser = mp_parse_output_format(optarg);
319 if (!parser.parsing_success) {
320 printf("Invalid output format: %s\n", optarg);
321 exit(STATE_UNKNOWN);
322 }
323
324 result.config.output_format_is_set = true;
325 result.config.output_format = parser.output_format;
270 break; 326 break;
271 case 'V': /* version */ 327 }
272 print_revision (progname, NP_VERSION);
273 exit (STATE_UNKNOWN);
274 case 'h': /* help */
275 print_help ();
276 exit (STATE_UNKNOWN);
277 case '?': /* help */
278 usage5 ();
279 } 328 }
280 } 329 }
281 330
282 c = optind; 331 option_char = optind;
283 if (argc > c && log_file == NULL) { 332 if (argc > option_char && result.config.log_file == NULL) {
284 log_file = argv[c++]; 333 result.config.log_file = argv[option_char++];
285 } 334 }
286 335
287 if (argc > c && expire_minutes == -1) { 336 if (argc > option_char && result.config.expire_minutes == -1) {
288 expire_minutes = atoi (argv[c++]); 337 result.config.expire_minutes = atoi(argv[option_char++]);
289 } 338 }
290 339
291 if (argc > c && strcmp (argv[c], "MAX") == 0) { 340 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) {
292 use_average = false; 341 result.config.use_average = false;
293 c++; 342 option_char++;
343 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) {
344 result.config.use_average = true;
345 option_char++;
294 } 346 }
295 else if (argc > c && strcmp (argv[c], "AVG") == 0) { 347
296 use_average = true; 348 if (argc > option_char && incoming_warning_threshold == 0) {
297 c++; 349 incoming_warning_threshold = strtoul(argv[option_char++], NULL, 10);
350 incoming_warning_set = true;
298 } 351 }
299 352
300 if (argc > c && incoming_warning_threshold == 0) { 353 if (argc > option_char && incoming_critical_threshold == 0) {
301 incoming_warning_threshold = strtoul (argv[c++], NULL, 10); 354 incoming_critical_threshold = strtoul(argv[option_char++], NULL, 10);
355 incoming_critical_set = true;
302 } 356 }
303 357
304 if (argc > c && incoming_critical_threshold == 0) { 358 if (argc > option_char && outgoing_warning_threshold == 0) {
305 incoming_critical_threshold = strtoul (argv[c++], NULL, 10); 359 outgoing_warning_threshold = strtoul(argv[option_char++], NULL, 10);
360 outgoing_warning_set = true;
306 } 361 }
307 362
308 if (argc > c && outgoing_warning_threshold == 0) { 363 if (argc > option_char && outgoing_critical_threshold == 0) {
309 outgoing_warning_threshold = strtoul (argv[c++], NULL, 10); 364 outgoing_critical_threshold = strtoul(argv[option_char++], NULL, 10);
365 outgoing_critical_set = true;
310 } 366 }
311 367
312 if (argc > c && outgoing_critical_threshold == 0) { 368 mp_range incoming_warning = mp_range_init();
313 outgoing_critical_threshold = strtoul (argv[c++], NULL, 10); 369 if (incoming_warning_set) {
370 incoming_warning =
371 mp_range_set_end(incoming_warning, mp_create_pd_value(incoming_warning_threshold));
314 } 372 }
315 373
316 return validate_arguments (); 374 result.config.incoming_thresholds =
317} 375 mp_thresholds_set_warn(result.config.incoming_thresholds, incoming_warning);
318 376
377 mp_range incoming_critical = mp_range_init();
378 if (incoming_critical_set) {
379 incoming_critical =
380 mp_range_set_end(incoming_critical, mp_create_pd_value(incoming_critical_threshold));
381 }
319 382
320int 383 result.config.incoming_thresholds =
321validate_arguments (void) 384 mp_thresholds_set_crit(result.config.incoming_thresholds, incoming_critical);
322{
323 return OK;
324}
325 385
386 mp_range outgoing_warning = mp_range_init();
387 if (outgoing_warning_set) {
388 outgoing_warning =
389 mp_range_set_end(outgoing_warning, mp_create_pd_value(outgoing_warning_threshold));
390 }
326 391
327void 392 result.config.outgoing_thresholds =
328print_help (void) 393 mp_thresholds_set_warn(result.config.outgoing_thresholds, outgoing_warning);
329{ 394
330 print_revision (progname, NP_VERSION); 395 mp_range outgoing_critical = mp_range_init();
331 396 if (outgoing_critical_set) {
332 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 397 outgoing_critical =
333 printf (COPYRIGHT, copyright, email); 398 mp_range_set_end(outgoing_critical, mp_create_pd_value(outgoing_critical_threshold));
334 399 }
335 printf ("%s\n", _("This plugin will check the incoming/outgoing transfer rates of a router,"));
336 printf ("%s\n", _("switch, etc recorded in an MRTG log. If the newest log entry is older"));
337 printf ("%s\n", _("than <expire_minutes>, a WARNING status is returned. If either the"));
338 printf ("%s\n", _("incoming or outgoing rates exceed the <icl> or <ocl> thresholds (in"));
339 printf ("%s\n", _("Bytes/sec), a CRITICAL status results. If either of the rates exceed"));
340 printf ("%s\n", _("the <iwl> or <owl> thresholds (in Bytes/sec), a WARNING status results."));
341
342 printf ("\n\n");
343
344 print_usage ();
345
346 printf (UT_HELP_VRSN);
347 printf (UT_EXTRA_OPTS);
348
349 printf (" %s\n", "-F, --filename=STRING");
350 printf (" %s\n", _("File to read log from"));
351 printf (" %s\n", "-e, --expires=INTEGER");
352 printf (" %s\n", _("Minutes after which log expires"));
353 printf (" %s\n", "-a, --aggregation=(AVG|MAX)");
354 printf (" %s\n", _("Test average or maximum"));
355 printf (" %s\n", "-w, --warning");
356 printf (" %s\n", _("Warning threshold pair <incoming>,<outgoing>"));
357 printf (" %s\n", "-c, --critical");
358 printf (" %s\n", _("Critical threshold pair <incoming>,<outgoing>"));
359
360 printf ("\n");
361 printf ("%s\n", _("Notes:"));
362 printf (" %s\n", _("- MRTG stands for Multi Router Traffic Grapher. It can be downloaded from"));
363 printf (" %s\n", " http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html");
364 printf (" %s\n", _("- While MRTG can monitor things other than traffic rates, this"));
365 printf (" %s\n", _(" plugin probably won't work with much else without modification."));
366 printf (" %s\n", _("- The calculated i/o rates are a little off from what MRTG actually"));
367 printf (" %s\n", _(" reports. I'm not sure why this is right now, but will look into it"));
368 printf (" %s\n", _(" for future enhancements of this plugin."));
369
370 printf (UT_SUPPORT);
371}
372 400
401 result.config.outgoing_thresholds =
402 mp_thresholds_set_crit(result.config.outgoing_thresholds, outgoing_critical);
373 403
404 return result;
405}
406
407void print_help(void) {
408 print_revision(progname, NP_VERSION);
409
410 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
411 printf(COPYRIGHT, copyright, email);
412
413 printf("%s\n", _("This plugin will check the incoming/outgoing transfer rates of a router,"));
414 printf("%s\n", _("switch, etc recorded in an MRTG log. If the newest log entry is older"));
415 printf("%s\n", _("than <expire_minutes>, a WARNING status is returned. If either the"));
416 printf("%s\n", _("incoming or outgoing rates exceed the <icl> or <ocl> thresholds (in"));
417 printf("%s\n", _("Bytes/sec), a CRITICAL status results. If either of the rates exceed"));
418 printf("%s\n", _("the <iwl> or <owl> thresholds (in Bytes/sec), a WARNING status results."));
419
420 printf("\n\n");
421
422 print_usage();
423
424 printf(UT_HELP_VRSN);
425 printf(UT_EXTRA_OPTS);
426
427 printf(" %s\n", "-F, --filename=STRING");
428 printf(" %s\n", _("File to read log from"));
429 printf(" %s\n", "-e, --expires=INTEGER");
430 printf(" %s\n", _("Minutes after which log expires"));
431 printf(" %s\n", "-a, --aggregation=(AVG|MAX)");
432 printf(" %s\n", _("Test average or maximum"));
433 printf(" %s\n", "-w, --warning");
434 printf(" %s\n", _("Warning threshold pair <incoming>,<outgoing>"));
435 printf(" %s\n", "-c, --critical");
436 printf(" %s\n", _("Critical threshold pair <incoming>,<outgoing>"));
437
438 printf(UT_OUTPUT_FORMAT);
439
440 printf("\n");
441 printf("%s\n", _("Notes:"));
442 printf(" %s\n", _("- MRTG stands for Multi Router Traffic Grapher. It can be downloaded from"));
443 printf(" %s\n", " http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html");
444 printf(" %s\n", _("- While MRTG can monitor things other than traffic rates, this"));
445 printf(" %s\n", _(" plugin probably won't work with much else without modification."));
446 printf(" %s\n", _("- The calculated i/o rates are a little off from what MRTG actually"));
447 printf(" %s\n", _(" reports. I'm not sure why this is right now, but will look into it"));
448 printf(" %s\n", _(" for future enhancements of this plugin."));
449
450 printf(UT_SUPPORT);
451}
374 452
375void 453void print_usage(void) {
376print_usage (void) 454 printf(_("Usage"));
377{ 455 printf(" %s -F <log_file> -a <AVG | MAX> -w <warning_pair>\n", progname);
378 printf (_("Usage")); 456 printf("-c <critical_pair> [-e expire_minutes]\n");
379 printf (" %s -F <log_file> -a <AVG | MAX> -w <warning_pair>\n",progname);
380 printf ("-c <critical_pair> [-e expire_minutes]\n");
381} 457}
diff --git a/plugins/check_mrtgtraf.d/config.h b/plugins/check_mrtgtraf.d/config.h
new file mode 100644
index 00000000..d9737243
--- /dev/null
+++ b/plugins/check_mrtgtraf.d/config.h
@@ -0,0 +1,33 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
6#include <stddef.h>
7#include <stdlib.h>
8
9typedef struct {
10 char *log_file;
11 int expire_minutes;
12 bool use_average;
13
14 mp_thresholds incoming_thresholds;
15 mp_thresholds outgoing_thresholds;
16
17 bool output_format_is_set;
18 mp_output_format output_format;
19} check_mrtgtraf_config;
20
21check_mrtgtraf_config check_mrtgtraf_config_init() {
22 check_mrtgtraf_config tmp = {
23 .log_file = NULL,
24 .expire_minutes = -1,
25 .use_average = true,
26
27 .incoming_thresholds = mp_thresholds_init(),
28 .outgoing_thresholds = mp_thresholds_init(),
29
30 .output_format_is_set = false,
31 };
32 return tmp;
33}
diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c
index 6a7daf11..b70e0e22 100644
--- a/plugins/check_mysql.c
+++ b/plugins/check_mysql.c
@@ -1,444 +1,552 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_mysql plugin 3 * Monitoring check_mysql plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at) 6 * Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)
7* Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net) 7 * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net)
8* Copyright (c) 1999-2011 Monitoring Plugins Development Team 8 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
9* 9 *
10* Description: 10 * Description:
11* 11 *
12* This file contains the check_mysql plugin 12 * This file contains the check_mysql plugin
13* 13 *
14* This program tests connections to a mysql server 14 * This program tests connections to a mysql server
15* 15 *
16* 16 *
17* This program is free software: you can redistribute it and/or modify 17 * This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by 18 * it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or 19 * the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version. 20 * (at your option) any later version.
21* 21 *
22* This program is distributed in the hope that it will be useful, 22 * This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details. 25 * GNU General Public License for more details.
26* 26 *
27* You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>. 28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29* 29 *
30* 30 *
31*****************************************************************************/ 31 *****************************************************************************/
32
33const char *progname = "check_mysql";
34const char *copyright = "1999-2011";
35const char *email = "devel@monitoring-plugins.org";
36
37#define SLAVERESULTSIZE 96
38 32
39#include "common.h" 33#include "common.h"
34#include "output.h"
35#include "perfdata.h"
36#include "states.h"
37#include "thresholds.h"
40#include "utils.h" 38#include "utils.h"
41#include "utils_base.h" 39#include "utils_base.h"
42#include "netutils.h" 40#include "netutils.h"
41#include "check_mysql.d/config.h"
43 42
44#include <mysql.h> 43#include <mysql.h>
45#include <mysqld_error.h> 44#include <mysqld_error.h>
46#include <errmsg.h> 45#include <errmsg.h>
47 46
48char *db_user = NULL; 47const char *progname = "check_mysql";
49char *db_host = NULL; 48const char *copyright = "1999-2024";
50char *db_socket = NULL; 49const char *email = "devel@monitoring-plugins.org";
51char *db_pass = NULL; 50
52char *db = NULL; 51static int verbose = 0;
53char *ca_cert = NULL; 52
54char *ca_dir = NULL; 53#define REPLICA_RESULTSIZE 96
55char *cert = NULL;
56char *key = NULL;
57char *ciphers = NULL;
58bool ssl = false;
59char *opt_file = NULL;
60char *opt_group = NULL;
61unsigned int db_port = MYSQL_PORT;
62int check_slave = 0, warn_sec = 0, crit_sec = 0;
63int ignore_auth = 0;
64int verbose = 0;
65
66static double warning_time = 0;
67static double critical_time = 0;
68 54
69#define LENGTH_METRIC_UNIT 6 55#define LENGTH_METRIC_UNIT 6
70static const char *metric_unit[LENGTH_METRIC_UNIT] = { 56static const char *metric_unit[LENGTH_METRIC_UNIT] = {
71 "Open_files", 57 "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache",
72 "Open_tables", 58 "Threads_connected", "Threads_running"};
73 "Qcache_free_memory",
74 "Qcache_queries_in_cache",
75 "Threads_connected",
76 "Threads_running"
77};
78 59
79#define LENGTH_METRIC_COUNTER 9 60#define LENGTH_METRIC_COUNTER 9
80static const char *metric_counter[LENGTH_METRIC_COUNTER] = { 61static const char *metric_counter[LENGTH_METRIC_COUNTER] = {"Connections",
81 "Connections", 62 "Qcache_hits",
82 "Qcache_hits", 63 "Qcache_inserts",
83 "Qcache_inserts", 64 "Qcache_lowmem_prunes",
84 "Qcache_lowmem_prunes", 65 "Qcache_not_cached",
85 "Qcache_not_cached", 66 "Queries",
86 "Queries", 67 "Questions",
87 "Questions", 68 "Table_locks_waited",
88 "Table_locks_waited", 69 "Uptime"};
89 "Uptime" 70
90}; 71#define MYSQLDUMP_THREADS_QUERY \
91 72 "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE " \
92#define MYSQLDUMP_THREADS_QUERY "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE 'SELECT /*!40001 SQL_NO_CACHE */%'" 73 "'SELECT /*!40001 SQL_NO_CACHE */%'"
93 74
94thresholds *my_threshold = NULL; 75typedef struct {
95 76 int errorcode;
96int process_arguments (int, char **); 77 check_mysql_config config;
97int validate_arguments (void); 78} check_mysql_config_wrapper;
98void print_help (void); 79static check_mysql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
99void print_usage (void); 80static check_mysql_config_wrapper validate_arguments(check_mysql_config_wrapper /*config_wrapper*/);
100 81static void print_help(void);
101int 82void print_usage(void);
102main (int argc, char **argv) 83
103{ 84int main(int argc, char **argv) {
85 setlocale(LC_ALL, "");
86 bindtextdomain(PACKAGE, LOCALEDIR);
87 textdomain(PACKAGE);
104 88
105 MYSQL mysql; 89 /* Parse extra opts if any */
106 MYSQL_RES *res; 90 argv = np_extra_opts(&argc, argv, progname);
107 MYSQL_ROW row;
108 91
109 /* should be status */ 92 check_mysql_config_wrapper tmp_config = process_arguments(argc, argv);
93 if (tmp_config.errorcode == ERROR) {
94 usage4(_("Could not parse arguments"));
95 }
110 96
111 char *result = NULL; 97 const check_mysql_config config = tmp_config.config;
112 char *error = NULL;
113 char slaveresult[SLAVERESULTSIZE] = { 0 };
114 char* perf;
115 98
116 perf = strdup (""); 99 if (config.output_format_is_set) {
100 mp_set_format(config.output_format);
101 }
117 102
118 setlocale (LC_ALL, ""); 103 MYSQL mysql;
119 bindtextdomain (PACKAGE, LOCALEDIR); 104 /* initialize mysql */
120 textdomain (PACKAGE); 105 mysql_init(&mysql);
121 106
122 /* Parse extra opts if any */ 107 if (config.opt_file != NULL) {
123 argv=np_extra_opts (&argc, argv, progname); 108 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file);
109 }
124 110
125 if (process_arguments (argc, argv) == ERROR) 111 if (config.opt_group != NULL) {
126 usage4 (_("Could not parse arguments")); 112 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group);
113 } else {
114 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client");
115 }
127 116
128 /* initialize mysql */ 117 if (config.ssl) {
129 mysql_init (&mysql); 118 mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir,
130 119 config.ciphers);
131 if (opt_file != NULL) 120 }
132 mysql_options(&mysql,MYSQL_READ_DEFAULT_FILE,opt_file); 121
133 122 mp_check overall = mp_check_init();
134 if (opt_group != NULL) 123
135 mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,opt_group); 124 mp_subcheck sc_connection = mp_subcheck_init();
136 else 125 /* establish a connection to the server and check for errors */
137 mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"client"); 126 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db,
138 127 config.db_port, config.db_socket, 0)) {
139 if (ssl)
140 mysql_ssl_set(&mysql,key,cert,ca_cert,ca_dir,ciphers);
141 /* establish a connection to the server and error checking */
142 if (!mysql_real_connect(&mysql,db_host,db_user,db_pass,db,db_port,db_socket,0)) {
143 /* Depending on internally-selected auth plugin MySQL might return */ 128 /* Depending on internally-selected auth plugin MySQL might return */
144 /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */ 129 /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */
145 /* Semantically these errors are the same. */ 130 /* Semantically these errors are the same. */
146 if (ignore_auth && (mysql_errno (&mysql) == ER_ACCESS_DENIED_ERROR || mysql_errno (&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) 131 if (config.ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR ||
147 { 132 mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) {
148 printf("MySQL OK - Version: %s (protocol %d)\n", 133 xasprintf(&sc_connection.output, "Version: %s (protocol %d)",
149 mysql_get_server_info(&mysql), 134 mysql_get_server_info(&mysql), mysql_get_proto_info(&mysql));
150 mysql_get_proto_info(&mysql) 135 sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK);
151 ); 136
152 mysql_close (&mysql); 137 mysql_close(&mysql);
153 return STATE_OK; 138 } else {
139 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
140 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
141 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
142 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
143 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
144 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
145 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
146 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
147 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
148 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
149 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
150 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
151 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
152 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
153 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
154 } else {
155 sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL);
156 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
157 }
154 } 158 }
155 else if (mysql_errno (&mysql) == CR_UNKNOWN_HOST) 159
156 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 160 mp_add_subcheck_to_check(&overall, sc_connection);
157 else if (mysql_errno (&mysql) == CR_VERSION_ERROR) 161 mp_exit(overall);
158 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 162 } else {
159 else if (mysql_errno (&mysql) == CR_OUT_OF_MEMORY) 163 // successful connection
160 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 164 sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK);
161 else if (mysql_errno (&mysql) == CR_IPSOCK_ERROR) 165 xasprintf(&sc_connection.output, "Version: %s (protocol %d)", mysql_get_server_info(&mysql),
162 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 166 mysql_get_proto_info(&mysql));
163 else if (mysql_errno (&mysql) == CR_SOCKET_CREATE_ERROR) 167 mp_add_subcheck_to_check(&overall, sc_connection);
164 die (STATE_WARNING, "%s\n", mysql_error (&mysql));
165 else
166 die (STATE_CRITICAL, "%s\n", mysql_error (&mysql));
167 } 168 }
168 169
169 /* get the server stats */ 170 /* get the server stats */
170 result = strdup (mysql_stat (&mysql)); 171 char *mysql_stats = strdup(mysql_stat(&mysql));
172
173 mp_subcheck sc_stats = mp_subcheck_init();
174 sc_stats = mp_set_subcheck_default_state(sc_stats, STATE_OK);
171 175
172 /* error checking once more */ 176 /* error checking once more */
173 if (mysql_error (&mysql)) { 177 if (mysql_errno(&mysql) != 0) {
174 if (mysql_errno (&mysql) == CR_SERVER_GONE_ERROR) 178 if ((mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) ||
175 die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); 179 (mysql_errno(&mysql) == CR_SERVER_LOST) || (mysql_errno(&mysql) == CR_UNKNOWN_ERROR)) {
176 else if (mysql_errno (&mysql) == CR_SERVER_LOST) 180 sc_stats = mp_set_subcheck_state(sc_stats, STATE_CRITICAL);
177 die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); 181 xasprintf(&sc_stats.output, "Retrieving stats failed: %s", mysql_error(&mysql));
178 else if (mysql_errno (&mysql) == CR_UNKNOWN_ERROR) 182 } else {
179 die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); 183 // not sure which error modes occur here, but mysql_error indicates an error
184 sc_stats = mp_set_subcheck_state(sc_stats, STATE_WARNING);
185 xasprintf(&sc_stats.output, "retrieving stats caused an error: %s",
186 mysql_error(&mysql));
187 }
188
189 mp_add_subcheck_to_check(&overall, sc_stats);
190 mp_exit(overall);
191 } else {
192 xasprintf(&sc_stats.output, "retrieved stats: %s", mysql_stats);
193 sc_stats = mp_set_subcheck_state(sc_stats, STATE_OK);
194 mp_add_subcheck_to_check(&overall, sc_stats);
180 } 195 }
181 196
197 MYSQL_RES *res;
198 MYSQL_ROW row;
199 mp_subcheck sc_query = mp_subcheck_init();
182 /* try to fetch some perf data */ 200 /* try to fetch some perf data */
183 if (mysql_query (&mysql, "show global status") == 0) { 201 if (mysql_query(&mysql, "show global status") == 0) {
184 if ( (res = mysql_store_result (&mysql)) == NULL) { 202 if ((res = mysql_store_result(&mysql)) == NULL) {
185 error = strdup(mysql_error(&mysql)); 203 xasprintf(&sc_connection.output, "query failed - status store_result error: %s",
186 mysql_close (&mysql); 204 mysql_error(&mysql));
187 die (STATE_CRITICAL, _("status store_result error: %s\n"), error); 205 mysql_close(&mysql);
188 }
189 206
190 while ( (row = mysql_fetch_row (res)) != NULL) { 207 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
191 int i; 208 mp_add_subcheck_to_check(&overall, sc_query);
209 mp_exit(overall);
210 }
192 211
193 for(i = 0; i < LENGTH_METRIC_UNIT; i++) { 212 while ((row = mysql_fetch_row(res)) != NULL) {
213 for (int i = 0; i < LENGTH_METRIC_UNIT; i++) {
194 if (strcmp(row[0], metric_unit[i]) == 0) { 214 if (strcmp(row[0], metric_unit[i]) == 0) {
195 xasprintf(&perf, "%s%s ", perf, perfdata(metric_unit[i], 215 mp_perfdata pd_mysql_stat = perfdata_init();
196 atol(row[1]), "", false, 0, false, 0, false, 0, false, 0)); 216 pd_mysql_stat.label = (char *)metric_unit[i];
217 pd_mysql_stat.value = mp_create_pd_value(atol(row[1]));
218 mp_add_perfdata_to_subcheck(&sc_stats, pd_mysql_stat);
197 continue; 219 continue;
198 } 220 }
199 } 221 }
200 for(i = 0; i < LENGTH_METRIC_COUNTER; i++) { 222
223 for (int i = 0; i < LENGTH_METRIC_COUNTER; i++) {
201 if (strcmp(row[0], metric_counter[i]) == 0) { 224 if (strcmp(row[0], metric_counter[i]) == 0) {
202 xasprintf(&perf, "%s%s ", perf, perfdata(metric_counter[i], 225 mp_perfdata pd_mysql_stat = perfdata_init();
203 atol(row[1]), "c", false, 0, false, 0, false, 0, false, 0)); 226 pd_mysql_stat.label = (char *)metric_counter[i];
227 pd_mysql_stat.value = mp_create_pd_value(atol(row[1]));
228 pd_mysql_stat.uom = "c";
229 mp_add_perfdata_to_subcheck(&sc_stats, pd_mysql_stat);
204 continue; 230 continue;
205 } 231 }
206 } 232 }
207 } 233 }
208 /* remove trailing space */ 234 } else {
209 if (strlen(perf) > 0) 235 // Query failed!
210 perf[strlen(perf) - 1] = '\0'; 236 xasprintf(&sc_connection.output, "query failed");
237 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
238 mp_add_subcheck_to_check(&overall, sc_query);
239 mp_exit(overall);
211 } 240 }
212 241
213 if(check_slave) { 242 if (config.check_replica) {
214 /* check the slave status */ 243 // Detect which version we are, on older version
215 if (mysql_query (&mysql, "show slave status") != 0) { 244 // "show slave status" should work, on newer ones
216 error = strdup(mysql_error(&mysql)); 245 // "show replica status"
217 mysql_close (&mysql); 246 // But first we have to find out whether this is
218 die (STATE_CRITICAL, _("slave query error: %s\n"), error); 247 // MySQL or MariaDB since the version numbering scheme
248 // is different
249 bool use_deprecated_slave_status = false;
250 const char *server_version = mysql_get_server_info(&mysql);
251 unsigned long server_verion_int = mysql_get_server_version(&mysql);
252 unsigned long major_version = server_verion_int / 10000;
253 unsigned long minor_version = (server_verion_int % 10000) / 100;
254 unsigned long patch_version = (server_verion_int % 100);
255
256 if (verbose) {
257 printf("Found MariaDB/MySQL: %s, main version: %lu, minor version: %lu, patch version: "
258 "%lu\n",
259 server_version, major_version, minor_version, patch_version);
260 }
261
262 if (strstr(server_version, "MariaDB") != NULL) {
263 // Looks like MariaDB, new commands should be available after 10.5.1
264 if (major_version < 10) {
265 use_deprecated_slave_status = true;
266 } else if (major_version == 10) {
267 if (minor_version < 5) {
268 use_deprecated_slave_status = true;
269 } else if (minor_version == 5 && patch_version < 1) {
270 use_deprecated_slave_status = true;
271 }
272 }
273 } else {
274 // Looks like MySQL or at least not like MariaDB
275 if (major_version < 8) {
276 use_deprecated_slave_status = true;
277 } else if (major_version == 10 && minor_version < 4) {
278 use_deprecated_slave_status = true;
279 }
280 }
281
282 char *replica_query = NULL;
283 if (use_deprecated_slave_status) {
284 replica_query = "show slave status";
285 } else {
286 replica_query = "show replica status";
287 }
288
289 mp_subcheck sc_replica = mp_subcheck_init();
290
291 /* check the replica status */
292 if (mysql_query(&mysql, replica_query) != 0) {
293 xasprintf(&sc_replica.output, "replica query error: %s", mysql_error(&mysql));
294 mysql_close(&mysql);
295
296 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
297 mp_add_subcheck_to_check(&overall, sc_replica);
298 mp_exit(overall);
219 } 299 }
220 300
221 /* store the result */ 301 /* store the result */
222 if ( (res = mysql_store_result (&mysql)) == NULL) { 302 if ((res = mysql_store_result(&mysql)) == NULL) {
223 error = strdup(mysql_error(&mysql)); 303 xasprintf(&sc_replica.output, "replica store_result error: %s", mysql_error(&mysql));
224 mysql_close (&mysql); 304 mysql_close(&mysql);
225 die (STATE_CRITICAL, _("slave store_result error: %s\n"), error); 305
306 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
307 mp_add_subcheck_to_check(&overall, sc_replica);
308 mp_exit(overall);
226 } 309 }
227 310
228 /* Check there is some data */ 311 /* Check there is some data */
229 if (mysql_num_rows(res) == 0) { 312 if (mysql_num_rows(res) == 0) {
230 mysql_close(&mysql); 313 mysql_close(&mysql);
231 die (STATE_WARNING, "%s\n", _("No slaves defined")); 314
315 xasprintf(&sc_replica.output, "no replicas defined");
316 sc_replica = mp_set_subcheck_state(sc_replica, STATE_WARNING);
317 mp_add_subcheck_to_check(&overall, sc_replica);
318 mp_exit(overall);
232 } 319 }
233 320
234 /* fetch the first row */ 321 /* fetch the first row */
235 if ( (row = mysql_fetch_row (res)) == NULL) { 322 if ((row = mysql_fetch_row(res)) == NULL) {
236 error = strdup(mysql_error(&mysql)); 323 xasprintf(&sc_replica.output, "replica fetch row error: %s", mysql_error(&mysql));
237 mysql_free_result (res); 324 mysql_free_result(res);
238 mysql_close (&mysql); 325 mysql_close(&mysql);
239 die (STATE_CRITICAL, _("slave fetch row error: %s\n"), error); 326
327 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
328 mp_add_subcheck_to_check(&overall, sc_replica);
329 mp_exit(overall);
240 } 330 }
241 331
242 if (mysql_field_count (&mysql) == 12) { 332 if (mysql_field_count(&mysql) == 12) {
243 /* mysql 3.23.x */ 333 /* mysql 3.23.x */
244 snprintf (slaveresult, SLAVERESULTSIZE, _("Slave running: %s"), row[6]); 334 xasprintf(&sc_replica.output, "Replica running: %s", row[6]);
245 if (strcmp (row[6], "Yes") != 0) { 335 if (strcmp(row[6], "Yes") != 0) {
246 mysql_free_result (res); 336 mysql_free_result(res);
247 mysql_close (&mysql); 337 mysql_close(&mysql);
248 die (STATE_CRITICAL, "%s\n", slaveresult); 338
339 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
340 mp_add_subcheck_to_check(&overall, sc_replica);
341 mp_exit(overall);
249 } 342 }
250
251 } else { 343 } else {
252 /* mysql 4.x.x and mysql 5.x.x */ 344 /* mysql 4.x.x and mysql 5.x.x */
253 int slave_io_field = -1 , slave_sql_field = -1, seconds_behind_field = -1, i, num_fields; 345 int replica_io_field = -1;
254 MYSQL_FIELD* fields; 346 int replica_sql_field = -1;
255 347 int seconds_behind_field = -1;
256 num_fields = mysql_num_fields(res); 348 unsigned int num_fields = mysql_num_fields(res);
257 fields = mysql_fetch_fields(res); 349 MYSQL_FIELD *fields = mysql_fetch_fields(res);
258 for(i = 0; i < num_fields; i++) { 350 for (int i = 0; i < (int)num_fields; i++) {
259 if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { 351 if ((strcasecmp(fields[i].name, "Slave_IO_Running") == 0) ||
260 slave_io_field = i; 352 (strcasecmp(fields[i].name, "Replica_IO_Running") == 0)) {
353 replica_io_field = i;
261 continue; 354 continue;
262 } 355 }
263 if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) { 356 if ((strcasecmp(fields[i].name, "Slave_SQL_Running") == 0) ||
264 slave_sql_field = i; 357 (strcasecmp(fields[i].name, "Replica_SQL_Running") == 0)) {
358 replica_sql_field = i;
265 continue; 359 continue;
266 } 360 }
267 if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) { 361 if ((strcasecmp(fields[i].name, "Seconds_Behind_Master") == 0) ||
362 (strcasecmp(fields[i].name, "Seconds_Behind_Source") == 0)) {
268 seconds_behind_field = i; 363 seconds_behind_field = i;
269 continue; 364 continue;
270 } 365 }
271 } 366 }
272 367
273 /* Check if slave status is available */ 368 /* Check if replica status is available */
274 if ((slave_io_field < 0) || (slave_sql_field < 0) || (num_fields == 0)) { 369 if ((replica_io_field < 0) || (replica_sql_field < 0) || (num_fields == 0)) {
275 mysql_free_result (res); 370 mysql_free_result(res);
276 mysql_close (&mysql); 371 mysql_close(&mysql);
277 die (STATE_CRITICAL, "Slave status unavailable\n"); 372
373 xasprintf(&sc_replica.output, "Replica status unavailable");
374 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
375 mp_add_subcheck_to_check(&overall, sc_replica);
376 mp_exit(overall);
278 } 377 }
279 378
280 /* Save slave status in slaveresult */ 379 /* Save replica status in replica_result */
281 snprintf (slaveresult, SLAVERESULTSIZE, "Slave IO: %s Slave SQL: %s Seconds Behind Master: %s", row[slave_io_field], row[slave_sql_field], seconds_behind_field!=-1?row[seconds_behind_field]:"Unknown"); 380 xasprintf(&sc_replica.output,
381 "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s",
382 row[replica_io_field], row[replica_sql_field],
383 seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown");
282 384
283 /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no mysqldump threads running */ 385 /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no
284 if (strcmp (row[slave_io_field], "Yes") != 0 || strcmp (row[slave_sql_field], "Yes") != 0) { 386 * mysqldump threads running */
387 if (strcmp(row[replica_io_field], "Yes") != 0 ||
388 strcmp(row[replica_sql_field], "Yes") != 0) {
285 MYSQL_RES *res_mysqldump; 389 MYSQL_RES *res_mysqldump;
286 MYSQL_ROW row_mysqldump; 390 MYSQL_ROW row_mysqldump;
287 unsigned int mysqldump_threads = 0; 391 unsigned int mysqldump_threads = 0;
288 392
289 if (mysql_query (&mysql, MYSQLDUMP_THREADS_QUERY) == 0) { 393 if (mysql_query(&mysql, MYSQLDUMP_THREADS_QUERY) == 0) {
290 /* store the result */ 394 /* store the result */
291 if ( (res_mysqldump = mysql_store_result (&mysql)) != NULL) { 395 if ((res_mysqldump = mysql_store_result(&mysql)) != NULL) {
292 if (mysql_num_rows(res_mysqldump) == 1) { 396 if (mysql_num_rows(res_mysqldump) == 1) {
293 if ( (row_mysqldump = mysql_fetch_row (res_mysqldump)) != NULL) { 397 if ((row_mysqldump = mysql_fetch_row(res_mysqldump)) != NULL) {
294 mysqldump_threads = atoi(row_mysqldump[0]); 398 mysqldump_threads = atoi(row_mysqldump[0]);
295 } 399 }
296 } 400 }
297 /* free the result */ 401 /* free the result */
298 mysql_free_result (res_mysqldump); 402 mysql_free_result(res_mysqldump);
299 } 403 }
300 mysql_close (&mysql); 404 mysql_close(&mysql);
301 } 405 }
406
302 if (mysqldump_threads == 0) { 407 if (mysqldump_threads == 0) {
303 die (STATE_CRITICAL, "%s\n", slaveresult); 408 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
409 mp_add_subcheck_to_check(&overall, sc_replica);
410 mp_exit(overall);
304 } else { 411 } else {
305 strncat(slaveresult, " Mysqldump: in progress", SLAVERESULTSIZE-1); 412 xasprintf(&sc_replica.output, "%s %s", sc_replica.output,
413 " Mysqldump: in progress");
306 } 414 }
307 } 415 }
308 416
309 if (verbose >=3) { 417 if (verbose >= 3) {
310 if (seconds_behind_field == -1) { 418 if (seconds_behind_field == -1) {
311 printf("seconds_behind_field not found\n"); 419 printf("seconds_behind_field not found\n");
312 } else { 420 } else {
313 printf ("seconds_behind_field(index %d)=%s\n", seconds_behind_field, row[seconds_behind_field]); 421 printf("seconds_behind_field(index %d)=%s\n", seconds_behind_field,
422 row[seconds_behind_field]);
314 } 423 }
315 } 424 }
316 425
317 /* Check Seconds Behind against threshold */ 426 /* Check Seconds Behind against threshold */
318 if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && strcmp (row[seconds_behind_field], "NULL") != 0)) { 427 if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL &&
319 double value = atof(row[seconds_behind_field]); 428 strcmp(row[seconds_behind_field], "NULL") != 0)) {
320 int status; 429 mp_perfdata pd_seconds_behind = perfdata_init();
321 430 pd_seconds_behind.label = "seconds behind master";
322 status = get_status(value, my_threshold); 431 pd_seconds_behind.value = mp_create_pd_value(atof(row[seconds_behind_field]));
323 432 pd_seconds_behind =
324 xasprintf (&perf, "%s %s", perf, fperfdata ("seconds behind master", value, "s", 433 mp_pd_set_thresholds(pd_seconds_behind, config.replica_thresholds);
325 true, (double) warning_time, 434 pd_seconds_behind.uom = "s";
326 true, (double) critical_time, 435 mp_add_perfdata_to_subcheck(&sc_replica, pd_seconds_behind);
327 false, 0, 436
328 false, 0)); 437 mp_state_enum status = mp_get_pd_status(pd_seconds_behind);
329 438
330 if (status == STATE_WARNING) { 439 sc_replica = mp_set_subcheck_state(sc_replica, status);
331 printf("SLOW_SLAVE %s: %s|%s\n", _("WARNING"), slaveresult, perf); 440
332 exit(STATE_WARNING); 441 if (status != STATE_OK) {
333 } else if (status == STATE_CRITICAL) { 442 xasprintf(&sc_replica.output, "slow replica - %s", sc_replica.output);
334 printf("SLOW_SLAVE %s: %s|%s\n", _("CRITICAL"), slaveresult, perf); 443 mp_add_subcheck_to_check(&overall, sc_replica);
335 exit(STATE_CRITICAL); 444 mp_exit(overall);
336 } 445 }
337 } 446 }
338 } 447 }
339 448
340 /* free the result */ 449 /* free the result */
341 mysql_free_result (res); 450 mysql_free_result(res);
342 } 451 }
343 452
344 /* close the connection */ 453 /* close the connection */
345 mysql_close (&mysql); 454 mysql_close(&mysql);
346
347 /* print out the result of stats */
348 if (check_slave) {
349 printf ("%s %s|%s\n", result, slaveresult, perf);
350 } else {
351 printf ("%s|%s\n", result, perf);
352 }
353 455
354 return STATE_OK; 456 mp_exit(overall);
355} 457}
356 458
357
358/* process command-line arguments */ 459/* process command-line arguments */
359int 460check_mysql_config_wrapper process_arguments(int argc, char **argv) {
360process_arguments (int argc, char **argv)
361{
362 int c;
363 char *warning = NULL;
364 char *critical = NULL;
365 461
366 int option = 0; 462 enum {
367 static struct option longopts[] = { 463 CHECK_REPLICA_OPT = CHAR_MAX + 1,
368 {"hostname", required_argument, 0, 'H'}, 464 output_format_index,
369 {"socket", required_argument, 0, 's'},
370 {"database", required_argument, 0, 'd'},
371 {"username", required_argument, 0, 'u'},
372 {"password", required_argument, 0, 'p'},
373 {"file", required_argument, 0, 'f'},
374 {"group", required_argument, 0, 'g'},
375 {"port", required_argument, 0, 'P'},
376 {"critical", required_argument, 0, 'c'},
377 {"warning", required_argument, 0, 'w'},
378 {"check-slave", no_argument, 0, 'S'},
379 {"ignore-auth", no_argument, 0, 'n'},
380 {"verbose", no_argument, 0, 'v'},
381 {"version", no_argument, 0, 'V'},
382 {"help", no_argument, 0, 'h'},
383 {"ssl", no_argument, 0, 'l'},
384 {"ca-cert", optional_argument, 0, 'C'},
385 {"key", required_argument,0,'k'},
386 {"cert", required_argument,0,'a'},
387 {"ca-dir", required_argument, 0, 'D'},
388 {"ciphers", required_argument, 0, 'L'},
389 {0, 0, 0, 0}
390 }; 465 };
391 466
392 if (argc < 1) 467 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
393 return ERROR; 468 {"socket", required_argument, 0, 's'},
469 {"database", required_argument, 0, 'd'},
470 {"username", required_argument, 0, 'u'},
471 {"password", required_argument, 0, 'p'},
472 {"file", required_argument, 0, 'f'},
473 {"group", required_argument, 0, 'g'},
474 {"port", required_argument, 0, 'P'},
475 {"critical", required_argument, 0, 'c'},
476 {"warning", required_argument, 0, 'w'},
477 {"check-slave", no_argument, 0, 'S'},
478 {"check-replica", no_argument, 0, CHECK_REPLICA_OPT},
479 {"ignore-auth", no_argument, 0, 'n'},
480 {"verbose", no_argument, 0, 'v'},
481 {"version", no_argument, 0, 'V'},
482 {"help", no_argument, 0, 'h'},
483 {"ssl", no_argument, 0, 'l'},
484 {"ca-cert", optional_argument, 0, 'C'},
485 {"key", required_argument, 0, 'k'},
486 {"cert", required_argument, 0, 'a'},
487 {"ca-dir", required_argument, 0, 'D'},
488 {"ciphers", required_argument, 0, 'L'},
489 {"output-format", required_argument, 0, output_format_index},
490 {0, 0, 0, 0}};
491
492 check_mysql_config_wrapper result = {
493 .errorcode = OK,
494 .config = check_mysql_config_init(),
495 };
394 496
395 while (1) { 497 if (argc < 1) {
396 c = getopt_long (argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option); 498 result.errorcode = ERROR;
499 return result;
500 }
397 501
398 if (c == -1 || c == EOF) 502 int option = 0;
503 while (true) {
504 int option_index =
505 getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option);
506
507 if (CHECK_EOF(option_index)) {
399 break; 508 break;
509 }
400 510
401 switch (c) { 511 switch (option_index) {
402 case 'H': /* hostname */ 512 case 'H': /* hostname */
403 if (is_host (optarg)) { 513 if (is_host(optarg)) {
404 db_host = optarg; 514 result.config.db_host = optarg;
405 } 515 } else if (*optarg == '/') {
406 else if (*optarg == '/') { 516 result.config.db_socket = optarg;
407 db_socket = optarg; 517 } else {
408 } 518 usage2(_("Invalid hostname/address"), optarg);
409 else {
410 usage2 (_("Invalid hostname/address"), optarg);
411 } 519 }
412 break; 520 break;
413 case 's': /* socket */ 521 case 's': /* socket */
414 db_socket = optarg; 522 result.config.db_socket = optarg;
415 break; 523 break;
416 case 'd': /* database */ 524 case 'd': /* database */
417 db = optarg; 525 result.config.db = optarg;
418 break; 526 break;
419 case 'l': 527 case 'l':
420 ssl = true; 528 result.config.ssl = true;
421 break; 529 break;
422 case 'C': 530 case 'C':
423 ca_cert = optarg; 531 result.config.ca_cert = optarg;
424 break; 532 break;
425 case 'a': 533 case 'a':
426 cert = optarg; 534 result.config.cert = optarg;
427 break; 535 break;
428 case 'k': 536 case 'k':
429 key = optarg; 537 result.config.key = optarg;
430 break; 538 break;
431 case 'D': 539 case 'D':
432 ca_dir = optarg; 540 result.config.ca_dir = optarg;
433 break; 541 break;
434 case 'L': 542 case 'L':
435 ciphers = optarg; 543 result.config.ciphers = optarg;
436 break; 544 break;
437 case 'u': /* username */ 545 case 'u': /* username */
438 db_user = optarg; 546 result.config.db_user = optarg;
439 break; 547 break;
440 case 'p': /* authentication information: password */ 548 case 'p': /* authentication information: password */
441 db_pass = strdup(optarg); 549 result.config.db_pass = strdup(optarg);
442 550
443 /* Delete the password from process list */ 551 /* Delete the password from process list */
444 while (*optarg != '\0') { 552 while (*optarg != '\0') {
@@ -446,167 +554,185 @@ process_arguments (int argc, char **argv)
446 optarg++; 554 optarg++;
447 } 555 }
448 break; 556 break;
449 case 'f': /* client options file */ 557 case 'f': /* client options file */
450 opt_file = optarg; 558 result.config.opt_file = optarg;
451 break; 559 break;
452 case 'g': /* client options group */ 560 case 'g': /* client options group */
453 opt_group = optarg; 561 result.config.opt_group = optarg;
454 break; 562 break;
455 case 'P': /* critical time threshold */ 563 case 'P': /* critical time threshold */
456 db_port = atoi (optarg); 564 result.config.db_port = atoi(optarg);
457 break; 565 break;
458 case 'S': 566 case 'S':
459 check_slave = 1; /* check-slave */ 567 case CHECK_REPLICA_OPT:
568 result.config.check_replica = true; /* check-slave */
460 break; 569 break;
461 case 'n': 570 case 'n':
462 ignore_auth = 1; /* ignore-auth */ 571 result.config.ignore_auth = true; /* ignore-auth */
463 break;
464 case 'w':
465 warning = optarg;
466 warning_time = strtod (warning, NULL);
467 break;
468 case 'c':
469 critical = optarg;
470 critical_time = strtod (critical, NULL);
471 break; 572 break;
472 case 'V': /* version */ 573 case 'w': {
473 print_revision (progname, NP_VERSION); 574 mp_range_parsed tmp = mp_parse_range_string(optarg);
474 exit (STATE_UNKNOWN); 575 if (tmp.error != MP_PARSING_SUCCESS) {
475 case 'h': /* help */ 576 die(STATE_UNKNOWN, "failed to parse warning time threshold");
476 print_help (); 577 }
477 exit (STATE_UNKNOWN); 578 result.config.replica_thresholds =
579 mp_thresholds_set_warn(result.config.replica_thresholds, tmp.range);
580 } break;
581 case 'c': {
582 mp_range_parsed tmp = mp_parse_range_string(optarg);
583 if (tmp.error != MP_PARSING_SUCCESS) {
584 die(STATE_UNKNOWN, "failed to parse critical time threshold");
585 }
586 result.config.replica_thresholds =
587 mp_thresholds_set_crit(result.config.replica_thresholds, tmp.range);
588 } break;
589 case 'V': /* version */
590 print_revision(progname, NP_VERSION);
591 exit(STATE_UNKNOWN);
592 case 'h': /* help */
593 print_help();
594 exit(STATE_UNKNOWN);
478 case 'v': 595 case 'v':
479 verbose++; 596 verbose++;
480 break; 597 break;
481 case '?': /* help */ 598 case '?': /* help */
482 usage5 (); 599 usage5();
600 case output_format_index: {
601 parsed_output_format parser = mp_parse_output_format(optarg);
602 if (!parser.parsing_success) {
603 printf("Invalid output format: %s\n", optarg);
604 exit(STATE_UNKNOWN);
605 }
606
607 result.config.output_format_is_set = true;
608 result.config.output_format = parser.output_format;
609 break;
610 }
483 } 611 }
484 } 612 }
485 613
486 c = optind; 614 int index = optind;
487 615
488 set_thresholds(&my_threshold, warning, critical); 616 while (argc > index) {
489 617 if (result.config.db_host == NULL) {
490 while ( argc > c ) { 618 if (is_host(argv[index])) {
491 619 result.config.db_host = argv[index++];
492 if (db_host == NULL) 620 } else {
493 if (is_host (argv[c])) { 621 usage2(_("Invalid hostname/address"), argv[index]);
494 db_host = argv[c++];
495 }
496 else {
497 usage2 (_("Invalid hostname/address"), argv[c]);
498 } 622 }
499 else if (db_user == NULL) 623 } else if (result.config.db_user == NULL) {
500 db_user = argv[c++]; 624 result.config.db_user = argv[index++];
501 else if (db_pass == NULL) 625 } else if (result.config.db_pass == NULL) {
502 db_pass = argv[c++]; 626 result.config.db_pass = argv[index++];
503 else if (db == NULL) 627 } else if (result.config.db == NULL) {
504 db = argv[c++]; 628 result.config.db = argv[index++];
505 else if (is_intnonneg (argv[c])) 629 } else if (is_intnonneg(argv[index])) {
506 db_port = atoi (argv[c++]); 630 result.config.db_port = atoi(argv[index++]);
507 else 631 } else {
508 break; 632 break;
633 }
509 } 634 }
510 635
511 return validate_arguments (); 636 return validate_arguments(result);
512} 637}
513 638
639check_mysql_config_wrapper validate_arguments(check_mysql_config_wrapper config_wrapper) {
640 if (config_wrapper.config.db_user == NULL) {
641 config_wrapper.config.db_user = strdup("");
642 }
514 643
515int 644 if (config_wrapper.config.db_host == NULL) {
516validate_arguments (void) 645 config_wrapper.config.db_host = strdup("");
517{ 646 }
518 if (db_user == NULL)
519 db_user = strdup("");
520
521 if (db_host == NULL)
522 db_host = strdup("");
523 647
524 if (db == NULL) 648 if (config_wrapper.config.db == NULL) {
525 db = strdup(""); 649 config_wrapper.config.db = strdup("");
650 }
526 651
527 return OK; 652 return config_wrapper;
528} 653}
529 654
530 655void print_help(void) {
531void
532print_help (void)
533{
534 char *myport; 656 char *myport;
535 xasprintf (&myport, "%d", MYSQL_PORT); 657 xasprintf(&myport, "%d", MYSQL_PORT);
536 658
537 print_revision (progname, NP_VERSION); 659 print_revision(progname, NP_VERSION);
538 660
539 printf (_(COPYRIGHT), copyright, email); 661 printf(_(COPYRIGHT), copyright, email);
540 662
541 printf ("%s\n", _("This program tests connections to a MySQL server")); 663 printf("%s\n", _("This program tests connections to a MySQL server"));
542 664
543 printf ("\n\n"); 665 printf("\n\n");
544 666
545 print_usage (); 667 print_usage();
546 668
547 printf (UT_HELP_VRSN); 669 printf(UT_HELP_VRSN);
548 printf (UT_EXTRA_OPTS); 670 printf(UT_EXTRA_OPTS);
549 671
550 printf (UT_HOST_PORT, 'P', myport); 672 printf(UT_HOST_PORT, 'P', myport);
551 printf (" %s\n", "-n, --ignore-auth"); 673 printf(" %s\n", "-n, --ignore-auth");
552 printf (" %s\n", _("Ignore authentication failure and check for mysql connectivity only")); 674 printf(" %s\n", _("Ignore authentication failure and check for mysql connectivity only"));
553 675
554 printf (" %s\n", "-s, --socket=STRING"); 676 printf(" %s\n", "-s, --socket=STRING");
555 printf (" %s\n", _("Use the specified socket (has no effect if -H is used)")); 677 printf(" %s\n", _("Use the specified socket (has no effect if -H is used)"));
556 678
557 printf (" %s\n", "-d, --database=STRING"); 679 printf(" %s\n", "-d, --database=STRING");
558 printf (" %s\n", _("Check database with indicated name")); 680 printf(" %s\n", _("Check database with indicated name"));
559 printf (" %s\n", "-f, --file=STRING"); 681 printf(" %s\n", "-f, --file=STRING");
560 printf (" %s\n", _("Read from the specified client options file")); 682 printf(" %s\n", _("Read from the specified client options file"));
561 printf (" %s\n", "-g, --group=STRING"); 683 printf(" %s\n", "-g, --group=STRING");
562 printf (" %s\n", _("Use a client options group")); 684 printf(" %s\n", _("Use a client options group"));
563 printf (" %s\n", "-u, --username=STRING"); 685 printf(" %s\n", "-u, --username=STRING");
564 printf (" %s\n", _("Connect using the indicated username")); 686 printf(" %s\n", _("Connect using the indicated username"));
565 printf (" %s\n", "-p, --password=STRING"); 687 printf(" %s\n", "-p, --password=STRING");
566 printf (" %s\n", _("Use the indicated password to authenticate the connection")); 688 printf(" %s\n", _("Use the indicated password to authenticate the connection"));
567 printf (" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); 689 printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!"));
568 printf (" %s\n", _("Your clear-text password could be visible as a process table entry")); 690 printf(" %s\n", _("Your clear-text password could be visible as a process table entry"));
569 printf (" %s\n", "-S, --check-slave"); 691 printf(" %s\n", "-S, --check-slave");
570 printf (" %s\n", _("Check if the slave thread is running properly.")); 692 printf(" %s\n", _("Check if the slave thread is running properly. This option is deprecated "
571 printf (" %s\n", "-w, --warning"); 693 "in favour of check-replica, which does the same"));
572 printf (" %s\n", _("Exit with WARNING status if slave server is more than INTEGER seconds")); 694 printf(" %s\n", "--check-replica");
573 printf (" %s\n", _("behind master")); 695 printf(" %s\n", _("Check if the replica thread is running properly."));
574 printf (" %s\n", "-c, --critical"); 696 printf(" %s\n", "-w, --warning");
575 printf (" %s\n", _("Exit with CRITICAL status if slave server is more then INTEGER seconds")); 697 printf(" %s\n",
576 printf (" %s\n", _("behind master")); 698 _("Exit with WARNING status if replica server is more than INTEGER seconds"));
577 printf (" %s\n", "-l, --ssl"); 699 printf(" %s\n", _("behind master"));
578 printf (" %s\n", _("Use ssl encryption")); 700 printf(" %s\n", "-c, --critical");
579 printf (" %s\n", "-C, --ca-cert=STRING"); 701 printf(" %s\n",
580 printf (" %s\n", _("Path to CA signing the cert")); 702 _("Exit with CRITICAL status if replica server is more then INTEGER seconds"));
581 printf (" %s\n", "-a, --cert=STRING"); 703 printf(" %s\n", _("behind master"));
582 printf (" %s\n", _("Path to SSL certificate")); 704 printf(" %s\n", "-l, --ssl");
583 printf (" %s\n", "-k, --key=STRING"); 705 printf(" %s\n", _("Use ssl encryption"));
584 printf (" %s\n", _("Path to private SSL key")); 706 printf(" %s\n", "-C, --ca-cert=STRING");
585 printf (" %s\n", "-D, --ca-dir=STRING"); 707 printf(" %s\n", _("Path to CA signing the cert"));
586 printf (" %s\n", _("Path to CA directory")); 708 printf(" %s\n", "-a, --cert=STRING");
587 printf (" %s\n", "-L, --ciphers=STRING"); 709 printf(" %s\n", _("Path to SSL certificate"));
588 printf (" %s\n", _("List of valid SSL ciphers")); 710 printf(" %s\n", "-k, --key=STRING");
589 711 printf(" %s\n", _("Path to private SSL key"));
590 712 printf(" %s\n", "-D, --ca-dir=STRING");
591 printf ("\n"); 713 printf(" %s\n", _("Path to CA directory"));
592 printf (" %s\n", _("There are no required arguments. By default, the local database is checked")); 714 printf(" %s\n", "-L, --ciphers=STRING");
593 printf (" %s\n", _("using the default unix socket. You can force TCP on localhost by using an")); 715 printf(" %s\n", _("List of valid SSL ciphers"));
594 printf (" %s\n", _("IP address or FQDN ('localhost' will use the socket as well).")); 716
595 717 printf(UT_OUTPUT_FORMAT);
596 printf ("\n"); 718
597 printf ("%s\n", _("Notes:")); 719 printf("\n");
598 printf (" %s\n", _("You must specify -p with an empty string to force an empty password,")); 720 printf(" %s\n",
599 printf (" %s\n", _("overriding any my.cnf settings.")); 721 _("There are no required arguments. By default, the local database is checked"));
600 722 printf(" %s\n", _("using the default unix socket. You can force TCP on localhost by using an"));
601 printf (UT_SUPPORT); 723 printf(" %s\n", _("IP address or FQDN ('localhost' will use the socket as well)."));
724
725 printf("\n");
726 printf("%s\n", _("Notes:"));
727 printf(" %s\n", _("You must specify -p with an empty string to force an empty password,"));
728 printf(" %s\n", _("overriding any my.cnf settings."));
729
730 printf(UT_SUPPORT);
602} 731}
603 732
604 733void print_usage(void) {
605void 734 printf("%s\n", _("Usage:"));
606print_usage (void) 735 printf(" %s [-d database] [-H host] [-P port] [-s socket]\n", progname);
607{ 736 printf(" [-u user] [-p password] [-S] [-l] [-a cert] [-k key]\n");
608 printf ("%s\n", _("Usage:")); 737 printf(" [-C ca-cert] [-D ca-dir] [-L ciphers] [-f optfile] [-g group]\n");
609 printf (" %s [-d database] [-H host] [-P port] [-s socket]\n",progname);
610 printf (" [-u user] [-p password] [-S] [-l] [-a cert] [-k key]\n");
611 printf (" [-C ca-cert] [-D ca-dir] [-L ciphers] [-f optfile] [-g group]\n");
612} 738}
diff --git a/plugins/check_mysql.d/config.h b/plugins/check_mysql.d/config.h
new file mode 100644
index 00000000..1d8c82bb
--- /dev/null
+++ b/plugins/check_mysql.d/config.h
@@ -0,0 +1,59 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
6#include <stddef.h>
7#include <mysql.h>
8
9typedef struct {
10 char *db_host;
11 unsigned int db_port;
12 char *db_user;
13 char *db_socket;
14 char *db_pass;
15 char *db;
16 char *ca_cert;
17 char *ca_dir;
18 char *cert;
19 char *key;
20 char *ciphers;
21 bool ssl;
22 char *opt_file;
23 char *opt_group;
24
25 bool check_replica;
26 bool ignore_auth;
27
28 mp_thresholds replica_thresholds;
29
30 bool output_format_is_set;
31 mp_output_format output_format;
32} check_mysql_config;
33
34check_mysql_config check_mysql_config_init() {
35 check_mysql_config tmp = {
36 .db_host = NULL,
37 .db_port = MYSQL_PORT,
38 .db = NULL,
39 .db_pass = NULL,
40 .db_socket = NULL,
41 .db_user = NULL,
42 .ca_cert = NULL,
43 .ca_dir = NULL,
44 .cert = NULL,
45 .key = NULL,
46 .ciphers = NULL,
47 .ssl = false,
48 .opt_file = NULL,
49 .opt_group = NULL,
50
51 .check_replica = false,
52 .ignore_auth = false,
53
54 .replica_thresholds = mp_thresholds_init(),
55
56 .output_format_is_set = false,
57 };
58 return tmp;
59}
diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c
index 842b7a2f..fc0966d3 100644
--- a/plugins/check_mysql_query.c
+++ b/plugins/check_mysql_query.c
@@ -1,234 +1,252 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_mysql_query plugin 3 * Monitoring check_mysql_query plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006-2009 Monitoring Plugins Development Team 6 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
7* Original code from check_mysql, copyright 1999 Didi Rieder 7 * Original code from check_mysql, copyright 1999 Didi Rieder
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_mysql_query plugin 11 * This file contains the check_mysql_query plugin
12* 12 *
13* This plugin is for running arbitrary SQL and checking the results 13 * This plugin is for running arbitrary SQL and checking the results
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31
32const char *progname = "check_mysql_query";
33const char *copyright = "1999-2007";
34const char *email = "devel@monitoring-plugins.org";
35 31
36#include "common.h" 32#include "common.h"
33#include "output.h"
34#include "perfdata.h"
35#include "states.h"
36#include "thresholds.h"
37#include "utils.h" 37#include "utils.h"
38#include "utils_base.h" 38#include "utils_base.h"
39#include "netutils.h" 39#include "netutils.h"
40#include "check_mysql_query.d/config.h"
40 41
41#include <mysql.h> 42#include <mysql.h>
42#include <errmsg.h> 43#include <errmsg.h>
43 44
44char *db_user = NULL; 45const char *progname = "check_mysql_query";
45char *db_host = NULL; 46const char *copyright = "1999-2024";
46char *db_socket = NULL; 47const char *email = "devel@monitoring-plugins.org";
47char *db_pass = NULL;
48char *db = NULL;
49char *opt_file = NULL;
50char *opt_group = NULL;
51unsigned int db_port = MYSQL_PORT;
52
53int process_arguments (int, char **);
54int validate_arguments (void);
55void print_help (void);
56void print_usage (void);
57 48
58char *sql_query = NULL; 49typedef struct {
59int verbose = 0; 50 int errorcode;
60thresholds *my_thresholds = NULL; 51 check_mysql_query_config config;
52} check_mysql_query_config_wrapper;
53static check_mysql_query_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
54static check_mysql_query_config_wrapper
55 validate_arguments(check_mysql_query_config_wrapper /*config_wrapper*/);
56static void print_help(void);
57void print_usage(void);
61 58
59static int verbose = 0;
62 60
63int 61int main(int argc, char **argv) {
64main (int argc, char **argv) 62 setlocale(LC_ALL, "");
65{ 63 bindtextdomain(PACKAGE, LOCALEDIR);
64 textdomain(PACKAGE);
66 65
67 MYSQL mysql; 66 /* Parse extra opts if any */
68 MYSQL_RES *res; 67 argv = np_extra_opts(&argc, argv, progname);
69 MYSQL_ROW row;
70
71 double value;
72 char *error = NULL;
73 int status;
74 68
75 setlocale (LC_ALL, ""); 69 check_mysql_query_config_wrapper tmp_config = process_arguments(argc, argv);
76 bindtextdomain (PACKAGE, LOCALEDIR); 70 if (tmp_config.errorcode == ERROR) {
77 textdomain (PACKAGE); 71 usage4(_("Could not parse arguments"));
72 }
78 73
79 /* Parse extra opts if any */ 74 const check_mysql_query_config config = tmp_config.config;
80 argv=np_extra_opts (&argc, argv, progname);
81 75
82 if (process_arguments (argc, argv) == ERROR) 76 if (config.output_format_is_set) {
83 usage4 (_("Could not parse arguments")); 77 mp_set_format(config.output_format);
78 }
84 79
80 MYSQL mysql;
85 /* initialize mysql */ 81 /* initialize mysql */
86 mysql_init (&mysql); 82 mysql_init(&mysql);
87 83
88 if (opt_file != NULL) 84 if (config.opt_file != NULL) {
89 mysql_options(&mysql,MYSQL_READ_DEFAULT_FILE,opt_file); 85 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file);
86 }
90 87
91 if (opt_group != NULL) 88 if (config.opt_group != NULL) {
92 mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,opt_group); 89 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group);
93 else 90 } else {
94 mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"client"); 91 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client");
92 }
93
94 mp_check overall = mp_check_init();
95 mp_subcheck sc_connect = mp_subcheck_init();
95 96
96 /* establish a connection to the server and error checking */ 97 /* establish a connection to the server and error checking */
97 if (!mysql_real_connect(&mysql,db_host,db_user,db_pass,db,db_port,db_socket,0)) { 98 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db,
98 if (mysql_errno (&mysql) == CR_UNKNOWN_HOST) 99 config.db_port, config.db_socket, 0)) {
99 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 100 xasprintf(&sc_connect.output, "query failed: %s", mysql_error(&mysql));
100 else if (mysql_errno (&mysql) == CR_VERSION_ERROR) 101
101 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 102 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
102 else if (mysql_errno (&mysql) == CR_OUT_OF_MEMORY) 103 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
103 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 104 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
104 else if (mysql_errno (&mysql) == CR_IPSOCK_ERROR) 105 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
105 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 106 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
106 else if (mysql_errno (&mysql) == CR_SOCKET_CREATE_ERROR) 107 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
107 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 108 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
108 else 109 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
109 die (STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error (&mysql)); 110 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
111 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
112 } else {
113 sc_connect = mp_set_subcheck_state(sc_connect, STATE_CRITICAL);
114 }
115
116 mp_add_subcheck_to_check(&overall, sc_connect);
117 mp_exit(overall);
110 } 118 }
111 119
112 if (mysql_query (&mysql, sql_query) != 0) { 120 sc_connect = mp_set_subcheck_state(sc_connect, STATE_OK);
113 error = strdup(mysql_error(&mysql)); 121 xasprintf(&sc_connect.output, "query succeeded");
114 mysql_close (&mysql); 122 mp_add_subcheck_to_check(&overall, sc_connect);
115 die (STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error); 123
124 if (mysql_query(&mysql, config.sql_query) != 0) {
125 char *error = strdup(mysql_error(&mysql));
126 mysql_close(&mysql);
127 die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error);
116 } 128 }
117 129
130 MYSQL_RES *res;
118 /* store the result */ 131 /* store the result */
119 if ( (res = mysql_store_result (&mysql)) == NULL) { 132 if ((res = mysql_store_result(&mysql)) == NULL) {
120 error = strdup(mysql_error(&mysql)); 133 char *error = strdup(mysql_error(&mysql));
121 mysql_close (&mysql); 134 mysql_close(&mysql);
122 die (STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error); 135 die(STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error);
123 } 136 }
124 137
125 /* Check there is some data */ 138 /* Check there is some data */
126 if (mysql_num_rows(res) == 0) { 139 if (mysql_num_rows(res) == 0) {
127 mysql_close(&mysql); 140 mysql_close(&mysql);
128 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned")); 141 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned"));
129 } 142 }
130 143
144 mp_subcheck sc_value = mp_subcheck_init();
145 MYSQL_ROW row;
131 /* fetch the first row */ 146 /* fetch the first row */
132 if ( (row = mysql_fetch_row (res)) == NULL) { 147 if ((row = mysql_fetch_row(res)) == NULL) {
133 error = strdup(mysql_error(&mysql)); 148 xasprintf(&sc_value.output, "fetch row error - %s", mysql_error(&mysql));
134 mysql_free_result (res); 149 mysql_free_result(res);
135 mysql_close (&mysql); 150 mysql_close(&mysql);
136 die (STATE_CRITICAL, "QUERY %s: Fetch row error - %s\n", _("CRITICAL"), error); 151
152 sc_value = mp_set_subcheck_state(sc_value, STATE_CRITICAL);
153 mp_add_subcheck_to_check(&overall, sc_value);
154 mp_exit(overall);
137 } 155 }
138 156
139 if (! is_numeric(row[0])) { 157 if (!is_numeric(row[0])) {
140 die (STATE_CRITICAL, "QUERY %s: %s - '%s'\n", _("CRITICAL"), _("Is not a numeric"), row[0]); 158 xasprintf(&sc_value.output, "query result is not numeric");
159 sc_value = mp_set_subcheck_state(sc_value, STATE_CRITICAL);
160 mp_add_subcheck_to_check(&overall, sc_value);
161 mp_exit(overall);
141 } 162 }
142 163
143 value = strtod(row[0], NULL); 164 double value = strtod(row[0], NULL);
144 165
145 /* free the result */ 166 /* free the result */
146 mysql_free_result (res); 167 mysql_free_result(res);
147 168
148 /* close the connection */ 169 /* close the connection */
149 mysql_close (&mysql); 170 mysql_close(&mysql);
150 171
151 if (verbose >= 3) 172 if (verbose >= 3) {
152 printf("mysql result: %f\n", value); 173 printf("mysql result: %f\n", value);
174 }
153 175
154 status = get_status(value, my_thresholds); 176 mp_perfdata pd_query_result = perfdata_init();
177 pd_query_result = mp_set_pd_value(pd_query_result, value);
178 pd_query_result = mp_pd_set_thresholds(pd_query_result, config.thresholds);
179 pd_query_result.label = "result";
180 mp_add_perfdata_to_subcheck(&sc_value, pd_query_result);
155 181
156 if (status == STATE_OK) { 182 sc_value = mp_set_subcheck_state(sc_value, mp_get_pd_status(pd_query_result));
157 printf("QUERY %s: ", _("OK")); 183 xasprintf(&sc_value.output, "'%s' returned '%f'", config.sql_query, value);
158 } else if (status == STATE_WARNING) {
159 printf("QUERY %s: ", _("WARNING"));
160 } else if (status == STATE_CRITICAL) {
161 printf("QUERY %s: ", _("CRITICAL"));
162 }
163 printf(_("'%s' returned %f | %s"), sql_query, value,
164 fperfdata("result", value, "",
165 my_thresholds->warning?true:false, my_thresholds->warning?my_thresholds->warning->end:0,
166 my_thresholds->critical?true:false, my_thresholds->critical?my_thresholds->critical->end:0,
167 false, 0,
168 false, 0)
169 );
170 printf("\n");
171 184
172 return status; 185 mp_add_subcheck_to_check(&overall, sc_value);
173}
174 186
187 mp_exit(overall);
188}
175 189
176/* process command-line arguments */ 190/* process command-line arguments */
177int 191check_mysql_query_config_wrapper process_arguments(int argc, char **argv) {
178process_arguments (int argc, char **argv) 192 enum {
179{ 193 output_format_index = CHAR_MAX + 1,
180 int c;
181 char *warning = NULL;
182 char *critical = NULL;
183
184 int option = 0;
185 static struct option longopts[] = {
186 {"hostname", required_argument, 0, 'H'},
187 {"socket", required_argument, 0, 's'},
188 {"database", required_argument, 0, 'd'},
189 {"username", required_argument, 0, 'u'},
190 {"password", required_argument, 0, 'p'},
191 {"file", required_argument, 0, 'f'},
192 {"group", required_argument, 0, 'g'},
193 {"port", required_argument, 0, 'P'},
194 {"verbose", no_argument, 0, 'v'},
195 {"version", no_argument, 0, 'V'},
196 {"help", no_argument, 0, 'h'},
197 {"query", required_argument, 0, 'q'},
198 {"warning", required_argument, 0, 'w'},
199 {"critical", required_argument, 0, 'c'},
200 {0, 0, 0, 0}
201 }; 194 };
202 195
203 if (argc < 1) 196 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
204 return ERROR; 197 {"socket", required_argument, 0, 's'},
198 {"database", required_argument, 0, 'd'},
199 {"username", required_argument, 0, 'u'},
200 {"password", required_argument, 0, 'p'},
201 {"file", required_argument, 0, 'f'},
202 {"group", required_argument, 0, 'g'},
203 {"port", required_argument, 0, 'P'},
204 {"verbose", no_argument, 0, 'v'},
205 {"version", no_argument, 0, 'V'},
206 {"help", no_argument, 0, 'h'},
207 {"query", required_argument, 0, 'q'},
208 {"warning", required_argument, 0, 'w'},
209 {"critical", required_argument, 0, 'c'},
210 {"output-format", required_argument, 0, output_format_index},
211 {0, 0, 0, 0}};
212
213 check_mysql_query_config_wrapper result = {
214 .errorcode = OK,
215 .config = check_mysql_query_config_init(),
216 };
217
218 if (argc < 1) {
219 result.errorcode = ERROR;
220 return result;
221 }
205 222
206 while (1) { 223 while (true) {
207 c = getopt_long (argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option); 224 int option = 0;
225 int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option);
208 226
209 if (c == -1 || c == EOF) 227 if (option_char == -1 || option_char == EOF) {
210 break; 228 break;
229 }
211 230
212 switch (c) { 231 switch (option_char) {
213 case 'H': /* hostname */ 232 case 'H': /* hostname */
214 if (is_host (optarg)) { 233 if (is_host(optarg)) {
215 db_host = optarg; 234 result.config.db_host = optarg;
216 } 235 } else {
217 else { 236 usage2(_("Invalid hostname/address"), optarg);
218 usage2 (_("Invalid hostname/address"), optarg);
219 } 237 }
220 break; 238 break;
221 case 's': /* socket */ 239 case 's': /* socket */
222 db_socket = optarg; 240 result.config.db_socket = optarg;
223 break; 241 break;
224 case 'd': /* database */ 242 case 'd': /* database */
225 db = optarg; 243 result.config.db = optarg;
226 break; 244 break;
227 case 'u': /* username */ 245 case 'u': /* username */
228 db_user = optarg; 246 result.config.db_user = optarg;
229 break; 247 break;
230 case 'p': /* authentication information: password */ 248 case 'p': /* authentication information: password */
231 db_pass = strdup(optarg); 249 result.config.db_pass = strdup(optarg);
232 250
233 /* Delete the password from process list */ 251 /* Delete the password from process list */
234 while (*optarg != '\0') { 252 while (*optarg != '\0') {
@@ -236,119 +254,132 @@ process_arguments (int argc, char **argv)
236 optarg++; 254 optarg++;
237 } 255 }
238 break; 256 break;
239 case 'f': /* client options file */ 257 case 'f': /* client options file */
240 opt_file = optarg; 258 result.config.opt_file = optarg;
241 break; 259 break;
242 case 'g': /* client options group */ 260 case 'g': /* client options group */
243 opt_group = optarg; 261 result.config.opt_group = optarg;
244 break; 262 break;
245 case 'P': /* critical time threshold */ 263 case 'P': /* critical time threshold */
246 db_port = atoi (optarg); 264 result.config.db_port = atoi(optarg);
247 break; 265 break;
248 case 'v': 266 case 'v':
249 verbose++; 267 verbose++;
250 break; 268 break;
251 case 'V': /* version */ 269 case 'V': /* version */
252 print_revision (progname, NP_VERSION); 270 print_revision(progname, NP_VERSION);
253 exit (STATE_UNKNOWN); 271 exit(STATE_UNKNOWN);
254 case 'h': /* help */ 272 case 'h': /* help */
255 print_help (); 273 print_help();
256 exit (STATE_UNKNOWN); 274 exit(STATE_UNKNOWN);
257 case 'q': 275 case 'q':
258 xasprintf(&sql_query, "%s", optarg); 276 xasprintf(&result.config.sql_query, "%s", optarg);
259 break;
260 case 'w':
261 warning = optarg;
262 break; 277 break;
263 case 'c': 278 case 'w': {
264 critical = optarg; 279 mp_range_parsed tmp = mp_parse_range_string(optarg);
280 if (tmp.error != MP_PARSING_SUCCESS) {
281 die(STATE_UNKNOWN, "failed to parse warning threshold");
282 }
283 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range);
284 } break;
285 case 'c': {
286 mp_range_parsed tmp = mp_parse_range_string(optarg);
287 if (tmp.error != MP_PARSING_SUCCESS) {
288 die(STATE_UNKNOWN, "failed to parse critical threshold");
289 }
290 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range);
291 } break;
292 case '?': /* help */
293 usage5();
294 case output_format_index: {
295 parsed_output_format parser = mp_parse_output_format(optarg);
296 if (!parser.parsing_success) {
297 printf("Invalid output format: %s\n", optarg);
298 exit(STATE_UNKNOWN);
299 }
300
301 result.config.output_format_is_set = true;
302 result.config.output_format = parser.output_format;
265 break; 303 break;
266 case '?': /* help */
267 usage5 ();
268 } 304 }
305 }
306 }
307
308 return validate_arguments(result);
309}
310
311check_mysql_query_config_wrapper
312validate_arguments(check_mysql_query_config_wrapper config_wrapper) {
313 if (config_wrapper.config.sql_query == NULL) {
314 usage("Must specify a SQL query to run");
315 }
316
317 if (config_wrapper.config.db_user == NULL) {
318 config_wrapper.config.db_user = strdup("");
269 } 319 }
270 320
271 c = optind; 321 if (config_wrapper.config.db_host == NULL) {
322 config_wrapper.config.db_host = strdup("");
323 }
272 324
273 set_thresholds(&my_thresholds, warning, critical); 325 if (config_wrapper.config.db == NULL) {
326 config_wrapper.config.db = strdup("");
327 }
274 328
275 return validate_arguments (); 329 return config_wrapper;
276} 330}
277 331
332void print_help(void) {
333 char *myport;
334 xasprintf(&myport, "%d", MYSQL_PORT);
278 335
279int 336 print_revision(progname, NP_VERSION);
280validate_arguments (void)
281{
282 if (sql_query == NULL)
283 usage("Must specify a SQL query to run");
284 337
285 if (db_user == NULL) 338 printf(_(COPYRIGHT), copyright, email);
286 db_user = strdup("");
287 339
288 if (db_host == NULL) 340 printf("%s\n", _("This program checks a query result against threshold levels"));
289 db_host = strdup("");
290 341
291 if (db == NULL) 342 printf("\n\n");
292 db = strdup("");
293 343
294 return OK; 344 print_usage();
295}
296 345
346 printf(UT_HELP_VRSN);
347 printf(UT_EXTRA_OPTS);
348 printf(" -q, --query=STRING\n");
349 printf(" %s\n", _("SQL query to run. Only first column in first row will be read"));
350 printf(UT_WARN_CRIT_RANGE);
351 printf(UT_HOST_PORT, 'P', myport);
352 printf(" %s\n", "-s, --socket=STRING");
353 printf(" %s\n", _("Use the specified socket (has no effect if -H is used)"));
354 printf(" -d, --database=STRING\n");
355 printf(" %s\n", _("Database to check"));
356 printf(" %s\n", "-f, --file=STRING");
357 printf(" %s\n", _("Read from the specified client options file"));
358 printf(" %s\n", "-g, --group=STRING");
359 printf(" %s\n", _("Use a client options group"));
360 printf(" -u, --username=STRING\n");
361 printf(" %s\n", _("Username to login with"));
362 printf(" -p, --password=STRING\n");
363 printf(" %s\n", _("Password to login with"));
364 printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!"));
365 printf(" %s\n", _("Your clear-text password could be visible as a process table entry"));
297 366
298void 367 printf(UT_OUTPUT_FORMAT);
299print_help (void) 368
300{ 369 printf("\n");
301 char *myport; 370 printf(" %s\n", _("A query is required. The result from the query should be numeric."));
302 xasprintf (&myport, "%d", MYSQL_PORT); 371 printf(" %s\n", _("For extra security, create a user with minimal access."));
303
304 print_revision (progname, NP_VERSION);
305
306 printf (_(COPYRIGHT), copyright, email);
307
308 printf ("%s\n", _("This program checks a query result against threshold levels"));
309
310 printf ("\n\n");
311
312 print_usage ();
313
314 printf (UT_HELP_VRSN);
315 printf (UT_EXTRA_OPTS);
316 printf (" -q, --query=STRING\n");
317 printf (" %s\n", _("SQL query to run. Only first column in first row will be read"));
318 printf (UT_WARN_CRIT_RANGE);
319 printf (UT_HOST_PORT, 'P', myport);
320 printf (" %s\n", "-s, --socket=STRING");
321 printf (" %s\n", _("Use the specified socket (has no effect if -H is used)"));
322 printf (" -d, --database=STRING\n");
323 printf (" %s\n", _("Database to check"));
324 printf (" %s\n", "-f, --file=STRING");
325 printf (" %s\n", _("Read from the specified client options file"));
326 printf (" %s\n", "-g, --group=STRING");
327 printf (" %s\n", _("Use a client options group"));
328 printf (" -u, --username=STRING\n");
329 printf (" %s\n", _("Username to login with"));
330 printf (" -p, --password=STRING\n");
331 printf (" %s\n", _("Password to login with"));
332 printf (" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!"));
333 printf (" %s\n", _("Your clear-text password could be visible as a process table entry"));
334
335 printf ("\n");
336 printf (" %s\n", _("A query is required. The result from the query should be numeric."));
337 printf (" %s\n", _("For extra security, create a user with minimal access."));
338
339 printf ("\n");
340 printf ("%s\n", _("Notes:"));
341 printf (" %s\n", _("You must specify -p with an empty string to force an empty password,"));
342 printf (" %s\n", _("overriding any my.cnf settings."));
343
344 printf (UT_SUPPORT);
345}
346 372
373 printf("\n");
374 printf("%s\n", _("Notes:"));
375 printf(" %s\n", _("You must specify -p with an empty string to force an empty password,"));
376 printf(" %s\n", _("overriding any my.cnf settings."));
377
378 printf(UT_SUPPORT);
379}
347 380
348void 381void print_usage(void) {
349print_usage (void) 382 printf("%s\n", _("Usage:"));
350{ 383 printf(" %s -q SQL_query [-w warn] [-c crit] [-H host] [-P port] [-s socket]\n", progname);
351 printf ("%s\n", _("Usage:")); 384 printf(" [-d database] [-u user] [-p password] [-f optfile] [-g group]\n");
352 printf (" %s -q SQL_query [-w warn] [-c crit] [-H host] [-P port] [-s socket]\n",progname);
353 printf (" [-d database] [-u user] [-p password] [-f optfile] [-g group]\n");
354} 385}
diff --git a/plugins/check_mysql_query.d/config.h b/plugins/check_mysql_query.d/config.h
new file mode 100644
index 00000000..32ab455a
--- /dev/null
+++ b/plugins/check_mysql_query.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
6#include <mysql.h>
7
8typedef struct {
9 char *db_host;
10 char *db_socket;
11 char *db;
12 char *db_user;
13 char *db_pass;
14 char *opt_file;
15 char *opt_group;
16 unsigned int db_port;
17
18 char *sql_query;
19 mp_thresholds thresholds;
20
21 bool output_format_is_set;
22 mp_output_format output_format;
23} check_mysql_query_config;
24
25check_mysql_query_config check_mysql_query_config_init() {
26 check_mysql_query_config tmp = {
27 .db_host = NULL,
28 .db_socket = NULL,
29 .db = NULL,
30 .db_user = NULL,
31 .db_pass = NULL,
32 .opt_file = NULL,
33 .opt_group = NULL,
34 .db_port = MYSQL_PORT,
35
36 .sql_query = NULL,
37 .thresholds = mp_thresholds_init(),
38
39 .output_format_is_set = false,
40 };
41 return tmp;
42}
diff --git a/plugins/check_nagios.c b/plugins/check_nagios.c
index 40d68f03..e2f230c9 100644
--- a/plugins/check_nagios.c
+++ b/plugins/check_nagios.c
@@ -1,325 +1,317 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_nagios plugin 3 * Monitoring check_nagios plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 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_nagios plugin 10 * This file contains the check_nagios plugin
11* 11 *
12* This plugin checks the status of the Nagios process on the local machine. 12 * This plugin checks the status of the Nagios process on the local machine.
13* The plugin will check to make sure the Nagios status log is no older than 13 * The plugin will check to make sure the Nagios status log is no older than
14* the number of minutes specified by the expires option. 14 * the number of minutes specified by the expires option.
15* It also checks the process table for a process matching the command 15 * It also checks the process table for a process matching the command
16* argument. 16 * argument.
17* 17 *
18* 18 *
19* This program is free software: you can redistribute it and/or modify 19 * This program is free software: you can redistribute it and/or modify
20* it under the terms of the GNU General Public License as published by 20 * it under the terms of the GNU General Public License as published by
21* the Free Software Foundation, either version 3 of the License, or 21 * the Free Software Foundation, either version 3 of the License, or
22* (at your option) any later version. 22 * (at your option) any later version.
23* 23 *
24* This program is distributed in the hope that it will be useful, 24 * This program is distributed in the hope that it will be useful,
25* but WITHOUT ANY WARRANTY; without even the implied warranty of 25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27* GNU General Public License for more details. 27 * GNU General Public License for more details.
28* 28 *
29* You should have received a copy of the GNU General Public License 29 * You should have received a copy of the GNU General Public License
30* along with this program. If not, see <http://www.gnu.org/licenses/>. 30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31* 31 *
32* 32 *
33*****************************************************************************/ 33 *****************************************************************************/
34 34
35const char *progname = "check_nagios"; 35const char *progname = "check_nagios";
36const char *copyright = "1999-2007"; 36const char *copyright = "1999-2024";
37const char *email = "devel@monitoring-plugins.org"; 37const char *email = "devel@monitoring-plugins.org";
38 38
39#include "common.h" 39#include "common.h"
40#include "runcmd.h" 40#include "runcmd.h"
41#include "utils.h" 41#include "utils.h"
42#include "states.h"
43#include "check_nagios.d/config.h"
42 44
43int process_arguments (int, char **); 45typedef struct {
44void print_help (void); 46 int errorcode;
45void print_usage (void); 47 check_nagios_config config;
48} check_nagios_config_wrapper;
49static check_nagios_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50static void print_help(void);
51void print_usage(void);
46 52
47char *status_log = NULL; 53static int verbose = 0;
48char *process_string = NULL;
49int expire_minutes = 0;
50 54
51int verbose = 0; 55int main(int argc, char **argv) {
52 56 setlocale(LC_ALL, "");
53int 57 bindtextdomain(PACKAGE, LOCALEDIR);
54main (int argc, char **argv) 58 textdomain(PACKAGE);
55{
56 int result = STATE_UNKNOWN;
57 char input_buffer[MAX_INPUT_BUFFER];
58 unsigned long latest_entry_time = 0L;
59 unsigned long temp_entry_time = 0L;
60 int proc_entries = 0;
61 time_t current_time;
62 char *temp_ptr;
63 FILE *fp;
64 int procuid = 0;
65 int procpid = 0;
66 int procppid = 0;
67 int procvsz = 0;
68 int procrss = 0;
69 float procpcpu = 0;
70 char procstat[8];
71#ifdef PS_USES_PROCETIME
72 char procetime[MAX_INPUT_BUFFER];
73#endif /* PS_USES_PROCETIME */
74 char procprog[MAX_INPUT_BUFFER];
75 char *procargs;
76 int pos, cols;
77 int expected_cols = PS_COLS - 1;
78 const char *zombie = "Z";
79 char *temp_string;
80 output chld_out, chld_err;
81 size_t i;
82
83 setlocale (LC_ALL, "");
84 bindtextdomain (PACKAGE, LOCALEDIR);
85 textdomain (PACKAGE);
86 59
87 /* Parse extra opts if any */ 60 /* Parse extra opts if any */
88 argv=np_extra_opts (&argc, argv, progname); 61 argv = np_extra_opts(&argc, argv, progname);
62
63 check_nagios_config_wrapper tmp_config = process_arguments(argc, argv);
89 64
90 if (process_arguments (argc, argv) == ERROR) 65 if (tmp_config.errorcode == ERROR) {
91 usage_va(_("Could not parse arguments")); 66 usage_va(_("Could not parse arguments"));
67 }
68
69 const check_nagios_config config = tmp_config.config;
92 70
93 /* Set signal handling and alarm timeout */ 71 /* Set signal handling and alarm timeout */
94 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 72 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
95 usage_va(_("Cannot catch SIGALRM")); 73 usage_va(_("Cannot catch SIGALRM"));
96 } 74 }
97 75
98 /* handle timeouts gracefully... */ 76 /* handle timeouts gracefully... */
99 alarm (timeout_interval); 77 alarm(timeout_interval);
100 78
101 /* open the status log */ 79 /* open the status log */
102 fp = fopen (status_log, "r"); 80 FILE *log_file = fopen(config.status_log, "r");
103 if (fp == NULL) { 81 if (log_file == NULL) {
104 die (STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot open status log for reading!")); 82 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot open status log for reading!"));
105 } 83 }
106 84
85 unsigned long latest_entry_time = 0L;
86 unsigned long temp_entry_time = 0L;
87 char input_buffer[MAX_INPUT_BUFFER];
88 char *temp_ptr;
107 /* get the date/time of the last item updated in the log */ 89 /* get the date/time of the last item updated in the log */
108 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) { 90 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, log_file)) {
109 if ((temp_ptr = strstr (input_buffer, "created=")) != NULL) { 91 if ((temp_ptr = strstr(input_buffer, "created=")) != NULL) {
110 temp_entry_time = strtoul (temp_ptr + 8, NULL, 10); 92 temp_entry_time = strtoul(temp_ptr + 8, NULL, 10);
111 latest_entry_time = temp_entry_time; 93 latest_entry_time = temp_entry_time;
112 break; 94 break;
113 } else if ((temp_ptr = strtok (input_buffer, "]")) != NULL) { 95 }
114 temp_entry_time = strtoul (temp_ptr + 1, NULL, 10); 96 if ((temp_ptr = strtok(input_buffer, "]")) != NULL) {
115 if (temp_entry_time > latest_entry_time) 97 temp_entry_time = strtoul(temp_ptr + 1, NULL, 10);
98 if (temp_entry_time > latest_entry_time) {
116 latest_entry_time = temp_entry_time; 99 latest_entry_time = temp_entry_time;
100 }
117 } 101 }
118 } 102 }
119 fclose (fp); 103 fclose(log_file);
120 104
121 if (verbose >= 2) 105 if (verbose >= 2) {
122 printf("command: %s\n", PS_COMMAND); 106 printf("command: %s\n", PS_COMMAND);
107 }
123 108
124 /* run the command to check for the Nagios process.. */ 109 /* run the command to check for the Nagios process.. */
125 if((result = np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0)) != 0) 110 mp_state_enum result = STATE_UNKNOWN;
111 output chld_out;
112 output chld_err;
113 if ((result = np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0)) != 0) {
126 result = STATE_WARNING; 114 result = STATE_WARNING;
115 }
127 116
117 int procuid = 0;
118 int procpid = 0;
119 int procppid = 0;
120 int procvsz = 0;
121 int procrss = 0;
122 int proc_entries = 0;
123 float procpcpu = 0;
124 char procstat[8];
125 char procprog[MAX_INPUT_BUFFER];
126 char *procargs;
127#ifdef PS_USES_PROCETIME
128 char procetime[MAX_INPUT_BUFFER];
129#endif /* PS_USES_PROCETIME */
130 int pos;
131 int expected_cols = PS_COLS - 1;
132 const char *zombie = "Z";
128 /* count the number of matching Nagios processes... */ 133 /* count the number of matching Nagios processes... */
129 for(i = 0; i < chld_out.lines; i++) { 134 for (size_t i = 0; i < chld_out.lines; i++) {
130 cols = sscanf (chld_out.line[i], PS_FORMAT, PS_VARLIST); 135 int cols = sscanf(chld_out.line[i], PS_FORMAT, PS_VARLIST);
131 /* Zombie processes do not give a procprog command */ 136 /* Zombie processes do not give a procprog command */
132 if ( cols == (expected_cols - 1) && strstr(procstat, zombie) ) { 137 if (cols == (expected_cols - 1) && strstr(procstat, zombie)) {
133 cols = expected_cols; 138 cols = expected_cols;
134 /* Set some value for procargs for the strip command further below 139 /* Set some value for procargs for the strip command further below
135 * Seen to be a problem on some Solaris 7 and 8 systems */ 140 * Seen to be a problem on some Solaris 7 and 8 systems */
136 chld_out.line[i][pos] = '\n'; 141 chld_out.line[i][pos] = '\n';
137 chld_out.line[i][pos+1] = 0x0; 142 chld_out.line[i][pos + 1] = 0x0;
138 } 143 }
139 if ( cols >= expected_cols ) { 144 if (cols >= expected_cols) {
140 xasprintf (&procargs, "%s", chld_out.line[i] + pos); 145 xasprintf(&procargs, "%s", chld_out.line[i] + pos);
141 strip (procargs); 146 strip(procargs);
142 147
143 /* Some ps return full pathname for command. This removes path */ 148 /* Some ps return full pathname for command. This removes path */
144 temp_string = strtok ((char *)procprog, "/"); 149 char *temp_string = strtok((char *)procprog, "/");
145 while (temp_string) { 150 while (temp_string) {
146 strcpy(procprog, temp_string); 151 strcpy(procprog, temp_string);
147 temp_string = strtok (NULL, "/"); 152 temp_string = strtok(NULL, "/");
148 } 153 }
149 154
150 /* May get empty procargs */ 155 /* May get empty procargs */
151 if (!strstr(procargs, argv[0]) && strstr(procargs, process_string) && strcmp(procargs,"")) { 156 if (!strstr(procargs, argv[0]) && strstr(procargs, config.process_string) && strcmp(procargs, "")) {
152 proc_entries++; 157 proc_entries++;
153 if (verbose >= 2) { 158 if (verbose >= 2) {
154 printf (_("Found process: %s %s\n"), procprog, procargs); 159 printf(_("Found process: %s %s\n"), procprog, procargs);
155 } 160 }
156 } 161 }
157 } 162 }
158 } 163 }
159 164
160 /* If we get anything on stderr, at least set warning */ 165 /* If we get anything on stderr, at least set warning */
161 if(chld_err.buflen) 166 if (chld_err.buflen) {
162 result = max_state (result, STATE_WARNING); 167 result = max_state(result, STATE_WARNING);
168 }
163 169
164 /* reset the alarm handler */ 170 /* reset the alarm handler */
165 alarm (0); 171 alarm(0);
166 172
167 if (proc_entries == 0) { 173 if (proc_entries == 0) {
168 die (STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Could not locate a running Nagios process!")); 174 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Could not locate a running Nagios process!"));
169 } 175 }
170 176
171 if (latest_entry_time == 0L) { 177 if (latest_entry_time == 0L) {
172 die (STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot parse Nagios log file for valid time")); 178 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot parse Nagios log file for valid time"));
173 } 179 }
174 180
175 time (&current_time); 181 time_t current_time;
176 if ((int)(current_time - latest_entry_time) > (expire_minutes * 60)) { 182 time(&current_time);
183 if ((int)(current_time - latest_entry_time) > (config.expire_minutes * 60)) {
177 result = STATE_WARNING; 184 result = STATE_WARNING;
178 } else { 185 } else {
179 result = STATE_OK; 186 result = STATE_OK;
180 } 187 }
181 188
182 printf ("NAGIOS %s: ", (result == STATE_OK) ? _("OK") : _("WARNING")); 189 printf("NAGIOS %s: ", (result == STATE_OK) ? _("OK") : _("WARNING"));
183 printf (ngettext ("%d process", "%d processes", proc_entries), proc_entries); 190 printf(ngettext("%d process", "%d processes", proc_entries), proc_entries);
184 printf (", "); 191 printf(", ");
185 printf ( 192 printf(ngettext("status log updated %d second ago", "status log updated %d seconds ago", (int)(current_time - latest_entry_time)),
186 ngettext ("status log updated %d second ago", 193 (int)(current_time - latest_entry_time));
187 "status log updated %d seconds ago", 194 printf("\n");
188 (int) (current_time - latest_entry_time) ),
189 (int) (current_time - latest_entry_time) );
190 printf ("\n");
191 195
192 return result; 196 exit(result);
193} 197}
194 198
195
196
197/* process command-line arguments */ 199/* process command-line arguments */
198int 200check_nagios_config_wrapper process_arguments(int argc, char **argv) {
199process_arguments (int argc, char **argv) 201 static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'},
200{ 202 {"command", required_argument, 0, 'C'}, {"timeout", optional_argument, 0, 't'},
201 int c; 203 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
202 204 {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}};
203 int option = 0; 205
204 static struct option longopts[] = { 206 check_nagios_config_wrapper result = {
205 {"filename", required_argument, 0, 'F'}, 207 .errorcode = OK,
206 {"expires", required_argument, 0, 'e'}, 208 .config = check_nagios_config_init(),
207 {"command", required_argument, 0, 'C'},
208 {"timeout", optional_argument, 0, 't'},
209 {"version", no_argument, 0, 'V'},
210 {"help", no_argument, 0, 'h'},
211 {"verbose", no_argument, 0, 'v'},
212 {0, 0, 0, 0}
213 }; 209 };
210 if (argc < 2) {
211 result.errorcode = ERROR;
212 return result;
213 }
214 214
215 if (argc < 2) 215 if (!is_option(argv[1])) {
216 return ERROR; 216 result.config.status_log = argv[1];
217 217 if (is_intnonneg(argv[2])) {
218 if (!is_option (argv[1])) { 218 result.config.expire_minutes = atoi(argv[2]);
219 status_log = argv[1]; 219 } else {
220 if (is_intnonneg (argv[2])) 220 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n"));
221 expire_minutes = atoi (argv[2]); 221 }
222 else 222 result.config.process_string = argv[3];
223 die (STATE_UNKNOWN, 223 return result;
224 _("Expiration time must be an integer (seconds)\n"));
225 process_string = argv[3];
226 return OK;
227 } 224 }
228 225
229 while (1) { 226 int option = 0;
230 c = getopt_long (argc, argv, "+hVvF:C:e:t:", longopts, &option); 227 while (true) {
228 int option_index = getopt_long(argc, argv, "+hVvF:C:e:t:", longopts, &option);
231 229
232 if (c == -1 || c == EOF || c == 1) 230 if (CHECK_EOF(option_index) || option_index == 1) {
233 break; 231 break;
232 }
234 233
235 switch (c) { 234 switch (option_index) {
236 case 'h': /* help */ 235 case 'h': /* help */
237 print_help (); 236 print_help();
238 exit (STATE_UNKNOWN); 237 exit(STATE_UNKNOWN);
239 case 'V': /* version */ 238 case 'V': /* version */
240 print_revision (progname, NP_VERSION); 239 print_revision(progname, NP_VERSION);
241 exit (STATE_UNKNOWN); 240 exit(STATE_UNKNOWN);
242 case 'F': /* status log */ 241 case 'F': /* status log */
243 status_log = optarg; 242 result.config.status_log = optarg;
244 break; 243 break;
245 case 'C': /* command */ 244 case 'C': /* command */
246 process_string = optarg; 245 result.config.process_string = optarg;
247 break; 246 break;
248 case 'e': /* expiry time */ 247 case 'e': /* expiry time */
249 if (is_intnonneg (optarg)) 248 if (is_intnonneg(optarg)) {
250 expire_minutes = atoi (optarg); 249 result.config.expire_minutes = atoi(optarg);
251 else 250 } else {
252 die (STATE_UNKNOWN, 251 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n"));
253 _("Expiration time must be an integer (seconds)\n")); 252 }
254 break; 253 break;
255 case 't': /* timeout */ 254 case 't': /* timeout */
256 if (is_intnonneg (optarg)) 255 if (is_intnonneg(optarg)) {
257 timeout_interval = atoi (optarg); 256 timeout_interval = atoi(optarg);
258 else 257 } else {
259 die (STATE_UNKNOWN, 258 die(STATE_UNKNOWN, _("Timeout must be an integer (seconds)\n"));
260 _("Timeout must be an integer (seconds)\n")); 259 }
261 break; 260 break;
262 case 'v': 261 case 'v':
263 verbose++; 262 verbose++;
264 break; 263 break;
265 default: /* print short usage_va statement if args not parsable */ 264 default: /* print short usage_va statement if args not parsable */
266 usage5(); 265 usage5();
267 } 266 }
268 } 267 }
269 268
269 if (result.config.status_log == NULL) {
270 die(STATE_UNKNOWN, _("You must provide the status_log\n"));
271 }
270 272
271 if (status_log == NULL) 273 if (result.config.process_string == NULL) {
272 die (STATE_UNKNOWN, _("You must provide the status_log\n")); 274 die(STATE_UNKNOWN, _("You must provide a process string\n"));
273 275 }
274 if (process_string == NULL)
275 die (STATE_UNKNOWN, _("You must provide a process string\n"));
276 276
277 return OK; 277 return result;
278} 278}
279 279
280void print_help(void) {
281 print_revision(progname, NP_VERSION);
280 282
283 printf(_(COPYRIGHT), copyright, email);
281 284
282void 285 printf("%s\n", _("This plugin checks the status of the Nagios process on the local machine"));
283print_help (void) 286 printf("%s\n", _("The plugin will check to make sure the Nagios status log is no older than"));
284{ 287 printf("%s\n", _("the number of minutes specified by the expires option."));
285 print_revision (progname, NP_VERSION); 288 printf("%s\n", _("It also checks the process table for a process matching the command argument."));
286 289
287 printf (_(COPYRIGHT), copyright, email); 290 printf("\n\n");
288 291
289 printf ("%s\n", _("This plugin checks the status of the Nagios process on the local machine")); 292 print_usage();
290 printf ("%s\n", _("The plugin will check to make sure the Nagios status log is no older than"));
291 printf ("%s\n", _("the number of minutes specified by the expires option."));
292 printf ("%s\n", _("It also checks the process table for a process matching the command argument."));
293 293
294 printf ("\n\n"); 294 printf(UT_HELP_VRSN);
295 printf(UT_EXTRA_OPTS);
295 296
296 print_usage (); 297 printf(" %s\n", "-F, --filename=FILE");
298 printf(" %s\n", _("Name of the log file to check"));
299 printf(" %s\n", "-e, --expires=INTEGER");
300 printf(" %s\n", _("Minutes aging after which logfile is considered stale"));
301 printf(" %s\n", "-C, --command=STRING");
302 printf(" %s\n", _("Substring to search for in process arguments"));
303 printf(" %s\n", "-t, --timeout=INTEGER");
304 printf(" %s\n", _("Timeout for the plugin in seconds"));
305 printf(UT_VERBOSE);
297 306
298 printf (UT_HELP_VRSN); 307 printf("\n");
299 printf (UT_EXTRA_OPTS); 308 printf("%s\n", _("Examples:"));
309 printf(" %s\n", "check_nagios -t 20 -e 5 -F /usr/local/nagios/var/status.log -C /usr/local/nagios/bin/nagios");
300 310
301 printf (" %s\n", "-F, --filename=FILE"); 311 printf(UT_SUPPORT);
302 printf (" %s\n", _("Name of the log file to check"));
303 printf (" %s\n", "-e, --expires=INTEGER");
304 printf (" %s\n", _("Minutes aging after which logfile is considered stale"));
305 printf (" %s\n", "-C, --command=STRING");
306 printf (" %s\n", _("Substring to search for in process arguments"));
307 printf (" %s\n", "-t, --timeout=INTEGER");
308 printf (" %s\n", _("Timeout for the plugin in seconds"));
309 printf (UT_VERBOSE);
310
311 printf ("\n");
312 printf ("%s\n", _("Examples:"));
313 printf (" %s\n", "check_nagios -t 20 -e 5 -F /usr/local/nagios/var/status.log -C /usr/local/nagios/bin/nagios");
314
315 printf (UT_SUPPORT);
316} 312}
317 313
318 314void print_usage(void) {
319 315 printf("%s\n", _("Usage:"));
320void 316 printf("%s -F <status log file> -t <timeout_seconds> -e <expire_minutes> -C <process_string>\n", progname);
321print_usage (void)
322{
323 printf ("%s\n", _("Usage:"));
324 printf ("%s -F <status log file> -t <timeout_seconds> -e <expire_minutes> -C <process_string>\n", progname);
325} 317}
diff --git a/plugins/check_nagios.d/config.h b/plugins/check_nagios.d/config.h
new file mode 100644
index 00000000..efe139f9
--- /dev/null
+++ b/plugins/check_nagios.d/config.h
@@ -0,0 +1,19 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6typedef struct {
7 char *status_log;
8 char *process_string;
9 int expire_minutes;
10} check_nagios_config;
11
12check_nagios_config check_nagios_config_init() {
13 check_nagios_config tmp = {
14 .status_log = NULL,
15 .process_string = NULL,
16 .expire_minutes = 0,
17 };
18 return tmp;
19}
diff --git a/plugins/check_nt.c b/plugins/check_nt.c
deleted file mode 100644
index 19c050de..00000000
--- a/plugins/check_nt.c
+++ /dev/null
@@ -1,801 +0,0 @@
1/*****************************************************************************
2*
3* Monitoring check_nt plugin
4*
5* License: GPL
6* Copyright (c) 2000-2002 Yves Rubin (rubiyz@yahoo.com)
7* Copyright (c) 2003-2007 Monitoring Plugins Development Team
8*
9* Description:
10*
11* This file contains the check_nt plugin
12*
13* This plugin collects data from the NSClient service running on a
14* Windows NT/2000/XP/2003 server.
15* This plugin requires NSClient software to run on NT
16* (http://nsclient.ready2run.nl/)
17*
18*
19* This program is free software: you can redistribute it and/or modify
20* it under the terms of the GNU General Public License as published by
21* the Free Software Foundation, either version 3 of the License, or
22* (at your option) any later version.
23*
24* This program is distributed in the hope that it will be useful,
25* but WITHOUT ANY WARRANTY; without even the implied warranty of
26* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27* GNU General Public License for more details.
28*
29* You should have received a copy of the GNU General Public License
30* along with this program. If not, see <http://www.gnu.org/licenses/>.
31*
32*
33*****************************************************************************/
34
35const char *progname = "check_nt";
36const char *copyright = "2000-2007";
37const char *email = "devel@monitoring-plugins.org";
38
39#include "common.h"
40#include "netutils.h"
41#include "utils.h"
42
43enum checkvars {
44 CHECK_NONE,
45 CHECK_CLIENTVERSION,
46 CHECK_CPULOAD,
47 CHECK_UPTIME,
48 CHECK_USEDDISKSPACE,
49 CHECK_SERVICESTATE,
50 CHECK_PROCSTATE,
51 CHECK_MEMUSE,
52 CHECK_COUNTER,
53 CHECK_FILEAGE,
54 CHECK_INSTANCES
55};
56
57enum {
58 MAX_VALUE_LIST = 30,
59 PORT = 1248
60};
61
62char *server_address=NULL;
63char *volume_name=NULL;
64int server_port=PORT;
65char *value_list=NULL;
66char *req_password=NULL;
67unsigned long lvalue_list[MAX_VALUE_LIST];
68unsigned long warning_value=0L;
69unsigned long critical_value=0L;
70bool check_warning_value=false;
71bool check_critical_value=false;
72enum checkvars vars_to_check = CHECK_NONE;
73bool show_all = false;
74
75char recv_buffer[MAX_INPUT_BUFFER];
76
77void fetch_data (const char* address, int port, const char* sendb);
78int process_arguments(int, char **);
79void preparelist(char *string);
80bool strtoularray(unsigned long *array, char *string, const char *delim);
81void print_help(void);
82void print_usage(void);
83
84int main(int argc, char **argv){
85
86/* should be int result = STATE_UNKNOWN; */
87
88 int return_code = STATE_UNKNOWN;
89 char *send_buffer=NULL;
90 char *output_message=NULL;
91 char *perfdata=NULL;
92 char *temp_string=NULL;
93 char *temp_string_perf=NULL;
94 char *description=NULL,*counter_unit = NULL;
95 char *minval = NULL, *maxval = NULL, *errcvt = NULL;
96 char *fds=NULL, *tds=NULL;
97 char *numstr;
98
99 double total_disk_space=0;
100 double free_disk_space=0;
101 double percent_used_space=0;
102 double warning_used_space=0;
103 double critical_used_space=0;
104 double mem_commitLimit=0;
105 double mem_commitByte=0;
106 double fminval = 0, fmaxval = 0;
107 unsigned long utilization;
108 unsigned long uptime;
109 unsigned long age_in_minutes;
110 double counter_value = 0.0;
111 int offset=0;
112 int updays=0;
113 int uphours=0;
114 int upminutes=0;
115
116 bool isPercent = false;
117 bool allRight = false;
118
119 setlocale (LC_ALL, "");
120 bindtextdomain (PACKAGE, LOCALEDIR);
121 textdomain (PACKAGE);
122
123 /* Parse extra opts if any */
124 argv=np_extra_opts (&argc, argv, progname);
125
126 if(process_arguments(argc,argv) == ERROR)
127 usage4 (_("Could not parse arguments"));
128
129 /* initialize alarm signal handling */
130 signal(SIGALRM,socket_timeout_alarm_handler);
131
132 /* set socket timeout */
133 alarm(socket_timeout);
134
135 switch (vars_to_check) {
136
137 case CHECK_CLIENTVERSION:
138
139 xasprintf(&send_buffer, "%s&1", req_password);
140 fetch_data (server_address, server_port, send_buffer);
141 if (value_list != NULL && strcmp(recv_buffer, value_list) != 0) {
142 xasprintf (&output_message, _("Wrong client version - running: %s, required: %s"), recv_buffer, value_list);
143 return_code = STATE_WARNING;
144 } else {
145 xasprintf (&output_message, "%s", recv_buffer);
146 return_code = STATE_OK;
147 }
148 break;
149
150 case CHECK_CPULOAD:
151
152 if (value_list==NULL)
153 output_message = strdup (_("missing -l parameters"));
154 else if (! strtoularray(lvalue_list,value_list,","))
155 output_message = strdup (_("wrong -l parameter."));
156 else {
157 /* -l parameters is present with only integers */
158 return_code=STATE_OK;
159 temp_string = strdup (_("CPU Load"));
160 temp_string_perf = strdup (" ");
161
162 /* loop until one of the parameters is wrong or not present */
163 while (lvalue_list[0+offset]> (unsigned long)0 &&
164 lvalue_list[0+offset]<=(unsigned long)17280 &&
165 lvalue_list[1+offset]> (unsigned long)0 &&
166 lvalue_list[1+offset]<=(unsigned long)100 &&
167 lvalue_list[2+offset]> (unsigned long)0 &&
168 lvalue_list[2+offset]<=(unsigned long)100) {
169
170 /* Send request and retrieve data */
171 xasprintf(&send_buffer,"%s&2&%lu",req_password,lvalue_list[0+offset]);
172 fetch_data (server_address, server_port, send_buffer);
173
174 utilization=strtoul(recv_buffer,NULL,10);
175
176 /* Check if any of the request is in a warning or critical state */
177 if(utilization >= lvalue_list[2+offset])
178 return_code=STATE_CRITICAL;
179 else if(utilization >= lvalue_list[1+offset] && return_code<STATE_WARNING)
180 return_code=STATE_WARNING;
181
182 xasprintf(&output_message,_(" %lu%% (%lu min average)"), utilization, lvalue_list[0+offset]);
183 xasprintf(&temp_string,"%s%s",temp_string,output_message);
184 xasprintf(&perfdata,_(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"), lvalue_list[0+offset], utilization,
185 lvalue_list[1+offset], lvalue_list[2+offset]);
186 xasprintf(&temp_string_perf,"%s%s",temp_string_perf,perfdata);
187 offset+=3; /* move across the array */
188 }
189
190 if (strlen(temp_string)>10) { /* we had at least one loop */
191 output_message = strdup (temp_string);
192 perfdata = temp_string_perf;
193 } else
194 output_message = strdup (_("not enough values for -l parameters"));
195 }
196 break;
197
198 case CHECK_UPTIME:
199
200 if (value_list == NULL) {
201 value_list = "minutes";
202 }
203 if (strncmp(value_list, "seconds", strlen("seconds") + 1 ) &&
204 strncmp(value_list, "minutes", strlen("minutes") + 1) &&
205 strncmp(value_list, "hours", strlen("hours") + 1) &&
206 strncmp(value_list, "days", strlen("days") + 1)) {
207
208 output_message = strdup (_("wrong -l argument"));
209 } else {
210 xasprintf(&send_buffer, "%s&3", req_password);
211 fetch_data (server_address, server_port, send_buffer);
212 uptime=strtoul(recv_buffer,NULL,10);
213 updays = uptime / 86400;
214 uphours = (uptime % 86400) / 3600;
215 upminutes = ((uptime % 86400) % 3600) / 60;
216
217 if (!strncmp(value_list, "minutes", strlen("minutes")))
218 uptime = uptime / 60;
219 else if (!strncmp(value_list, "hours", strlen("hours")))
220 uptime = uptime / 3600;
221 else if (!strncmp(value_list, "days", strlen("days")))
222 uptime = uptime / 86400;
223 /* else uptime in seconds, nothing to do */
224
225 xasprintf(&output_message,_("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"),updays, uphours, upminutes, uptime);
226
227 if (check_critical_value && uptime <= critical_value)
228 return_code=STATE_CRITICAL;
229 else if (check_warning_value && uptime <= warning_value)
230 return_code=STATE_WARNING;
231 else
232 return_code=STATE_OK;
233 }
234 break;
235
236 case CHECK_USEDDISKSPACE:
237
238 if (value_list==NULL)
239 output_message = strdup (_("missing -l parameters"));
240 else if (strlen(value_list)!=1)
241 output_message = strdup (_("wrong -l argument"));
242 else {
243 xasprintf(&send_buffer,"%s&4&%s", req_password, value_list);
244 fetch_data (server_address, server_port, send_buffer);
245 fds=strtok(recv_buffer,"&");
246 tds=strtok(NULL,"&");
247 if(fds!=NULL)
248 free_disk_space=atof(fds);
249 if(tds!=NULL)
250 total_disk_space=atof(tds);
251
252 if (total_disk_space>0 && free_disk_space>=0) {
253 percent_used_space = ((total_disk_space - free_disk_space) / total_disk_space) * 100;
254 warning_used_space = ((float)warning_value / 100) * total_disk_space;
255 critical_used_space = ((float)critical_value / 100) * total_disk_space;
256
257 xasprintf(&temp_string,_("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"),
258 value_list, total_disk_space / 1073741824, (total_disk_space - free_disk_space) / 1073741824,
259 percent_used_space, free_disk_space / 1073741824, (free_disk_space / total_disk_space)*100);
260 xasprintf(&temp_string_perf,_("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"), value_list,
261 (total_disk_space - free_disk_space) / 1073741824, warning_used_space / 1073741824,
262 critical_used_space / 1073741824, total_disk_space / 1073741824);
263
264 if(check_critical_value && percent_used_space >= critical_value)
265 return_code=STATE_CRITICAL;
266 else if (check_warning_value && percent_used_space >= warning_value)
267 return_code=STATE_WARNING;
268 else
269 return_code=STATE_OK;
270
271 output_message = strdup (temp_string);
272 perfdata = temp_string_perf;
273 } else {
274 output_message = strdup (_("Free disk space : Invalid drive"));
275 return_code=STATE_UNKNOWN;
276 }
277 }
278 break;
279
280 case CHECK_SERVICESTATE:
281 case CHECK_PROCSTATE:
282
283 if (value_list==NULL)
284 output_message = strdup (_("No service/process specified"));
285 else {
286 preparelist(value_list); /* replace , between services with & to send the request */
287 xasprintf(&send_buffer,"%s&%u&%s&%s", req_password,(vars_to_check==CHECK_SERVICESTATE)?5:6,
288 (show_all) ? "ShowAll" : "ShowFail",value_list);
289 fetch_data (server_address, server_port, send_buffer);
290 numstr = strtok(recv_buffer,"&");
291 if (numstr == NULL)
292 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
293 return_code=atoi(numstr);
294 temp_string=strtok(NULL,"&");
295 output_message = strdup (temp_string);
296 }
297 break;
298
299 case CHECK_MEMUSE:
300
301 xasprintf(&send_buffer,"%s&7", req_password);
302 fetch_data (server_address, server_port, send_buffer);
303 numstr = strtok(recv_buffer,"&");
304 if (numstr == NULL)
305 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
306 mem_commitLimit=atof(numstr);
307 numstr = strtok(NULL,"&");
308 if (numstr == NULL)
309 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
310 mem_commitByte=atof(numstr);
311 percent_used_space = (mem_commitByte / mem_commitLimit) * 100;
312 warning_used_space = ((float)warning_value / 100) * mem_commitLimit;
313 critical_used_space = ((float)critical_value / 100) * mem_commitLimit;
314
315 /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here,
316 which equals RAM + Pagefiles. */
317 xasprintf(&output_message,_("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"),
318 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space,
319 (mem_commitLimit - mem_commitByte) / 1048567, (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100);
320 xasprintf(&perfdata,_("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"), mem_commitByte / 1048567,
321 warning_used_space / 1048567, critical_used_space / 1048567, mem_commitLimit / 1048567);
322
323 return_code=STATE_OK;
324 if(check_critical_value && percent_used_space >= critical_value)
325 return_code=STATE_CRITICAL;
326 else if (check_warning_value && percent_used_space >= warning_value)
327 return_code=STATE_WARNING;
328
329 break;
330
331 case CHECK_COUNTER:
332
333
334 /*
335 CHECK_COUNTER has been modified to provide extensive perfdata information.
336 In order to do this, some modifications have been done to the code
337 and some constraints have been introduced.
338
339 1) For the sake of simplicity of the code, perfdata information will only be
340 provided when the "description" field is added.
341
342 2) If the counter you're going to measure is percent-based, the code will detect
343 the percent sign in its name and will attribute minimum (0%) and maximum (100%)
344 values automagically, as well the "%" sign to graph units.
345
346 3) OTOH, if the counter is "absolute", you'll have to provide the following
347 the counter unit - that is, the dimensions of the counter you're getting. Examples:
348 pages/s, packets transferred, etc.
349
350 4) If you want, you may provide the minimum and maximum values to expect. They aren't mandatory,
351 but once specified they MUST have the same order of magnitude and units of -w and -c; otherwise.
352 strange things will happen when you make graphs of your data.
353 */
354
355 if (value_list == NULL)
356 output_message = strdup (_("No counter specified"));
357 else
358 {
359 preparelist (value_list); /* replace , between services with & to send the request */
360 isPercent = (strchr (value_list, '%') != NULL);
361
362 strtok (value_list, "&"); /* burn the first parameters */
363 description = strtok (NULL, "&");
364 counter_unit = strtok (NULL, "&");
365 xasprintf (&send_buffer, "%s&8&%s", req_password, value_list);
366 fetch_data (server_address, server_port, send_buffer);
367 counter_value = atof (recv_buffer);
368
369 if (description == NULL)
370 xasprintf (&output_message, "%.f", counter_value);
371 else if (isPercent)
372 {
373 counter_unit = strdup ("%");
374 allRight = true;
375 }
376
377 if ((counter_unit != NULL) && (!allRight))
378 {
379 minval = strtok (NULL, "&");
380 maxval = strtok (NULL, "&");
381
382 /* All parameters specified. Let's check the numbers */
383
384 fminval = (minval != NULL) ? strtod (minval, &errcvt) : -1;
385 fmaxval = (minval != NULL) ? strtod (maxval, &errcvt) : -1;
386
387 if ((fminval == 0) && (minval == errcvt))
388 output_message = strdup (_("Minimum value contains non-numbers"));
389 else
390 {
391 if ((fmaxval == 0) && (maxval == errcvt))
392 output_message = strdup (_("Maximum value contains non-numbers"));
393 else
394 allRight = true; /* Everything is OK. */
395
396 }
397 }
398 else if ((counter_unit == NULL) && (description != NULL))
399 output_message = strdup (_("No unit counter specified"));
400
401 if (allRight)
402 {
403 /* Let's format the output string, finally... */
404 if (strstr(description, "%") == NULL) {
405 xasprintf (&output_message, "%s = %.2f %s", description, counter_value, counter_unit);
406 } else {
407 /* has formatting, will segv if wrong */
408 xasprintf (&output_message, description, counter_value);
409 }
410 xasprintf (&output_message, "%s |", output_message);
411 xasprintf (&output_message,"%s %s", output_message,
412 fperfdata (description, counter_value,
413 counter_unit, 1, warning_value, 1, critical_value,
414 (!(isPercent) && (minval != NULL)), fminval,
415 (!(isPercent) && (minval != NULL)), fmaxval));
416 }
417 }
418
419 if (critical_value > warning_value)
420 { /* Normal thresholds */
421 if (check_critical_value && counter_value >= critical_value)
422 return_code = STATE_CRITICAL;
423 else if (check_warning_value && counter_value >= warning_value)
424 return_code = STATE_WARNING;
425 else
426 return_code = STATE_OK;
427 }
428 else
429 { /* inverse thresholds */
430 return_code = STATE_OK;
431 if (check_critical_value && counter_value <= critical_value)
432 return_code = STATE_CRITICAL;
433 else if (check_warning_value && counter_value <= warning_value)
434 return_code = STATE_WARNING;
435 }
436 break;
437
438 case CHECK_FILEAGE:
439
440 if (value_list==NULL)
441 output_message = strdup (_("No counter specified"));
442 else {
443 preparelist(value_list); /* replace , between services with & to send the request */
444 xasprintf(&send_buffer,"%s&9&%s", req_password,value_list);
445 fetch_data (server_address, server_port, send_buffer);
446 age_in_minutes = atoi(strtok(recv_buffer,"&"));
447 description = strtok(NULL,"&");
448 output_message = strdup (description);
449
450 if (critical_value > warning_value) { /* Normal thresholds */
451 if(check_critical_value && age_in_minutes >= critical_value)
452 return_code=STATE_CRITICAL;
453 else if (check_warning_value && age_in_minutes >= warning_value)
454 return_code=STATE_WARNING;
455 else
456 return_code=STATE_OK;
457 }
458 else { /* inverse thresholds */
459 if(check_critical_value && age_in_minutes <= critical_value)
460 return_code=STATE_CRITICAL;
461 else if (check_warning_value && age_in_minutes <= warning_value)
462 return_code=STATE_WARNING;
463 else
464 return_code=STATE_OK;
465 }
466 }
467 break;
468
469 case CHECK_INSTANCES:
470 if (value_list==NULL)
471 output_message = strdup (_("No counter specified"));
472 else {
473 xasprintf(&send_buffer,"%s&10&%s", req_password,value_list);
474 fetch_data (server_address, server_port, send_buffer);
475 if (!strncmp(recv_buffer,"ERROR",5)) {
476 printf("NSClient - %s\n",recv_buffer);
477 exit(STATE_UNKNOWN);
478 }
479 xasprintf(&output_message,"%s",recv_buffer);
480 return_code=STATE_OK;
481 }
482 break;
483
484 case CHECK_NONE:
485 default:
486 usage4 (_("Please specify a variable to check"));
487 break;
488
489 }
490
491 /* reset timeout */
492 alarm(0);
493
494 if (perfdata==NULL)
495 printf("%s\n",output_message);
496 else
497 printf("%s | %s\n",output_message,perfdata);
498 return return_code;
499}
500
501
502
503/* process command-line arguments */
504int process_arguments(int argc, char **argv){
505 int c;
506
507 int option = 0;
508 static struct option longopts[] =
509 {
510 {"port", required_argument,0,'p'},
511 {"timeout", required_argument,0,'t'},
512 {"critical", required_argument,0,'c'},
513 {"warning", required_argument,0,'w'},
514 {"variable", required_argument,0,'v'},
515 {"hostname", required_argument,0,'H'},
516 {"params", required_argument,0,'l'},
517 {"secret", required_argument,0,'s'},
518 {"display", required_argument,0,'d'},
519 {"unknown-timeout", no_argument, 0, 'u'},
520 {"version", no_argument, 0,'V'},
521 {"help", no_argument, 0,'h'},
522 {0,0,0,0}
523 };
524
525 /* no options were supplied */
526 if(argc<2) return ERROR;
527
528 /* backwards compatibility */
529 if (! is_option(argv[1])) {
530 server_address = strdup(argv[1]);
531 argv[1]=argv[0];
532 argv=&argv[1];
533 argc--;
534 }
535
536 for (c=1;c<argc;c++) {
537 if(strcmp("-to",argv[c])==0)
538 strcpy(argv[c],"-t");
539 else if (strcmp("-wv",argv[c])==0)
540 strcpy(argv[c],"-w");
541 else if (strcmp("-cv",argv[c])==0)
542 strcpy(argv[c],"-c");
543 }
544
545 while (1) {
546 c = getopt_long(argc,argv,"+hVH:t:c:w:p:v:l:s:d:u",longopts,&option);
547
548 if (c==-1||c==EOF||c==1)
549 break;
550
551 switch (c) {
552 case '?': /* print short usage statement if args not parsable */
553 usage5 ();
554 case 'h': /* help */
555 print_help();
556 exit(STATE_UNKNOWN);
557 case 'V': /* version */
558 print_revision(progname, NP_VERSION);
559 exit(STATE_UNKNOWN);
560 case 'H': /* hostname */
561 server_address = optarg;
562 break;
563 case 's': /* password */
564 req_password = optarg;
565 break;
566 case 'p': /* port */
567 if (is_intnonneg(optarg))
568 server_port=atoi(optarg);
569 else
570 die(STATE_UNKNOWN,_("Server port must be an integer\n"));
571 break;
572 case 'v':
573 if(strlen(optarg)<4)
574 return ERROR;
575 if(!strcmp(optarg,"CLIENTVERSION"))
576 vars_to_check=CHECK_CLIENTVERSION;
577 else if(!strcmp(optarg,"CPULOAD"))
578 vars_to_check=CHECK_CPULOAD;
579 else if(!strcmp(optarg,"UPTIME"))
580 vars_to_check=CHECK_UPTIME;
581 else if(!strcmp(optarg,"USEDDISKSPACE"))
582 vars_to_check=CHECK_USEDDISKSPACE;
583 else if(!strcmp(optarg,"SERVICESTATE"))
584 vars_to_check=CHECK_SERVICESTATE;
585 else if(!strcmp(optarg,"PROCSTATE"))
586 vars_to_check=CHECK_PROCSTATE;
587 else if(!strcmp(optarg,"MEMUSE"))
588 vars_to_check=CHECK_MEMUSE;
589 else if(!strcmp(optarg,"COUNTER"))
590 vars_to_check=CHECK_COUNTER;
591 else if(!strcmp(optarg,"FILEAGE"))
592 vars_to_check=CHECK_FILEAGE;
593 else if(!strcmp(optarg,"INSTANCES"))
594 vars_to_check=CHECK_INSTANCES;
595 else
596 return ERROR;
597 break;
598 case 'l': /* value list */
599 value_list = optarg;
600 break;
601 case 'w': /* warning threshold */
602 warning_value=strtoul(optarg,NULL,10);
603 check_warning_value=true;
604 break;
605 case 'c': /* critical threshold */
606 critical_value=strtoul(optarg,NULL,10);
607 check_critical_value=true;
608 break;
609 case 'd': /* Display select for services */
610 if (!strcmp(optarg,"SHOWALL"))
611 show_all = true;
612 break;
613 case 'u':
614 socket_timeout_state=STATE_UNKNOWN;
615 break;
616 case 't': /* timeout */
617 socket_timeout=atoi(optarg);
618 if(socket_timeout<=0)
619 return ERROR;
620 }
621
622 }
623 if (server_address == NULL)
624 usage4 (_("You must provide a server address or host name"));
625
626 if (vars_to_check==CHECK_NONE)
627 return ERROR;
628
629 if (req_password == NULL)
630 req_password = strdup (_("None"));
631
632 return OK;
633}
634
635
636
637void fetch_data (const char *address, int port, const char *sendb) {
638 int result;
639
640 result=process_tcp_request(address, port, sendb, recv_buffer,sizeof(recv_buffer));
641
642 if(result!=STATE_OK)
643 die (result, _("could not fetch information from server\n"));
644
645 if (!strncmp(recv_buffer,"ERROR",5))
646 die (STATE_UNKNOWN, "NSClient - %s\n",recv_buffer);
647}
648
649bool strtoularray(unsigned long *array, char *string, const char *delim) {
650 /* split a <delim> delimited string into a long array */
651 int idx=0;
652 char *t1;
653
654 for (idx=0;idx<MAX_VALUE_LIST;idx++)
655 array[idx]=0;
656
657 idx=0;
658 for(t1 = strtok(string,delim);t1 != NULL; t1 = strtok(NULL, delim)) {
659 if (is_numeric(t1) && idx<MAX_VALUE_LIST) {
660 array[idx]=strtoul(t1,NULL,10);
661 idx++;
662 } else
663 return false;
664 }
665 return true;
666}
667
668void preparelist(char *string) {
669 /* Replace all , with & which is the delimiter for the request */
670 int i;
671
672 for (i = 0; (size_t)i < strlen(string); i++)
673 if (string[i] == ',') {
674 string[i]='&';
675 }
676}
677
678
679
680void print_help(void)
681{
682 print_revision(progname, NP_VERSION);
683
684 printf ("Copyright (c) 2000 Yves Rubin (rubiyz@yahoo.com)\n");
685 printf (COPYRIGHT, copyright, email);
686
687 printf ("%s\n", _("This plugin collects data from the NSClient service running on a"));
688 printf ("%s\n", _("Windows NT/2000/XP/2003 server."));
689
690 printf ("\n\n");
691
692 print_usage();
693
694 printf (UT_HELP_VRSN);
695 printf (UT_EXTRA_OPTS);
696
697 printf ("%s\n", _("Options:"));
698 printf (" %s\n", "-H, --hostname=HOST");
699 printf (" %s\n", _("Name of the host to check"));
700 printf (" %s\n", "-p, --port=INTEGER");
701 printf (" %s", _("Optional port number (default: "));
702 printf ("%d)\n", PORT);
703 printf (" %s\n", "-s, --secret=<password>");
704 printf (" %s\n", _("Password needed for the request"));
705 printf (" %s\n", "-w, --warning=INTEGER");
706 printf (" %s\n", _("Threshold which will result in a warning status"));
707 printf (" %s\n", "-c, --critical=INTEGER");
708 printf (" %s\n", _("Threshold which will result in a critical status"));
709 printf (" %s\n", "-t, --timeout=INTEGER");
710 printf (" %s", _("Seconds before connection attempt times out (default: "));
711 printf (" %s\n", "-l, --params=<parameters>");
712 printf (" %s", _("Parameters passed to specified check (see below)"));
713 printf (" %s\n", "-d, --display={SHOWALL}");
714 printf (" %s", _("Display options (currently only SHOWALL works)"));
715 printf (" %s\n", "-u, --unknown-timeout");
716 printf (" %s", _("Return UNKNOWN on timeouts"));
717 printf ("%d)\n", DEFAULT_SOCKET_TIMEOUT);
718 printf (" %s\n", "-h, --help");
719 printf (" %s\n", _("Print this help screen"));
720 printf (" %s\n", "-V, --version");
721 printf (" %s\n", _("Print version information"));
722 printf (" %s\n", "-v, --variable=STRING");
723 printf (" %s\n\n", _("Variable to check"));
724 printf ("%s\n", _("Valid variables are:"));
725 printf (" %s", "CLIENTVERSION =");
726 printf (" %s\n", _("Get the NSClient version"));
727 printf (" %s\n", _("If -l <version> is specified, will return warning if versions differ."));
728 printf (" %s\n", "CPULOAD =");
729 printf (" %s\n", _("Average CPU load on last x minutes."));
730 printf (" %s\n", _("Request a -l parameter with the following syntax:"));
731 printf (" %s\n", _("-l <minutes range>,<warning threshold>,<critical threshold>."));
732 printf (" %s\n", _("<minute range> should be less than 24*60."));
733 printf (" %s\n", _("Thresholds are percentage and up to 10 requests can be done in one shot."));
734 printf (" %s\n", "ie: -l 60,90,95,120,90,95");
735 printf (" %s\n", "UPTIME =");
736 printf (" %s\n", _("Get the uptime of the machine."));
737 printf (" %s\n", _("-l <unit> "));
738 printf (" %s\n", _("<unit> = seconds, minutes, hours, or days. (default: minutes)"));
739 printf (" %s\n", _("Thresholds will use the unit specified above."));
740 printf (" %s\n", "USEDDISKSPACE =");
741 printf (" %s\n", _("Size and percentage of disk use."));
742 printf (" %s\n", _("Request a -l parameter containing the drive letter only."));
743 printf (" %s\n", _("Warning and critical thresholds can be specified with -w and -c."));
744 printf (" %s\n", "MEMUSE =");
745 printf (" %s\n", _("Memory use."));
746 printf (" %s\n", _("Warning and critical thresholds can be specified with -w and -c."));
747 printf (" %s\n", "SERVICESTATE =");
748 printf (" %s\n", _("Check the state of one or several services."));
749 printf (" %s\n", _("Request a -l parameters with the following syntax:"));
750 printf (" %s\n", _("-l <service1>,<service2>,<service3>,..."));
751 printf (" %s\n", _("You can specify -d SHOWALL in case you want to see working services"));
752 printf (" %s\n", _("in the returned string."));
753 printf (" %s\n", "PROCSTATE =");
754 printf (" %s\n", _("Check if one or several process are running."));
755 printf (" %s\n", _("Same syntax as SERVICESTATE."));
756 printf (" %s\n", "COUNTER =");
757 printf (" %s\n", _("Check any performance counter of Windows NT/2000."));
758 printf (" %s\n", _("Request a -l parameters with the following syntax:"));
759 printf (" %s\n", _("-l \"\\\\<performance object>\\\\counter\",\"<description>"));
760 printf (" %s\n", _("The <description> parameter is optional and is given to a printf "));
761 printf (" %s\n", _("output command which requires a float parameter."));
762 printf (" %s\n", _("If <description> does not include \"%%\", it is used as a label."));
763 printf (" %s\n", _("Some examples:"));
764 printf (" %s\n", "\"Paging file usage is %%.2f %%%%\"");
765 printf (" %s\n", "\"%%.f %%%% paging file used.\"");
766 printf (" %s\n", "INSTANCES =");
767 printf (" %s\n", _("Check any performance counter object of Windows NT/2000."));
768 printf (" %s\n", _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>"));
769 printf (" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),"));
770 printf (" %s\n", _("if it is two words, it should be enclosed in quotes"));
771 printf (" %s\n", _("The returned results will be a comma-separated list of instances on "));
772 printf (" %s\n", _(" the selected computer for that object."));
773 printf (" %s\n", _("The purpose of this is to be run from command line to determine what instances"));
774 printf (" %s\n", _(" are available for monitoring without having to log onto the Windows server"));
775 printf (" %s\n", _(" to run Perfmon directly."));
776 printf (" %s\n", _("It can also be used in scripts that automatically create the monitoring service"));
777 printf (" %s\n", _(" configuration files."));
778 printf (" %s\n", _("Some examples:"));
779 printf (" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process"));
780
781 printf ("%s\n", _("Notes:"));
782 printf (" %s\n", _("- The NSClient service should be running on the server to get any information"));
783 printf (" %s\n", "(http://nsclient.ready2run.nl).");
784 printf (" %s\n", _("- Critical thresholds should be lower than warning thresholds"));
785 printf (" %s\n", _("- Default port 1248 is sometimes in use by other services. The error"));
786 printf (" %s\n", _("output when this happens contains \"Cannot map xxxxx to protocol number\"."));
787 printf (" %s\n", _("One fix for this is to change the port to something else on check_nt "));
788 printf (" %s\n", _("and on the client service it\'s connecting to."));
789
790 printf (UT_SUPPORT);
791}
792
793
794
795void print_usage(void)
796{
797 printf ("%s\n", _("Usage:"));
798 printf ("%s -H host -v variable [-p port] [-w warning] [-c critical]\n",progname);
799 printf ("[-l params] [-d SHOWALL] [-u] [-t timeout]\n");
800}
801
diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c
deleted file mode 100644
index 61b2d699..00000000
--- a/plugins/check_ntp.c
+++ /dev/null
@@ -1,901 +0,0 @@
1/*****************************************************************************
2*
3* Monitoring check_ntp plugin
4*
5* License: GPL
6* Copyright (c) 2006 Sean Finney <seanius@seanius.net>
7* Copyright (c) 2006-2008 Monitoring Plugins Development Team
8*
9* Description:
10*
11* This file contains the check_ntp plugin
12*
13* This plugin to check ntp servers independent of any commandline
14* programs or external libraries.
15*
16*
17* This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version.
21*
22* This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details.
26*
27* You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>.
29*
30*
31*****************************************************************************/
32
33const char *progname = "check_ntp";
34const char *copyright = "2006-2008";
35const char *email = "devel@monitoring-plugins.org";
36
37#include "common.h"
38#include "netutils.h"
39#include "utils.h"
40
41static char *server_address=NULL;
42static int verbose=0;
43static bool do_offset = false;
44static char *owarn="60";
45static char *ocrit="120";
46static bool do_jitter = false;
47static char *jwarn="5000";
48static char *jcrit="10000";
49
50int process_arguments (int, char **);
51thresholds *offset_thresholds = NULL;
52thresholds *jitter_thresholds = NULL;
53void print_help (void);
54void print_usage (void);
55
56/* number of times to perform each request to get a good average. */
57#ifndef AVG_NUM
58#define AVG_NUM 4
59#endif
60
61/* max size of control message data */
62#define MAX_CM_SIZE 468
63
64/* this structure holds everything in an ntp request/response as per rfc1305 */
65typedef struct {
66 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
67 uint8_t stratum; /* clock stratum */
68 int8_t poll; /* polling interval */
69 int8_t precision; /* precision of the local clock */
70 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */
71 uint32_t rtdisp; /* like above, but for max err to primary src */
72 uint32_t refid; /* ref clock identifier */
73 uint64_t refts; /* reference timestamp. local time local clock */
74 uint64_t origts; /* time at which request departed client */
75 uint64_t rxts; /* time at which request arrived at server */
76 uint64_t txts; /* time at which request departed server */
77} ntp_message;
78
79/* this structure holds data about results from querying offset from a peer */
80typedef struct {
81 time_t waiting; /* ts set when we started waiting for a response */
82 int num_responses; /* number of successfully received responses */
83 uint8_t stratum; /* copied verbatim from the ntp_message */
84 double rtdelay; /* converted from the ntp_message */
85 double rtdisp; /* converted from the ntp_message */
86 double offset[AVG_NUM]; /* offsets from each response */
87 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
88} ntp_server_results;
89
90/* this structure holds everything in an ntp control message as per rfc1305 */
91typedef struct {
92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
93 uint8_t op; /* R,E,M bits and Opcode */
94 uint16_t seq; /* Packet sequence */
95 uint16_t status; /* Clock status */
96 uint16_t assoc; /* Association */
97 uint16_t offset; /* Similar to TCP sequence # */
98 uint16_t count; /* # bytes of data */
99 char data[MAX_CM_SIZE]; /* ASCII data of the request */
100 /* NB: not necessarily NULL terminated! */
101} ntp_control_message;
102
103/* this is an association/status-word pair found in control packet responses */
104typedef struct {
105 uint16_t assoc;
106 uint16_t status;
107} ntp_assoc_status_pair;
108
109/* bits 1,2 are the leap indicator */
110#define LI_MASK 0xc0
111#define LI(x) ((x&LI_MASK)>>6)
112#define LI_SET(x,y) do{ x |= ((y<<6)&LI_MASK); }while(0)
113/* and these are the values of the leap indicator */
114#define LI_NOWARNING 0x00
115#define LI_EXTRASEC 0x01
116#define LI_MISSINGSEC 0x02
117#define LI_ALARM 0x03
118/* bits 3,4,5 are the ntp version */
119#define VN_MASK 0x38
120#define VN(x) ((x&VN_MASK)>>3)
121#define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0)
122#define VN_RESERVED 0x02
123/* bits 6,7,8 are the ntp mode */
124#define MODE_MASK 0x07
125#define MODE(x) (x&MODE_MASK)
126#define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0)
127/* here are some values */
128#define MODE_CLIENT 0x03
129#define MODE_CONTROLMSG 0x06
130/* In control message, bits 8-10 are R,E,M bits */
131#define REM_MASK 0xe0
132#define REM_RESP 0x80
133#define REM_ERROR 0x40
134#define REM_MORE 0x20
135/* In control message, bits 11 - 15 are opcode */
136#define OP_MASK 0x1f
137#define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0)
138#define OP_READSTAT 0x01
139#define OP_READVAR 0x02
140/* In peer status bytes, bits 6,7,8 determine clock selection status */
141#define PEER_SEL(x) ((ntohs(x)>>8)&0x07)
142#define PEER_INCLUDED 0x04
143#define PEER_SYNCSOURCE 0x06
144
145/**
146 ** a note about the 32-bit "fixed point" numbers:
147 **
148 they are divided into halves, each being a 16-bit int in network byte order:
149 - the first 16 bits are an int on the left side of a decimal point.
150 - the second 16 bits represent a fraction n/(2^16)
151 likewise for the 64-bit "fixed point" numbers with everything doubled :)
152 **/
153
154/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point"
155 number. note that these can be used as lvalues too */
156#define L16(x) (((uint16_t*)&x)[0])
157#define R16(x) (((uint16_t*)&x)[1])
158/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
159 number. these too can be used as lvalues */
160#define L32(x) (((uint32_t*)&x)[0])
161#define R32(x) (((uint32_t*)&x)[1])
162
163/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */
164#define EPOCHDIFF 0x83aa7e80UL
165
166/* extract a 32-bit ntp fixed point number into a double */
167#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x))/65536.0)
168
169/* likewise for a 64-bit ntp fp number */
170#define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\
171 (ntohl(L32(n))-EPOCHDIFF) + \
172 (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\
173 0)
174
175/* convert a struct timeval to a double */
176#define TVasDOUBLE(x) (double)(x.tv_sec+(0.000001*x.tv_usec))
177
178/* convert an ntp 64-bit fp number to a struct timeval */
179#define NTP64toTV(n,t) \
180 do{ if(!n) t.tv_sec = t.tv_usec = 0; \
181 else { \
182 t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \
183 t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \
184 } \
185 }while(0)
186
187/* convert a struct timeval to an ntp 64-bit fp number */
188#define TVtoNTP64(t,n) \
189 do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \
190 else { \
191 L32(n)=htonl(t.tv_sec + EPOCHDIFF); \
192 R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \
193 } \
194 } while(0)
195
196/* NTP control message header is 12 bytes, plus any data in the data
197 * field, plus null padding to the nearest 32-bit boundary per rfc.
198 */
199#define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((ntohs(m.count)%4)?4-(ntohs(m.count)%4):0))
200
201/* finally, a little helper or two for debugging: */
202#define DBG(x) do{if(verbose>1){ x; }}while(0);
203#define PRINTSOCKADDR(x) \
204 do{ \
205 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\
206 }while(0);
207
208/* calculate the offset of the local clock */
209static inline double calc_offset(const ntp_message *m, const struct timeval *t){
210 double client_tx, peer_rx, peer_tx, client_rx;
211 client_tx = NTP64asDOUBLE(m->origts);
212 peer_rx = NTP64asDOUBLE(m->rxts);
213 peer_tx = NTP64asDOUBLE(m->txts);
214 client_rx=TVasDOUBLE((*t));
215 return (.5*((peer_tx-client_rx)+(peer_rx-client_tx)));
216}
217
218/* print out a ntp packet in human readable/debuggable format */
219void print_ntp_message(const ntp_message *p){
220 struct timeval ref, orig, rx, tx;
221
222 NTP64toTV(p->refts,ref);
223 NTP64toTV(p->origts,orig);
224 NTP64toTV(p->rxts,rx);
225 NTP64toTV(p->txts,tx);
226
227 printf("packet contents:\n");
228 printf("\tflags: 0x%.2x\n", p->flags);
229 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK);
230 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK);
231 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK);
232 printf("\tstratum = %d\n", p->stratum);
233 printf("\tpoll = %g\n", pow(2, p->poll));
234 printf("\tprecision = %g\n", pow(2, p->precision));
235 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay));
236 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp));
237 printf("\trefid = %x\n", p->refid);
238 printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts));
239 printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts));
240 printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts));
241 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts));
242}
243
244void print_ntp_control_message(const ntp_control_message *p){
245 int i=0, numpeers=0;
246 const ntp_assoc_status_pair *peer=NULL;
247
248 printf("control packet contents:\n");
249 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op);
250 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK);
251 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK);
252 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK);
253 printf("\t response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP);
254 printf("\t more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE);
255 printf("\t error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR);
256 printf("\t op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK);
257 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq));
258 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status));
259 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc));
260 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset));
261 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count));
262 numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair));
263 if(p->op&REM_RESP && p->op&OP_READSTAT){
264 peer=(ntp_assoc_status_pair*)p->data;
265 for(i=0;i<numpeers;i++){
266 printf("\tpeer id %.2x status %.2x",
267 ntohs(peer[i].assoc), ntohs(peer[i].status));
268 if (PEER_SEL(peer[i].status) >= PEER_INCLUDED){
269 if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){
270 printf(" <-- current sync source");
271 } else {
272 printf(" <-- current sync candidate");
273 }
274 }
275 printf("\n");
276 }
277 }
278}
279
280void setup_request(ntp_message *p){
281 struct timeval t;
282
283 memset(p, 0, sizeof(ntp_message));
284 LI_SET(p->flags, LI_ALARM);
285 VN_SET(p->flags, 4);
286 MODE_SET(p->flags, MODE_CLIENT);
287 p->poll=4;
288 p->precision=(int8_t)0xfa;
289 L16(p->rtdelay)=htons(1);
290 L16(p->rtdisp)=htons(1);
291
292 gettimeofday(&t, NULL);
293 TVtoNTP64(t,p->txts);
294}
295
296/* select the "best" server from a list of servers, and return its index.
297 * this is done by filtering servers based on stratum, dispersion, and
298 * finally round-trip delay. */
299int best_offset_server(const ntp_server_results *slist, int nservers){
300 int cserver=0, best_server=-1;
301
302 /* for each server */
303 for(cserver=0; cserver<nservers; cserver++){
304 /* We don't want any servers that fails these tests */
305 /* Sort out servers that didn't respond or responede with a 0 stratum;
306 * stratum 0 is for reference clocks so no NTP server should ever report
307 * a stratum 0 */
308 if ( slist[cserver].stratum == 0){
309 if (verbose) printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
310 continue;
311 }
312 /* Sort out servers with error flags */
313 if ( LI(slist[cserver].flags) == LI_ALARM ){
314 if (verbose) printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
315 continue;
316 }
317
318 /* If we don't have a server yet, use the first one */
319 if (best_server == -1) {
320 best_server = cserver;
321 DBG(printf("using peer %d as our first candidate\n", best_server));
322 continue;
323 }
324
325 /* compare the server to the best one we've seen so far */
326 /* does it have an equal or better stratum? */
327 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server));
328 if(slist[cserver].stratum <= slist[best_server].stratum){
329 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server));
330 /* does it have an equal or better dispersion? */
331 if(slist[cserver].rtdisp <= slist[best_server].rtdisp){
332 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server));
333 /* does it have a better rtdelay? */
334 if(slist[cserver].rtdelay < slist[best_server].rtdelay){
335 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server));
336 best_server = cserver;
337 DBG(printf("peer %d is now our best candidate\n", best_server));
338 }
339 }
340 }
341 }
342
343 if(best_server >= 0) {
344 DBG(printf("best server selected: peer %d\n", best_server));
345 return best_server;
346 } else {
347 DBG(printf("no peers meeting synchronization criteria :(\n"));
348 return -1;
349 }
350}
351
352/* do everything we need to get the total average offset
353 * - we use a certain amount of parallelization with poll() to ensure
354 * we don't waste time sitting around waiting for single packets.
355 * - we also "manually" handle resolving host names and connecting, because
356 * we have to do it in a way that our lazy macros don't handle currently :( */
357double offset_request(const char *host, int *status){
358 int i=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0;
359 int servers_completed=0, one_read=0, servers_readable=0, best_index=-1;
360 time_t now_time=0, start_ts=0;
361 ntp_message *req=NULL;
362 double avg_offset=0.;
363 struct timeval recv_time;
364 struct addrinfo *ai=NULL, *ai_tmp=NULL, hints;
365 struct pollfd *ufds=NULL;
366 ntp_server_results *servers=NULL;
367
368 /* setup hints to only return results from getaddrinfo that we'd like */
369 memset(&hints, 0, sizeof(struct addrinfo));
370 hints.ai_family = address_family;
371 hints.ai_protocol = IPPROTO_UDP;
372 hints.ai_socktype = SOCK_DGRAM;
373
374 /* fill in ai with the list of hosts resolved by the host name */
375 ga_result = getaddrinfo(host, "123", &hints, &ai);
376 if(ga_result!=0){
377 die(STATE_UNKNOWN, "error getting address for %s: %s\n",
378 host, gai_strerror(ga_result));
379 }
380
381 /* count the number of returned hosts, and allocate stuff accordingly */
382 for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; }
383 req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts);
384 if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array");
385 socklist=(int*)malloc(sizeof(int)*num_hosts);
386 if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array");
387 ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts);
388 if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array");
389 servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts);
390 if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array");
391 memset(servers, 0, sizeof(ntp_server_results)*num_hosts);
392 DBG(printf("Found %d peers to check\n", num_hosts));
393
394 /* setup each socket for writing, and the corresponding struct pollfd */
395 ai_tmp=ai;
396 for(i=0;ai_tmp;i++){
397 socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
398 if(socklist[i] == -1) {
399 perror(NULL);
400 die(STATE_UNKNOWN, "can not create new socket");
401 }
402 if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){
403 /* don't die here, because it is enough if there is one server
404 answering in time. This also would break for dual ipv4/6 stacked
405 ntp servers when the client only supports on of them.
406 */
407 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno)));
408 } else {
409 ufds[i].fd=socklist[i];
410 ufds[i].events=POLLIN;
411 ufds[i].revents=0;
412 }
413 ai_tmp = ai_tmp->ai_next;
414 }
415
416 /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds
417 * have passed in order to ensure post-processing and jitter time. */
418 now_time=start_ts=time(NULL);
419 while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){
420 /* loop through each server and find each one which hasn't
421 * been touched in the past second or so and is still lacking
422 * some responses. for each of these servers, send a new request,
423 * and update the "waiting" timestamp with the current time. */
424 now_time=time(NULL);
425
426 for(i=0; i<num_hosts; i++){
427 if(servers[i].waiting<now_time && servers[i].num_responses<AVG_NUM){
428 if(verbose && servers[i].waiting != 0) printf("re-");
429 if(verbose) printf("sending request to peer %d\n", i);
430 setup_request(&req[i]);
431 write(socklist[i], &req[i], sizeof(ntp_message));
432 servers[i].waiting=now_time;
433 break;
434 }
435 }
436
437 /* quickly poll for any sockets with pending data */
438 servers_readable=poll(ufds, num_hosts, 100);
439 if(servers_readable==-1){
440 perror("polling ntp sockets");
441 die(STATE_UNKNOWN, "communication errors");
442 }
443
444 /* read from any sockets with pending data */
445 for(i=0; servers_readable && i<num_hosts; i++){
446 if(ufds[i].revents&POLLIN && servers[i].num_responses < AVG_NUM){
447 if(verbose) {
448 printf("response from peer %d: ", i);
449 }
450
451 read(ufds[i].fd, &req[i], sizeof(ntp_message));
452 gettimeofday(&recv_time, NULL);
453 DBG(print_ntp_message(&req[i]));
454 respnum=servers[i].num_responses++;
455 servers[i].offset[respnum]=calc_offset(&req[i], &recv_time);
456 if(verbose) {
457 printf("offset %.10g\n", servers[i].offset[respnum]);
458 }
459 servers[i].stratum=req[i].stratum;
460 servers[i].rtdisp=NTP32asDOUBLE(req[i].rtdisp);
461 servers[i].rtdelay=NTP32asDOUBLE(req[i].rtdelay);
462 servers[i].waiting=0;
463 servers[i].flags=req[i].flags;
464 servers_readable--;
465 one_read = 1;
466 if(servers[i].num_responses==AVG_NUM) servers_completed++;
467 }
468 }
469 /* lather, rinse, repeat. */
470 }
471
472 if (one_read == 0) {
473 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
474 }
475
476 /* now, pick the best server from the list */
477 best_index=best_offset_server(servers, num_hosts);
478 if(best_index < 0){
479 *status=STATE_UNKNOWN;
480 } else {
481 /* finally, calculate the average offset */
482 for(i=0; i<servers[best_index].num_responses;i++){
483 avg_offset+=servers[best_index].offset[i];
484 }
485 avg_offset/=servers[best_index].num_responses;
486 }
487
488 /* cleanup */
489 /* FIXME: Not closing the socket to avoid reuse of the local port
490 * which can cause old NTP packets to be read instead of NTP control
491 * packets in jitter_request(). THERE MUST BE ANOTHER WAY...
492 * for(j=0; j<num_hosts; j++){ close(socklist[j]); } */
493 free(socklist);
494 free(ufds);
495 free(servers);
496 free(req);
497 freeaddrinfo(ai);
498
499 if(verbose) printf("overall average offset: %.10g\n", avg_offset);
500 return avg_offset;
501}
502
503void
504setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
505 memset(p, 0, sizeof(ntp_control_message));
506 LI_SET(p->flags, LI_NOWARNING);
507 VN_SET(p->flags, VN_RESERVED);
508 MODE_SET(p->flags, MODE_CONTROLMSG);
509 OP_SET(p->op, opcode);
510 p->seq = htons(seq);
511 /* Remaining fields are zero for requests */
512}
513
514/* XXX handle responses with the error bit set */
515double jitter_request(int *status){
516 int conn=-1, i, npeers=0, num_candidates=0;
517 bool syncsource_found = false;
518 int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0;
519 int peers_size=0, peer_offset=0;
520 ntp_assoc_status_pair *peers=NULL;
521 ntp_control_message req;
522 const char *getvar = "jitter";
523 double rval = 0.0, jitter = -1.0;
524 char *startofvalue=NULL, *nptr=NULL;
525 void *tmp;
526
527 /* Long-winded explanation:
528 * Getting the jitter requires a number of steps:
529 * 1) Send a READSTAT request.
530 * 2) Interpret the READSTAT reply
531 * a) The data section contains a list of peer identifiers (16 bits)
532 * and associated status words (16 bits)
533 * b) We want the value of 0x06 in the SEL (peer selection) value,
534 * which means "current synchronizatin source". If that's missing,
535 * we take anything better than 0x04 (see the rfc for details) but
536 * set a minimum of warning.
537 * 3) Send a READVAR request for information on each peer identified
538 * in 2b greater than the minimum selection value.
539 * 4) Extract the jitter value from the data[] (it's ASCII)
540 */
541 my_udp_connect(server_address, 123, &conn);
542
543 /* keep sending requests until the server stops setting the
544 * REM_MORE bit, though usually this is only 1 packet. */
545 do{
546 setup_control_request(&req, OP_READSTAT, 1);
547 DBG(printf("sending READSTAT request"));
548 write(conn, &req, SIZEOF_NTPCM(req));
549 DBG(print_ntp_control_message(&req));
550 /* Attempt to read the largest size packet possible */
551 req.count=htons(MAX_CM_SIZE);
552 DBG(printf("receiving READSTAT response"))
553 read(conn, &req, SIZEOF_NTPCM(req));
554 DBG(print_ntp_control_message(&req));
555 /* Each peer identifier is 4 bytes in the data section, which
556 * we represent as a ntp_assoc_status_pair datatype.
557 */
558 peers_size+=ntohs(req.count);
559 if((tmp=realloc(peers, peers_size)) == NULL)
560 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
561 peers=tmp;
562 memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count));
563 npeers=peers_size/sizeof(ntp_assoc_status_pair);
564 peer_offset+=ntohs(req.count);
565 } while(req.op&REM_MORE);
566
567 /* first, let's find out if we have a sync source, or if there are
568 * at least some candidates. in the case of the latter we'll issue
569 * a warning but go ahead with the check on them. */
570 for (i = 0; i < npeers; i++){
571 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){
572 num_candidates++;
573 if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){
574 syncsource_found = true;
575 min_peer_sel=PEER_SYNCSOURCE;
576 }
577 }
578 }
579 if(verbose) printf("%d candidate peers available\n", num_candidates);
580 if(verbose && syncsource_found) printf("synchronization source found\n");
581 if(! syncsource_found){
582 *status = STATE_UNKNOWN;
583 if(verbose) printf("warning: no synchronization source found\n");
584 }
585
586
587 for (run=0; run<AVG_NUM; run++){
588 if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM);
589 for (i = 0; i < npeers; i++){
590 /* Only query this server if it is the current sync source */
591 if (PEER_SEL(peers[i].status) >= min_peer_sel){
592 char jitter_data[MAX_CM_SIZE+1];
593 size_t jitter_data_count;
594
595 num_selected++;
596 setup_control_request(&req, OP_READVAR, 2);
597 req.assoc = peers[i].assoc;
598 /* By spec, putting the variable name "jitter" in the request
599 * should cause the server to provide _only_ the jitter value.
600 * thus reducing net traffic, guaranteeing us only a single
601 * datagram in reply, and making interpretation much simpler
602 */
603 /* Older servers doesn't know what jitter is, so if we get an
604 * error on the first pass we redo it with "dispersion" */
605 strncpy(req.data, getvar, MAX_CM_SIZE-1);
606 req.count = htons(strlen(getvar));
607 DBG(printf("sending READVAR request...\n"));
608 write(conn, &req, SIZEOF_NTPCM(req));
609 DBG(print_ntp_control_message(&req));
610
611 req.count = htons(MAX_CM_SIZE);
612 DBG(printf("receiving READVAR response...\n"));
613 read(conn, &req, SIZEOF_NTPCM(req));
614 DBG(print_ntp_control_message(&req));
615
616 if(req.op&REM_ERROR && strstr(getvar, "jitter")) {
617 if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n");
618 getvar = "dispersion";
619 num_selected--;
620 i--;
621 continue;
622 }
623
624 /* get to the float value */
625 if(verbose) {
626 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc));
627 }
628 if((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)){
629 die(STATE_UNKNOWN,
630 _("jitter response too large (%lu bytes)\n"),
631 (unsigned long)jitter_data_count);
632 }
633 memcpy(jitter_data, req.data, jitter_data_count);
634 jitter_data[jitter_data_count] = '\0';
635 startofvalue = strchr(jitter_data, '=');
636 if(startofvalue != NULL) {
637 startofvalue++;
638 jitter = strtod(startofvalue, &nptr);
639 }
640 if(startofvalue == NULL || startofvalue==nptr){
641 printf("warning: unable to read server jitter response.\n");
642 *status = STATE_UNKNOWN;
643 } else {
644 if(verbose) printf("%g\n", jitter);
645 num_valid++;
646 rval += jitter;
647 }
648 }
649 }
650 if(verbose){
651 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
652 }
653 }
654
655 rval = num_valid ? rval / num_valid : -1.0;
656
657 close(conn);
658 if(peers!=NULL) free(peers);
659 /* If we return -1.0, it means no synchronization source was found */
660 return rval;
661}
662
663int process_arguments(int argc, char **argv){
664 int c;
665 int option=0;
666 static struct option longopts[] = {
667 {"version", no_argument, 0, 'V'},
668 {"help", no_argument, 0, 'h'},
669 {"verbose", no_argument, 0, 'v'},
670 {"use-ipv4", no_argument, 0, '4'},
671 {"use-ipv6", no_argument, 0, '6'},
672 {"warning", required_argument, 0, 'w'},
673 {"critical", required_argument, 0, 'c'},
674 {"jwarn", required_argument, 0, 'j'},
675 {"jcrit", required_argument, 0, 'k'},
676 {"timeout", required_argument, 0, 't'},
677 {"hostname", required_argument, 0, 'H'},
678 {0, 0, 0, 0}
679 };
680
681
682 if (argc < 2)
683 usage ("\n");
684
685 while (1) {
686 c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option);
687 if (c == -1 || c == EOF || c == 1)
688 break;
689
690 switch (c) {
691 case 'h':
692 print_help();
693 exit(STATE_UNKNOWN);
694 break;
695 case 'V':
696 print_revision(progname, NP_VERSION);
697 exit(STATE_UNKNOWN);
698 break;
699 case 'v':
700 verbose++;
701 break;
702 case 'w':
703 do_offset = true;
704 owarn = optarg;
705 break;
706 case 'c':
707 do_offset = true;
708 ocrit = optarg;
709 break;
710 case 'j':
711 do_jitter = true;
712 jwarn = optarg;
713 break;
714 case 'k':
715 do_jitter = true;
716 jcrit = optarg;
717 break;
718 case 'H':
719 if(!is_host(optarg))
720 usage2(_("Invalid hostname/address"), optarg);
721 server_address = strdup(optarg);
722 break;
723 case 't':
724 socket_timeout=atoi(optarg);
725 break;
726 case '4':
727 address_family = AF_INET;
728 break;
729 case '6':
730#ifdef USE_IPV6
731 address_family = AF_INET6;
732#else
733 usage4 (_("IPv6 support not available"));
734#endif
735 break;
736 case '?':
737 /* print short usage statement if args not parsable */
738 usage5 ();
739 break;
740 }
741 }
742
743 if(server_address == NULL){
744 usage4(_("Hostname was not supplied"));
745 }
746
747 return 0;
748}
749
750char *perfd_offset (double offset)
751{
752 return fperfdata ("offset", offset, "s",
753 true, offset_thresholds->warning->end,
754 true, offset_thresholds->critical->end,
755 false, 0, false, 0);
756}
757
758char *perfd_jitter (double jitter)
759{
760 return fperfdata ("jitter", jitter, "s",
761 do_jitter, jitter_thresholds->warning->end,
762 do_jitter, jitter_thresholds->critical->end,
763 true, 0, false, 0);
764}
765
766int main(int argc, char *argv[]){
767 int result, offset_result, jitter_result;
768 double offset=0, jitter=0;
769 char *result_line, *perfdata_line;
770
771 setlocale (LC_ALL, "");
772 bindtextdomain (PACKAGE, LOCALEDIR);
773 textdomain (PACKAGE);
774
775 result = offset_result = jitter_result = STATE_OK;
776
777 /* Parse extra opts if any */
778 argv=np_extra_opts (&argc, argv, progname);
779
780 if (process_arguments (argc, argv) == ERROR)
781 usage4 (_("Could not parse arguments"));
782
783 set_thresholds(&offset_thresholds, owarn, ocrit);
784 set_thresholds(&jitter_thresholds, jwarn, jcrit);
785
786 /* initialize alarm signal handling */
787 signal (SIGALRM, socket_timeout_alarm_handler);
788
789 /* set socket timeout */
790 alarm (socket_timeout);
791
792 offset = offset_request(server_address, &offset_result);
793 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN.
794 * Now we'll only do that is the offset thresholds were set */
795 if (do_offset && offset_result == STATE_UNKNOWN) {
796 result = STATE_CRITICAL;
797 } else {
798 result = get_status(fabs(offset), offset_thresholds);
799 }
800
801 /* If not told to check the jitter, we don't even send packets.
802 * jitter is checked using NTP control packets, which not all
803 * servers recognize. Trying to check the jitter on OpenNTPD
804 * (for example) will result in an error
805 */
806 if(do_jitter){
807 jitter=jitter_request(&jitter_result);
808 result = max_state_alt(result, get_status(jitter, jitter_thresholds));
809 /* -1 indicates that we couldn't calculate the jitter
810 * Only overrides STATE_OK from the offset */
811 if(jitter == -1.0 && result == STATE_OK)
812 result = STATE_UNKNOWN;
813 }
814 result = max_state_alt(result, jitter_result);
815
816 switch (result) {
817 case STATE_CRITICAL :
818 xasprintf(&result_line, _("NTP CRITICAL:"));
819 break;
820 case STATE_WARNING :
821 xasprintf(&result_line, _("NTP WARNING:"));
822 break;
823 case STATE_OK :
824 xasprintf(&result_line, _("NTP OK:"));
825 break;
826 default :
827 xasprintf(&result_line, _("NTP UNKNOWN:"));
828 break;
829 }
830 if(offset_result == STATE_UNKNOWN){
831 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
832 xasprintf(&perfdata_line, "");
833 } else {
834 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
835 xasprintf(&perfdata_line, "%s", perfd_offset(offset));
836 }
837 if (do_jitter) {
838 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter);
839 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter));
840 }
841 printf("%s|%s\n", result_line, perfdata_line);
842
843 if(server_address!=NULL) free(server_address);
844 return result;
845}
846
847
848
849void print_help(void){
850 print_revision(progname, NP_VERSION);
851
852 printf ("Copyright (c) 2006 Sean Finney\n");
853 printf (COPYRIGHT, copyright, email);
854
855 printf ("%s\n", _("This plugin checks the selected ntp server"));
856
857 printf ("\n\n");
858
859 print_usage();
860 printf (UT_HELP_VRSN);
861 printf (UT_EXTRA_OPTS);
862 printf (UT_HOST_PORT, 'p', "123");
863 printf (UT_IPv46);
864 printf (" %s\n", "-w, --warning=THRESHOLD");
865 printf (" %s\n", _("Offset to result in warning status (seconds)"));
866 printf (" %s\n", "-c, --critical=THRESHOLD");
867 printf (" %s\n", _("Offset to result in critical status (seconds)"));
868 printf (" %s\n", "-j, --jwarn=THRESHOLD");
869 printf (" %s\n", _("Warning threshold for jitter"));
870 printf (" %s\n", "-k, --jcrit=THRESHOLD");
871 printf (" %s\n", _("Critical threshold for jitter"));
872 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
873 printf (UT_VERBOSE);
874
875 printf("\n");
876 printf("%s\n", _("Notes:"));
877 printf(UT_THRESHOLDS_NOTES);
878
879 printf("\n");
880 printf("%s\n", _("Examples:"));
881 printf(" %s\n", _("Normal offset check:"));
882 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1"));
883 printf("\n");
884 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available"));
885 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
886 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
887
888 printf (UT_SUPPORT);
889
890 printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
891 printf ("%s\n\n", _("check_ntp_time instead."));
892}
893
894void
895print_usage(void)
896{
897 printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
898 printf ("%s\n\n", _("check_ntp_time instead."));
899 printf ("%s\n", _("Usage:"));
900 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n", progname);
901}
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index 464a9e10..b5cfb460 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -1,88 +1,78 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ntp_peer plugin 3 * Monitoring check_ntp_peer plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006 Sean Finney <seanius@seanius.net> 6 * Copyright (c) 2006 Sean Finney <seanius@seanius.net>
7* Copyright (c) 2006-2008 Monitoring Plugins Development Team 7 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_ntp_peer plugin 11 * This file contains the check_ntp_peer plugin
12* 12 *
13* This plugin checks an NTP server independent of any commandline 13 * This plugin checks an NTP server independent of any commandline
14* programs or external libraries. 14 * programs or external libraries.
15* 15 *
16* Use this plugin to check the health of an NTP server. It supports 16 * Use this plugin to check the health of an NTP server. It supports
17* checking the offset with the sync peer, the jitter and stratum. This 17 * checking the offset with the sync peer, the jitter and stratum. This
18* plugin will not check the clock offset between the local host and NTP 18 * plugin will not check the clock offset between the local host and NTP
19* server; please use check_ntp_time for that purpose. 19 * server; please use check_ntp_time for that purpose.
20* 20 *
21* 21 *
22* This program is free software: you can redistribute it and/or modify 22 * This program is free software: you can redistribute it and/or modify
23* it under the terms of the GNU General Public License as published by 23 * it under the terms of the GNU General Public License as published by
24* the Free Software Foundation, either version 3 of the License, or 24 * the Free Software Foundation, either version 3 of the License, or
25* (at your option) any later version. 25 * (at your option) any later version.
26* 26 *
27* This program is distributed in the hope that it will be useful, 27 * This program is distributed in the hope that it will be useful,
28* but WITHOUT ANY WARRANTY; without even the implied warranty of 28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30* GNU General Public License for more details. 30 * GNU General Public License for more details.
31* 31 *
32* You should have received a copy of the GNU General Public License 32 * You should have received a copy of the GNU General Public License
33* along with this program. If not, see <http://www.gnu.org/licenses/>. 33 * along with this program. If not, see <http://www.gnu.org/licenses/>.
34* 34 *
35* 35 *
36*****************************************************************************/ 36 *****************************************************************************/
37 37
38const char *progname = "check_ntp_peer"; 38const char *progname = "check_ntp_peer";
39const char *copyright = "2006-2008"; 39const char *copyright = "2006-2024";
40const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
41 41
42#include "output.h"
43#include "perfdata.h"
44#include <openssl/x509.h>
45#include "thresholds.h"
42#include "common.h" 46#include "common.h"
43#include "netutils.h" 47#include "netutils.h"
44#include "utils.h" 48#include "utils.h"
49#include "../lib/states.h"
50#include "check_ntp_peer.d/config.h"
45 51
46static char *server_address=NULL; 52static int verbose = 0;
47static int port=123; 53
48static int verbose=0; 54typedef struct {
49static bool quiet = false; 55 int errorcode;
50static char *owarn="60"; 56 check_ntp_peer_config config;
51static char *ocrit="120"; 57} check_ntp_peer_config_wrapper;
52static bool do_stratum = false; 58static check_ntp_peer_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
53static char *swarn="-1:16"; 59static void print_help(void);
54static char *scrit="-1:16"; 60void print_usage(void);
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;
62static bool li_alarm = false;
63
64int process_arguments (int, char **);
65thresholds *offset_thresholds = NULL;
66thresholds *jitter_thresholds = NULL;
67thresholds *stratum_thresholds = NULL;
68thresholds *truechimer_thresholds = NULL;
69void print_help (void);
70void print_usage (void);
71 61
72/* max size of control message data */ 62/* max size of control message data */
73#define MAX_CM_SIZE 468 63#define MAX_CM_SIZE 468
74 64
75/* this structure holds everything in an ntp control message as per rfc1305 */ 65/* this structure holds everything in an ntp control message as per rfc1305 */
76typedef struct { 66typedef struct {
77 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 67 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
78 uint8_t op; /* R,E,M bits and Opcode */ 68 uint8_t op; /* R,E,M bits and Opcode */
79 uint16_t seq; /* Packet sequence */ 69 uint16_t seq; /* Packet sequence */
80 uint16_t status; /* Clock status */ 70 uint16_t status; /* Clock status */
81 uint16_t assoc; /* Association */ 71 uint16_t assoc; /* Association */
82 uint16_t offset; /* Similar to TCP sequence # */ 72 uint16_t offset; /* Similar to TCP sequence # */
83 uint16_t count; /* # bytes of data */ 73 uint16_t count; /* # bytes of data */
84 char data[MAX_CM_SIZE]; /* ASCII data of the request */ 74 char data[MAX_CM_SIZE]; /* ASCII data of the request */
85 /* NB: not necessarily NULL terminated! */ 75 /* NB: not necessarily NULL terminated! */
86} ntp_control_message; 76} ntp_control_message;
87 77
88/* this is an association/status-word pair found in control packet responses */ 78/* this is an association/status-word pair found in control packet responses */
@@ -93,82 +83,97 @@ typedef struct {
93 83
94/* bits 1,2 are the leap indicator */ 84/* bits 1,2 are the leap indicator */
95#define LI_MASK 0xc0 85#define LI_MASK 0xc0
96#define LI(x) ((x&LI_MASK)>>6) 86#define LI(x) ((x & LI_MASK) >> 6)
97#define LI_SET(x,y) do{ x |= ((y<<6)&LI_MASK); }while(0) 87#define LI_SET(x, y) \
88 do { \
89 x |= ((y << 6) & LI_MASK); \
90 } while (0)
98/* and these are the values of the leap indicator */ 91/* and these are the values of the leap indicator */
99#define LI_NOWARNING 0x00 92#define LI_NOWARNING 0x00
100#define LI_EXTRASEC 0x01 93#define LI_EXTRASEC 0x01
101#define LI_MISSINGSEC 0x02 94#define LI_MISSINGSEC 0x02
102#define LI_ALARM 0x03 95#define LI_ALARM 0x03
103/* bits 3,4,5 are the ntp version */ 96/* bits 3,4,5 are the ntp version */
104#define VN_MASK 0x38 97#define VN_MASK 0x38
105#define VN(x) ((x&VN_MASK)>>3) 98#define VN(x) ((x & VN_MASK) >> 3)
106#define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) 99#define VN_SET(x, y) \
100 do { \
101 x |= ((y << 3) & VN_MASK); \
102 } while (0)
107#define VN_RESERVED 0x02 103#define VN_RESERVED 0x02
108/* bits 6,7,8 are the ntp mode */ 104/* bits 6,7,8 are the ntp mode */
109#define MODE_MASK 0x07 105#define MODE_MASK 0x07
110#define MODE(x) (x&MODE_MASK) 106#define MODE(x) (x & MODE_MASK)
111#define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) 107#define MODE_SET(x, y) \
108 do { \
109 x |= (y & MODE_MASK); \
110 } while (0)
112/* here are some values */ 111/* here are some values */
113#define MODE_CLIENT 0x03 112#define MODE_CLIENT 0x03
114#define MODE_CONTROLMSG 0x06 113#define MODE_CONTROLMSG 0x06
115/* In control message, bits 8-10 are R,E,M bits */ 114/* In control message, bits 8-10 are R,E,M bits */
116#define REM_MASK 0xe0 115#define REM_MASK 0xe0
117#define REM_RESP 0x80 116#define REM_RESP 0x80
118#define REM_ERROR 0x40 117#define REM_ERROR 0x40
119#define REM_MORE 0x20 118#define REM_MORE 0x20
120/* In control message, bits 11 - 15 are opcode */ 119/* In control message, bits 11 - 15 are opcode */
121#define OP_MASK 0x1f 120#define OP_MASK 0x1f
122#define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) 121#define OP_SET(x, y) \
122 do { \
123 x |= (y & OP_MASK); \
124 } while (0)
123#define OP_READSTAT 0x01 125#define OP_READSTAT 0x01
124#define OP_READVAR 0x02 126#define OP_READVAR 0x02
125/* In peer status bytes, bits 6,7,8 determine clock selection status */ 127/* In peer status bytes, bits 6,7,8 determine clock selection status */
126#define PEER_SEL(x) ((ntohs(x)>>8)&0x07) 128#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07)
127#define PEER_TRUECHIMER 0x02 129#define PEER_TRUECHIMER 0x02
128#define PEER_INCLUDED 0x04 130#define PEER_INCLUDED 0x04
129#define PEER_SYNCSOURCE 0x06 131#define PEER_SYNCSOURCE 0x06
130 132
131/* NTP control message header is 12 bytes, plus any data in the data 133/* NTP control message header is 12 bytes, plus any data in the data
132 * field, plus null padding to the nearest 32-bit boundary per rfc. 134 * field, plus null padding to the nearest 32-bit boundary per rfc.
133 */ 135 */
134#define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((ntohs(m.count)%4)?4-(ntohs(m.count)%4):0)) 136#define SIZEOF_NTPCM(m) \
137 (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0))
135 138
136/* finally, a little helper or two for debugging: */ 139/* finally, a little helper or two for debugging: */
137#define DBG(x) do{if(verbose>1){ x; }}while(0); 140#define DBG(x) \
138#define PRINTSOCKADDR(x) \ 141 do { \
139 do{ \ 142 if (verbose > 1) { \
140 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ 143 x; \
141 }while(0); 144 } \
142 145 } while (0);
143void print_ntp_control_message(const ntp_control_message *p){ 146#define PRINTSOCKADDR(x) \
144 int i=0, numpeers=0; 147 do { \
145 const ntp_assoc_status_pair *peer=NULL; 148 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
146 149 } while (0);
150
151void print_ntp_control_message(const ntp_control_message *message) {
147 printf("control packet contents:\n"); 152 printf("control packet contents:\n");
148 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); 153 printf("\tflags: 0x%.2x , 0x%.2x\n", message->flags, message->op);
149 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); 154 printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK);
150 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); 155 printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK);
151 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); 156 printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK);
152 printf("\t response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP); 157 printf("\t response=%d (0x%.2x)\n", (message->op & REM_RESP) > 0, message->op & REM_RESP);
153 printf("\t more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE); 158 printf("\t more=%d (0x%.2x)\n", (message->op & REM_MORE) > 0, message->op & REM_MORE);
154 printf("\t error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR); 159 printf("\t error=%d (0x%.2x)\n", (message->op & REM_ERROR) > 0, message->op & REM_ERROR);
155 printf("\t op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK); 160 printf("\t op=%d (0x%.2x)\n", message->op & OP_MASK, message->op & OP_MASK);
156 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); 161 printf("\tsequence: %d (0x%.2x)\n", ntohs(message->seq), ntohs(message->seq));
157 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); 162 printf("\tstatus: %d (0x%.2x)\n", ntohs(message->status), ntohs(message->status));
158 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); 163 printf("\tassoc: %d (0x%.2x)\n", ntohs(message->assoc), ntohs(message->assoc));
159 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); 164 printf("\toffset: %d (0x%.2x)\n", ntohs(message->offset), ntohs(message->offset));
160 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); 165 printf("\tcount: %d (0x%.2x)\n", ntohs(message->count), ntohs(message->count));
161 numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair)); 166
162 if(p->op&REM_RESP && p->op&OP_READSTAT){ 167 int numpeers = ntohs(message->count) / (sizeof(ntp_assoc_status_pair));
163 peer=(ntp_assoc_status_pair*)p->data; 168 if (message->op & REM_RESP && message->op & OP_READSTAT) {
164 for(i=0;i<numpeers;i++){ 169 const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)message->data;
165 printf("\tpeer id %.2x status %.2x", 170 for (int i = 0; i < numpeers; i++) {
166 ntohs(peer[i].assoc), ntohs(peer[i].status)); 171 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
167 if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){ 172 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
168 printf(" <-- current sync source"); 173 printf(" <-- current sync source");
169 } else if(PEER_SEL(peer[i].status) >= PEER_INCLUDED){ 174 } else if (PEER_SEL(peer[i].status) >= PEER_INCLUDED) {
170 printf(" <-- current sync candidate"); 175 printf(" <-- current sync candidate");
171 } else if(PEER_SEL(peer[i].status) >= PEER_TRUECHIMER){ 176 } else if (PEER_SEL(peer[i].status) >= PEER_TRUECHIMER) {
172 printf(" <-- outlyer, but truechimer"); 177 printf(" <-- outlyer, but truechimer");
173 } 178 }
174 printf("\n"); 179 printf("\n");
@@ -176,14 +181,13 @@ void print_ntp_control_message(const ntp_control_message *p){
176 } 181 }
177} 182}
178 183
179void 184void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_t seq) {
180setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){ 185 memset(message, 0, sizeof(ntp_control_message));
181 memset(p, 0, sizeof(ntp_control_message)); 186 LI_SET(message->flags, LI_NOWARNING);
182 LI_SET(p->flags, LI_NOWARNING); 187 VN_SET(message->flags, VN_RESERVED);
183 VN_SET(p->flags, VN_RESERVED); 188 MODE_SET(message->flags, MODE_CONTROLMSG);
184 MODE_SET(p->flags, MODE_CONTROLMSG); 189 OP_SET(message->op, opcode);
185 OP_SET(p->op, opcode); 190 message->seq = htons(seq);
186 p->seq = htons(seq);
187 /* Remaining fields are zero for requests */ 191 /* Remaining fields are zero for requests */
188} 192}
189 193
@@ -195,25 +199,28 @@ setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
195 * positive value means a success retrieving the value. 199 * positive value means a success retrieving the value.
196 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is 200 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is
197 * the return value of the function. 201 * the return value of the function.
198 * status is pretty much useless as syncsource_found is a global variable 202 */
199 * used later in main to check is the server was synchronized. It works 203typedef struct {
200 * so I left it alone */ 204 mp_state_enum state;
201int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum, int *num_truechimers){ 205 mp_state_enum offset_result;
202 int conn=-1, i, npeers=0, num_candidates=0; 206 double offset;
203 double tmp_offset = 0; 207 double jitter;
204 int min_peer_sel=PEER_INCLUDED; 208 long stratum;
205 int peers_size=0, peer_offset=0; 209 int num_truechimers;
206 int status; 210 bool syncsource_found;
207 ntp_assoc_status_pair *peers=NULL; 211 bool li_alarm;
208 ntp_control_message req; 212} ntp_request_result;
209 const char *getvar = "stratum,offset,jitter"; 213ntp_request_result ntp_request(const check_ntp_peer_config config) {
210 char *data, *value, *nptr; 214
211 void *tmp; 215 ntp_request_result result = {
212 216 .state = STATE_OK,
213 status = STATE_OK; 217 .offset_result = STATE_UNKNOWN,
214 *offset_result = STATE_UNKNOWN; 218 .jitter = -1,
215 *jitter = *stratum = -1; 219 .stratum = -1,
216 *num_truechimers = 0; 220 .num_truechimers = 0,
221 .syncsource_found = false,
222 .li_alarm = false,
223 };
217 224
218 /* Long-winded explanation: 225 /* Long-winded explanation:
219 * Getting the sync peer offset, jitter and stratum requires a number of 226 * Getting the sync peer offset, jitter and stratum requires a number of
@@ -231,11 +238,17 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
231 * 4) Extract the offset, jitter and stratum value from the data[] 238 * 4) Extract the offset, jitter and stratum value from the data[]
232 * (it's ASCII) 239 * (it's ASCII)
233 */ 240 */
234 my_udp_connect(server_address, port, &conn); 241 int conn = -1;
242 my_udp_connect(config.server_address, config.port, &conn);
235 243
236 /* keep sending requests until the server stops setting the 244 /* keep sending requests until the server stops setting the
237 * REM_MORE bit, though usually this is only 1 packet. */ 245 * REM_MORE bit, though usually this is only 1 packet. */
238 do{ 246 ntp_control_message req;
247 ntp_assoc_status_pair *peers = NULL;
248 int peer_offset = 0;
249 size_t peers_size = 0;
250 size_t npeers = 0;
251 do {
239 setup_control_request(&req, OP_READSTAT, 1); 252 setup_control_request(&req, OP_READSTAT, 1);
240 DBG(printf("sending READSTAT request")); 253 DBG(printf("sending READSTAT request"));
241 write(conn, &req, SIZEOF_NTPCM(req)); 254 write(conn, &req, SIZEOF_NTPCM(req));
@@ -243,63 +256,84 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
243 256
244 do { 257 do {
245 /* Attempt to read the largest size packet possible */ 258 /* Attempt to read the largest size packet possible */
246 req.count=htons(MAX_CM_SIZE); 259 req.count = htons(MAX_CM_SIZE);
247 DBG(printf("receiving READSTAT response")) 260 DBG(printf("receiving READSTAT response"))
248 if(read(conn, &req, SIZEOF_NTPCM(req)) == -1) 261 if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) {
249 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 262 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
263 }
250 DBG(print_ntp_control_message(&req)); 264 DBG(print_ntp_control_message(&req));
251 /* discard obviously invalid packets */ 265 /* discard obviously invalid packets */
252 if (ntohs(req.count) > MAX_CM_SIZE) 266 if (ntohs(req.count) > MAX_CM_SIZE) {
253 die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n"); 267 die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n");
254 } while (!(req.op&OP_READSTAT && ntohs(req.seq) == 1)); 268 }
269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1));
255 270
256 if (LI(req.flags) == LI_ALARM) li_alarm = true; 271 if (LI(req.flags) == LI_ALARM) {
272 result.li_alarm = true;
273 }
257 /* Each peer identifier is 4 bytes in the data section, which 274 /* Each peer identifier is 4 bytes in the data section, which
258 * we represent as a ntp_assoc_status_pair datatype. 275 * we represent as a ntp_assoc_status_pair datatype.
259 */ 276 */
260 peers_size+=ntohs(req.count); 277 peers_size += ntohs(req.count);
261 if((tmp=realloc(peers, peers_size)) == NULL) 278 void *tmp;
279 if ((tmp = realloc(peers, peers_size)) == NULL) {
262 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 280 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
263 peers=tmp; 281 }
264 memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count)); 282 peers = tmp;
265 npeers=peers_size/sizeof(ntp_assoc_status_pair); 283 memcpy((peers + peer_offset), (void *)req.data, ntohs(req.count));
266 peer_offset+=ntohs(req.count); 284 npeers = peers_size / sizeof(ntp_assoc_status_pair);
267 } while(req.op&REM_MORE); 285 peer_offset += ntohs(req.count);
286 } while (req.op & REM_MORE);
268 287
269 /* first, let's find out if we have a sync source, or if there are 288 /* first, let's find out if we have a sync source, or if there are
270 * at least some candidates. In the latter case we'll issue 289 * at least some candidates. In the latter case we'll issue
271 * a warning but go ahead with the check on them. */ 290 * a warning but go ahead with the check on them. */
272 for (i = 0; i < npeers; i++){ 291 int min_peer_sel = PEER_INCLUDED;
273 if(PEER_SEL(peers[i].status) >= PEER_TRUECHIMER){ 292 int num_candidates = 0;
274 (*num_truechimers)++; 293 for (size_t i = 0; i < npeers; i++) {
275 if(PEER_SEL(peers[i].status) >= PEER_INCLUDED){ 294 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) {
295 result.num_truechimers++;
296 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
276 num_candidates++; 297 num_candidates++;
277 if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){ 298 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
278 syncsource_found = true; 299 result.syncsource_found = true;
279 min_peer_sel=PEER_SYNCSOURCE; 300 min_peer_sel = PEER_SYNCSOURCE;
280 } 301 }
281 } 302 }
282 } 303 }
283 } 304 }
284 if(verbose) printf("%d candidate peers available\n", num_candidates); 305
285 if(verbose && syncsource_found) printf("synchronization source found\n"); 306 if (verbose) {
286 if(! syncsource_found){ 307 printf("%d candidate peers available\n", num_candidates);
287 status = STATE_WARNING; 308 if (result.syncsource_found) {
288 if(verbose) printf("warning: no synchronization source found\n"); 309 printf("synchronization source found\n");
289 } 310 }
290 if(li_alarm){
291 status = STATE_WARNING;
292 if(verbose) printf("warning: LI_ALARM bit is set\n");
293 } 311 }
294 312
313 if (!result.syncsource_found) {
314 result.state = STATE_WARNING;
315 if (verbose) {
316 printf("warning: no synchronization source found\n");
317 }
318 }
319 if (result.li_alarm) {
320 result.state = STATE_WARNING;
321 if (verbose) {
322 printf("warning: LI_ALARM bit is set\n");
323 }
324 }
295 325
296 for (i = 0; i < npeers; i++){ 326 const char *getvar = "stratum,offset,jitter";
327 char *data;
328 for (size_t i = 0; i < npeers; i++) {
297 /* Only query this server if it is the current sync source */ 329 /* Only query this server if it is the current sync source */
298 /* If there's no sync.peer, query all candidates and use the best one */ 330 /* If there's no sync.peer, query all candidates and use the best one */
299 if (PEER_SEL(peers[i].status) >= min_peer_sel){ 331 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
300 if(verbose) printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); 332 if (verbose) {
301 xasprintf(&data, ""); 333 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
302 do{ 334 }
335 data = strdup("");
336 do {
303 setup_control_request(&req, OP_READVAR, 2); 337 setup_control_request(&req, OP_READVAR, 2);
304 req.assoc = peers[i].assoc; 338 req.assoc = peers[i].assoc;
305 /* Putting the wanted variable names in the request 339 /* Putting the wanted variable names in the request
@@ -309,7 +343,7 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
309 */ 343 */
310 /* Older servers doesn't know what jitter is, so if we get an 344 /* Older servers doesn't know what jitter is, so if we get an
311 * error on the first pass we redo it with "dispersion" */ 345 * error on the first pass we redo it with "dispersion" */
312 strncpy(req.data, getvar, MAX_CM_SIZE-1); 346 strncpy(req.data, getvar, MAX_CM_SIZE - 1);
313 req.count = htons(strlen(getvar)); 347 req.count = htons(strlen(getvar));
314 DBG(printf("sending READVAR request...\n")); 348 DBG(printf("sending READVAR request...\n"));
315 write(conn, &req, SIZEOF_NTPCM(req)); 349 write(conn, &req, SIZEOF_NTPCM(req));
@@ -320,131 +354,184 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
320 DBG(printf("receiving READVAR response...\n")); 354 DBG(printf("receiving READVAR response...\n"));
321 read(conn, &req, SIZEOF_NTPCM(req)); 355 read(conn, &req, SIZEOF_NTPCM(req));
322 DBG(print_ntp_control_message(&req)); 356 DBG(print_ntp_control_message(&req));
323 } while (!(req.op&OP_READVAR && ntohs(req.seq) == 2)); 357 } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2));
324 358
325 if(!(req.op&REM_ERROR)) 359 if (!(req.op & REM_ERROR)) {
326 xasprintf(&data, "%s%s", data, req.data); 360 xasprintf(&data, "%s%s", data, req.data);
327 } while(req.op&REM_MORE); 361 }
328 362 } while (req.op & REM_MORE);
329 if(req.op&REM_ERROR) { 363
330 if(strstr(getvar, "jitter")) { 364 if (req.op & REM_ERROR) {
331 if(verbose) printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with 'dispersion'...\n"); 365 if (strstr(getvar, "jitter")) {
366 if (verbose) {
367 printf("The command failed. This is usually caused by servers refusing the "
368 "'jitter'\nvariable. Restarting with "
369 "'dispersion'...\n");
370 }
332 getvar = "stratum,offset,dispersion"; 371 getvar = "stratum,offset,dispersion";
333 i--; 372 i--;
334 continue; 373 continue;
335 } else if(strlen(getvar)) { 374 }
336 if(verbose) printf("Server didn't like dispersion either; will retrieve everything\n"); 375 if (strlen(getvar)) {
376 if (verbose) {
377 printf("Server didn't like dispersion either; will retrieve everything\n");
378 }
337 getvar = ""; 379 getvar = "";
338 i--; 380 i--;
339 continue; 381 continue;
340 } 382 }
341 } 383 }
342 384
343 if(verbose > 1) 385 if (verbose > 1) {
344 printf("Server responded: >>>%s<<<\n", data); 386 printf("Server responded: >>>%s<<<\n", data);
387 }
345 388
389 double tmp_offset = 0;
390 char *value;
391 char *nptr;
346 /* get the offset */ 392 /* get the offset */
347 if(verbose) 393 if (verbose) {
348 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc)); 394 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc));
395 }
349 396
350 value = np_extract_ntpvar(data, "offset"); 397 value = np_extract_ntpvar(data, "offset");
351 nptr=NULL; 398 nptr = NULL;
352 /* Convert the value if we have one */ 399 /* Convert the value if we have one */
353 if(value != NULL) 400 if (value != NULL) {
354 tmp_offset = strtod(value, &nptr) / 1000; 401 tmp_offset = strtod(value, &nptr) / 1000;
402 }
355 /* If value is null or no conversion was performed */ 403 /* If value is null or no conversion was performed */
356 if(value == NULL || value==nptr) { 404 if (value == NULL || value == nptr) {
357 if(verbose) printf("error: unable to read server offset response.\n"); 405 if (verbose) {
406 printf("error: unable to read server offset response.\n");
407 }
358 } else { 408 } else {
359 if(verbose) printf("%.10g\n", tmp_offset); 409 if (verbose) {
360 if(*offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(*offset)) { 410 printf("%.10g\n", tmp_offset);
361 *offset = tmp_offset; 411 }
362 *offset_result = STATE_OK; 412 if (result.offset_result == STATE_UNKNOWN ||
413 fabs(tmp_offset) < fabs(result.offset)) {
414 result.offset = tmp_offset;
415 result.offset_result = STATE_OK;
363 } else { 416 } else {
364 /* Skip this one; move to the next */ 417 /* Skip this one; move to the next */
365 continue; 418 continue;
366 } 419 }
367 } 420 }
368 421
369 if(do_jitter) { 422 if (config.do_jitter) {
370 /* get the jitter */ 423 /* get the jitter */
371 if(verbose) { 424 if (verbose) {
372 printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", ntohs(peers[i].assoc)); 425 printf("parsing %s from peer %.2x: ",
426 strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter",
427 ntohs(peers[i].assoc));
373 } 428 }
374 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter"); 429 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion"
375 nptr=NULL; 430 : "jitter");
431 nptr = NULL;
376 /* Convert the value if we have one */ 432 /* Convert the value if we have one */
377 if(value != NULL) 433 if (value != NULL) {
378 *jitter = strtod(value, &nptr); 434 result.jitter = strtod(value, &nptr);
435 }
379 /* If value is null or no conversion was performed */ 436 /* If value is null or no conversion was performed */
380 if(value == NULL || value==nptr) { 437 if (value == NULL || value == nptr) {
381 if(verbose) printf("error: unable to read server jitter/dispersion response.\n"); 438 if (verbose) {
382 *jitter = -1; 439 printf("error: unable to read server jitter/dispersion response.\n");
383 } else if(verbose) { 440 }
384 printf("%.10g\n", *jitter); 441 result.jitter = -1;
442 } else if (verbose) {
443 printf("%.10g\n", result.jitter);
385 } 444 }
386 } 445 }
387 446
388 if(do_stratum) { 447 if (config.do_stratum) {
389 /* get the stratum */ 448 /* get the stratum */
390 if(verbose) { 449 if (verbose) {
391 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc)); 450 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc));
392 } 451 }
393 value = np_extract_ntpvar(data, "stratum"); 452 value = np_extract_ntpvar(data, "stratum");
394 nptr=NULL; 453 nptr = NULL;
395 /* Convert the value if we have one */ 454 /* Convert the value if we have one */
396 if(value != NULL) 455 if (value != NULL) {
397 *stratum = strtol(value, &nptr, 10); 456 result.stratum = strtol(value, &nptr, 10);
398 if(value == NULL || value==nptr) { 457 }
399 if(verbose) printf("error: unable to read server stratum response.\n"); 458 if (value == NULL || value == nptr) {
400 *stratum = -1; 459 if (verbose) {
460 printf("error: unable to read server stratum response.\n");
461 }
462 result.stratum = -1;
401 } else { 463 } else {
402 if(verbose) printf("%i\n", *stratum); 464 if (verbose) {
465 printf("%li\n", result.stratum);
466 }
403 } 467 }
404 } 468 }
405 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */ 469 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */
406 } /* for (i = 0; i < npeers; i++) */ 470 } /* for (i = 0; i < npeers; i++) */
407 471
408 close(conn); 472 close(conn);
409 if(peers!=NULL) free(peers); 473 if (peers != NULL) {
474 free(peers);
475 }
410 476
411 return status; 477 return result;
412} 478}
413 479
414int process_arguments(int argc, char **argv){ 480check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
415 int c; 481
416 int option=0; 482 enum {
417 static struct option longopts[] = { 483 output_format_index = CHAR_MAX + 1,
418 {"version", no_argument, 0, 'V'},
419 {"help", no_argument, 0, 'h'},
420 {"verbose", no_argument, 0, 'v'},
421 {"use-ipv4", no_argument, 0, '4'},
422 {"use-ipv6", no_argument, 0, '6'},
423 {"quiet", no_argument, 0, 'q'},
424 {"warning", required_argument, 0, 'w'},
425 {"critical", required_argument, 0, 'c'},
426 {"swarn", required_argument, 0, 'W'},
427 {"scrit", required_argument, 0, 'C'},
428 {"jwarn", required_argument, 0, 'j'},
429 {"jcrit", required_argument, 0, 'k'},
430 {"twarn", required_argument, 0, 'm'},
431 {"tcrit", required_argument, 0, 'n'},
432 {"timeout", required_argument, 0, 't'},
433 {"hostname", required_argument, 0, 'H'},
434 {"port", required_argument, 0, 'p'},
435 {0, 0, 0, 0}
436 }; 484 };
437 485
486 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
487 {"help", no_argument, 0, 'h'},
488 {"verbose", no_argument, 0, 'v'},
489 {"use-ipv4", no_argument, 0, '4'},
490 {"use-ipv6", no_argument, 0, '6'},
491 {"quiet", no_argument, 0, 'q'},
492 {"warning", required_argument, 0, 'w'},
493 {"critical", required_argument, 0, 'c'},
494 {"swarn", required_argument, 0, 'W'},
495 {"scrit", required_argument, 0, 'C'},
496 {"jwarn", required_argument, 0, 'j'},
497 {"jcrit", required_argument, 0, 'k'},
498 {"twarn", required_argument, 0, 'm'},
499 {"tcrit", required_argument, 0, 'n'},
500 {"timeout", required_argument, 0, 't'},
501 {"hostname", required_argument, 0, 'H'},
502 {"port", required_argument, 0, 'p'},
503 {"output-format", required_argument, 0, output_format_index},
504 {0, 0, 0, 0}};
505
506 if (argc < 2) {
507 usage("\n");
508 }
438 509
439 if (argc < 2) 510 check_ntp_peer_config_wrapper result = {
440 usage ("\n"); 511 .errorcode = OK,
512 .config = check_ntp_peer_config_init(),
513 };
441 514
442 while (true) { 515 while (true) {
443 c = getopt_long (argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); 516 int option = 0;
444 if (c == -1 || c == EOF || c == 1) 517 int option_char =
518 getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option);
519 if (option_char == -1 || option_char == EOF || option_char == 1) {
445 break; 520 break;
521 }
522
523 switch (option_char) {
524 case output_format_index: {
525 parsed_output_format parser = mp_parse_output_format(optarg);
526 if (!parser.parsing_success) {
527 printf("Invalid output format: %s\n", optarg);
528 exit(STATE_UNKNOWN);
529 }
446 530
447 switch (c) { 531 result.config.output_format_is_set = true;
532 result.config.output_format = parser.output_format;
533 break;
534 }
448 case 'h': 535 case 'h':
449 print_help(); 536 print_help();
450 exit(STATE_UNKNOWN); 537 exit(STATE_UNKNOWN);
@@ -457,272 +544,313 @@ int process_arguments(int argc, char **argv){
457 verbose++; 544 verbose++;
458 break; 545 break;
459 case 'q': 546 case 'q':
460 quiet = true; 547 result.config.quiet = true;
461 break;
462 case 'w':
463 owarn = optarg;
464 break;
465 case 'c':
466 ocrit = optarg;
467 break;
468 case 'W':
469 do_stratum = true;
470 swarn = optarg;
471 break;
472 case 'C':
473 do_stratum = true;
474 scrit = optarg;
475 break;
476 case 'j':
477 do_jitter = true;
478 jwarn = optarg;
479 break;
480 case 'k':
481 do_jitter = true;
482 jcrit = optarg;
483 break;
484 case 'm':
485 do_truechimers = true;
486 twarn = optarg;
487 break;
488 case 'n':
489 do_truechimers = true;
490 tcrit = optarg;
491 break; 548 break;
549 case 'w': {
550 mp_range_parsed tmp = mp_parse_range_string(optarg);
551 if (tmp.error != MP_PARSING_SUCCESS) {
552 die(STATE_UNKNOWN, "failed to parse warning offset threshold");
553 }
554
555 result.config.offset_thresholds =
556 mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
557 } break;
558 case 'c': {
559 mp_range_parsed tmp = mp_parse_range_string(optarg);
560 if (tmp.error != MP_PARSING_SUCCESS) {
561 die(STATE_UNKNOWN, "failed to parse critical offset threshold");
562 }
563
564 result.config.offset_thresholds =
565 mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
566 } break;
567 case 'W': {
568 result.config.do_stratum = true;
569 mp_range_parsed tmp = mp_parse_range_string(optarg);
570 if (tmp.error != MP_PARSING_SUCCESS) {
571 die(STATE_UNKNOWN, "failed to parse warning stratum threshold");
572 }
573
574 result.config.stratum_thresholds =
575 mp_thresholds_set_warn(result.config.stratum_thresholds, tmp.range);
576 } break;
577 case 'C': {
578 result.config.do_stratum = true;
579 mp_range_parsed tmp = mp_parse_range_string(optarg);
580 if (tmp.error != MP_PARSING_SUCCESS) {
581 die(STATE_UNKNOWN, "failed to parse critical stratum threshold");
582 }
583
584 result.config.stratum_thresholds =
585 mp_thresholds_set_crit(result.config.stratum_thresholds, tmp.range);
586 } break;
587 case 'j': {
588 result.config.do_jitter = true;
589 mp_range_parsed tmp = mp_parse_range_string(optarg);
590 if (tmp.error != MP_PARSING_SUCCESS) {
591 die(STATE_UNKNOWN, "failed to parse warning jitter threshold");
592 }
593
594 result.config.jitter_thresholds =
595 mp_thresholds_set_warn(result.config.jitter_thresholds, tmp.range);
596 } break;
597 case 'k': {
598 result.config.do_jitter = true;
599 mp_range_parsed tmp = mp_parse_range_string(optarg);
600 if (tmp.error != MP_PARSING_SUCCESS) {
601 die(STATE_UNKNOWN, "failed to parse critical jitter threshold");
602 }
603
604 result.config.jitter_thresholds =
605 mp_thresholds_set_crit(result.config.jitter_thresholds, tmp.range);
606 } break;
607 case 'm': {
608 result.config.do_truechimers = true;
609 mp_range_parsed tmp = mp_parse_range_string(optarg);
610 if (tmp.error != MP_PARSING_SUCCESS) {
611 die(STATE_UNKNOWN, "failed to parse warning truechimer threshold");
612 }
613
614 result.config.truechimer_thresholds =
615 mp_thresholds_set_warn(result.config.truechimer_thresholds, tmp.range);
616 } break;
617 case 'n': {
618 result.config.do_truechimers = true;
619 mp_range_parsed tmp = mp_parse_range_string(optarg);
620 if (tmp.error != MP_PARSING_SUCCESS) {
621 die(STATE_UNKNOWN, "failed to parse critical truechimer threshold");
622 }
623
624 result.config.truechimer_thresholds =
625 mp_thresholds_set_crit(result.config.truechimer_thresholds, tmp.range);
626 } break;
492 case 'H': 627 case 'H':
493 if(!is_host(optarg)) 628 if (!is_host(optarg) && (optarg[0] != '/')) {
494 usage2(_("Invalid hostname/address"), optarg); 629 usage2(_("Invalid hostname/address"), optarg);
495 server_address = strdup(optarg); 630 }
631 result.config.server_address = strdup(optarg);
496 break; 632 break;
497 case 'p': 633 case 'p':
498 port=atoi(optarg); 634 result.config.port = atoi(optarg);
499 break; 635 break;
500 case 't': 636 case 't':
501 socket_timeout=atoi(optarg); 637 socket_timeout = atoi(optarg);
502 break; 638 break;
503 case '4': 639 case '4':
504 address_family = AF_INET; 640 address_family = AF_INET;
505 break; 641 break;
506 case '6': 642 case '6':
507#ifdef USE_IPV6
508 address_family = AF_INET6; 643 address_family = AF_INET6;
509#else
510 usage4 (_("IPv6 support not available"));
511#endif
512 break; 644 break;
513 case '?': 645 case '?':
514 /* print short usage statement if args not parsable */ 646 /* print short usage statement if args not parsable */
515 usage5 (); 647 usage5();
516 break; 648 break;
517 } 649 }
518 } 650 }
519 651
520 if(server_address == NULL){ 652 if (result.config.server_address == NULL) {
521 usage4(_("Hostname was not supplied")); 653 usage4(_("Hostname was not supplied"));
522 } 654 }
523 655
524 return 0; 656 return result;
525} 657}
526 658
527char *perfd_offset (double offset) 659char *perfd_offset(double offset, thresholds *offset_thresholds) {
528{ 660 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
529 return fperfdata ("offset", offset, "s", 661 offset_thresholds->critical->end, false, 0, false, 0);
530 true, offset_thresholds->warning->end,
531 true, offset_thresholds->critical->end,
532 false, 0, false, 0);
533} 662}
534 663
535char *perfd_jitter (double jitter) 664char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) {
536{ 665 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter,
537 return fperfdata ("jitter", jitter, "", 666 jitter_thresholds->critical->end, true, 0, false, 0);
538 do_jitter, jitter_thresholds->warning->end,
539 do_jitter, jitter_thresholds->critical->end,
540 true, 0, false, 0);
541} 667}
542 668
543char *perfd_stratum (int stratum) 669char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) {
544{ 670 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end,
545 return perfdata ("stratum", stratum, "", 671 do_stratum, (int)stratum_thresholds->critical->end, true, 0, true, 16);
546 do_stratum, (int)stratum_thresholds->warning->end,
547 do_stratum, (int)stratum_thresholds->critical->end,
548 true, 0, true, 16);
549} 672}
550 673
551char *perfd_truechimers (int num_truechimers) 674char *perfd_truechimers(int num_truechimers, const bool do_truechimers,
552{ 675 thresholds *truechimer_thresholds) {
553 return perfdata ("truechimers", num_truechimers, "", 676 return perfdata("truechimers", num_truechimers, "", do_truechimers,
554 do_truechimers, (int)truechimer_thresholds->warning->end, 677 (int)truechimer_thresholds->warning->end, do_truechimers,
555 do_truechimers, (int)truechimer_thresholds->critical->end, 678 (int)truechimer_thresholds->critical->end, true, 0, false, 0);
556 true, 0, false, 0);
557} 679}
558 680
559int main(int argc, char *argv[]){ 681int main(int argc, char *argv[]) {
560 int result, offset_result, stratum, num_truechimers; 682 setlocale(LC_ALL, "");
561 double offset=0, jitter=0; 683 bindtextdomain(PACKAGE, LOCALEDIR);
562 char *result_line, *perfdata_line; 684 textdomain(PACKAGE);
563
564 setlocale (LC_ALL, "");
565 bindtextdomain (PACKAGE, LOCALEDIR);
566 textdomain (PACKAGE);
567 685
568 /* Parse extra opts if any */ 686 /* Parse extra opts if any */
569 argv=np_extra_opts (&argc, argv, progname); 687 argv = np_extra_opts(&argc, argv, progname);
688
689 check_ntp_peer_config_wrapper tmp_config = process_arguments(argc, argv);
570 690
571 if (process_arguments (argc, argv) == ERROR) 691 if (tmp_config.errorcode == ERROR) {
572 usage4 (_("Could not parse arguments")); 692 usage4(_("Could not parse arguments"));
693 }
694
695 const check_ntp_peer_config config = tmp_config.config;
573 696
574 set_thresholds(&offset_thresholds, owarn, ocrit); 697 if (config.output_format_is_set) {
575 set_thresholds(&jitter_thresholds, jwarn, jcrit); 698 mp_set_format(config.output_format);
576 set_thresholds(&stratum_thresholds, swarn, scrit); 699 }
577 set_thresholds(&truechimer_thresholds, twarn, tcrit);
578 700
579 /* initialize alarm signal handling */ 701 /* initialize alarm signal handling */
580 signal (SIGALRM, socket_timeout_alarm_handler); 702 signal(SIGALRM, socket_timeout_alarm_handler);
581 703
582 /* set socket timeout */ 704 /* set socket timeout */
583 alarm (socket_timeout); 705 alarm(socket_timeout);
584 706
585 /* This returns either OK or WARNING (See comment preceding ntp_request) */ 707 /* This returns either OK or WARNING (See comment preceding ntp_request) */
586 result = ntp_request(&offset, &offset_result, &jitter, &stratum, &num_truechimers); 708 const ntp_request_result ntp_res = ntp_request(config);
709 mp_check overall = mp_check_init();
587 710
588 if(offset_result == STATE_UNKNOWN) { 711 mp_subcheck sc_offset = mp_subcheck_init();
712 xasprintf(&sc_offset.output, "offset");
713 if (ntp_res.offset_result == STATE_UNKNOWN) {
589 /* if there's no sync peer (this overrides ntp_request output): */ 714 /* if there's no sync peer (this overrides ntp_request output): */
590 result = (quiet ? STATE_UNKNOWN : STATE_CRITICAL); 715 sc_offset =
716 mp_set_subcheck_state(sc_offset, (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL));
717 xasprintf(&sc_offset.output, "%s unknown", sc_offset.output);
591 } else { 718 } else {
592 /* Be quiet if there's no candidates either */ 719 /* Be quiet if there's no candidates either */
593 if (quiet && result == STATE_WARNING) 720 mp_state_enum tmp = STATE_OK;
594 result = STATE_UNKNOWN; 721 if (config.quiet && ntp_res.state == STATE_WARNING) {
595 result = max_state_alt(result, get_status(fabs(offset), offset_thresholds)); 722 tmp = STATE_UNKNOWN;
596 } 723 }
597 724
598 int oresult = result; 725 xasprintf(&sc_offset.output, "%s: %.6fs", sc_offset.output, ntp_res.offset);
599 726
727 mp_perfdata pd_offset = perfdata_init();
728 pd_offset.value = mp_create_pd_value(fabs(ntp_res.offset));
729 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
730 pd_offset.label = "offset";
731 pd_offset.uom = "s";
732 mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
600 733
601 int tresult = STATE_UNKNOWN; 734 tmp = max_state_alt(tmp, mp_get_pd_status(pd_offset));
602 735 sc_offset = mp_set_subcheck_state(sc_offset, tmp);
603 if(do_truechimers) {
604 tresult = get_status(num_truechimers, truechimer_thresholds);
605 result = max_state_alt(result, tresult);
606 } 736 }
607 737
738 mp_add_subcheck_to_check(&overall, sc_offset);
739
740 // truechimers
741 if (config.do_truechimers) {
742 mp_subcheck sc_truechimers = mp_subcheck_init();
743 xasprintf(&sc_truechimers.output, "truechimers: %i", ntp_res.num_truechimers);
608 744
609 int sresult = STATE_UNKNOWN; 745 mp_perfdata pd_truechimers = perfdata_init();
746 pd_truechimers.value = mp_create_pd_value(ntp_res.num_truechimers);
747 pd_truechimers.label = "truechimers";
748 pd_truechimers = mp_pd_set_thresholds(pd_truechimers, config.truechimer_thresholds);
610 749
611 if(do_stratum) { 750 mp_add_perfdata_to_subcheck(&sc_truechimers, pd_truechimers);
612 sresult = get_status(stratum, stratum_thresholds); 751
613 result = max_state_alt(result, sresult); 752 sc_truechimers = mp_set_subcheck_state(sc_truechimers, mp_get_pd_status(pd_truechimers));
753
754 mp_add_subcheck_to_check(&overall, sc_truechimers);
614 } 755 }
615 756
757 if (config.do_stratum) {
758 mp_subcheck sc_stratum = mp_subcheck_init();
759 xasprintf(&sc_stratum.output, "stratum: %li", ntp_res.stratum);
760
761 mp_perfdata pd_stratum = perfdata_init();
762 pd_stratum.value = mp_create_pd_value(ntp_res.stratum);
763 pd_stratum = mp_pd_set_thresholds(pd_stratum, config.stratum_thresholds);
764 pd_stratum.label = "stratum";
616 765
617 int jresult = STATE_UNKNOWN; 766 mp_add_perfdata_to_subcheck(&sc_stratum, pd_stratum);
618 767
619 if(do_jitter) { 768 sc_stratum = mp_set_subcheck_state(sc_stratum, mp_get_pd_status(pd_stratum));
620 jresult = get_status(jitter, jitter_thresholds); 769
621 result = max_state_alt(result, jresult); 770 mp_add_subcheck_to_check(&overall, sc_stratum);
622 } 771 }
623 772
624 switch (result) { 773 if (config.do_jitter) {
625 case STATE_CRITICAL : 774 mp_subcheck sc_jitter = mp_subcheck_init();
626 xasprintf(&result_line, _("NTP CRITICAL:")); 775 xasprintf(&sc_jitter.output, "jitter: %f", ntp_res.jitter);
627 break; 776
628 case STATE_WARNING : 777 mp_perfdata pd_jitter = perfdata_init();
629 xasprintf(&result_line, _("NTP WARNING:")); 778 pd_jitter.value = mp_create_pd_value(ntp_res.jitter);
630 break; 779 pd_jitter = mp_pd_set_thresholds(pd_jitter, config.jitter_thresholds);
631 case STATE_OK : 780 pd_jitter.label = "jitter";
632 xasprintf(&result_line, _("NTP OK:")); 781
633 break; 782 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
634 default : 783
635 xasprintf(&result_line, _("NTP UNKNOWN:")); 784 sc_jitter = mp_set_subcheck_state(sc_jitter, mp_get_pd_status(pd_jitter));
636 break; 785 mp_add_subcheck_to_check(&overall, sc_jitter);
637 } 786 }
638 if(!syncsource_found) 787
639 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); 788 mp_subcheck sc_other_info = mp_subcheck_init();
640 else if(li_alarm) 789 sc_other_info = mp_set_subcheck_default_state(sc_other_info, STATE_OK);
641 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); 790 if (!ntp_res.syncsource_found) {
642 791 xasprintf(&sc_other_info.output, "%s", _("Server not synchronized"));
643 if(offset_result == STATE_UNKNOWN){ 792 mp_add_subcheck_to_check(&overall, sc_other_info);
644 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 793 } else if (ntp_res.li_alarm) {
645 xasprintf(&perfdata_line, ""); 794 xasprintf(&sc_other_info.output, "%s", _("Server has the LI_ALARM bit set"));
646 } else if (oresult == STATE_WARNING) { 795 mp_add_subcheck_to_check(&overall, sc_other_info);
647 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), offset);
648 } else if (oresult == STATE_CRITICAL) {
649 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), offset);
650 } else {
651 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
652 }
653 xasprintf(&perfdata_line, "%s", perfd_offset(offset));
654
655 if (do_jitter) {
656 if (jresult == STATE_WARNING) {
657 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, jitter);
658 } else if (jresult == STATE_CRITICAL) {
659 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, jitter);
660 } else {
661 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter);
662 }
663 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter));
664 } 796 }
665 if (do_stratum) { 797
666 if (sresult == STATE_WARNING) { 798 {
667 xasprintf(&result_line, "%s, stratum=%i (WARNING)", result_line, stratum); 799 mp_subcheck sc_offset = mp_subcheck_init();
668 } else if (sresult == STATE_CRITICAL) { 800 sc_offset = mp_set_subcheck_default_state(sc_offset, STATE_OK);
669 xasprintf(&result_line, "%s, stratum=%i (CRITICAL)", result_line, stratum); 801 xasprintf(&sc_offset.output, "offset: %.10gs", ntp_res.offset);
670 } else { 802
671 xasprintf(&result_line, "%s, stratum=%i", result_line, stratum); 803 mp_perfdata pd_offset = perfdata_init();
672 } 804 pd_offset.value = mp_create_pd_value(ntp_res.offset);
673 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(stratum)); 805 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
806
807 sc_offset = mp_set_subcheck_state(sc_offset, ntp_res.offset_result);
674 } 808 }
675 if (do_truechimers) { 809
676 if (tresult == STATE_WARNING) { 810 if (config.server_address != NULL) {
677 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, num_truechimers); 811 free(config.server_address);
678 } else if (tresult == STATE_CRITICAL) {
679 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, num_truechimers);
680 } else {
681 xasprintf(&result_line, "%s, truechimers=%i", result_line, num_truechimers);
682 }
683 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_truechimers(num_truechimers));
684 } 812 }
685 printf("%s|%s\n", result_line, perfdata_line);
686 813
687 if(server_address!=NULL) free(server_address); 814 mp_exit(overall);
688 return result;
689} 815}
690 816
691void print_help(void){ 817void print_help(void) {
692 print_revision(progname, NP_VERSION); 818 print_revision(progname, NP_VERSION);
693 819
694 printf ("Copyright (c) 2006 Sean Finney\n"); 820 printf("Copyright (c) 2006 Sean Finney\n");
695 printf (COPYRIGHT, copyright, email); 821 printf(COPYRIGHT, copyright, email);
696 822
697 printf ("%s\n", _("This plugin checks the selected ntp server")); 823 printf("%s\n", _("This plugin checks the selected ntp server"));
698 824
699 printf ("\n\n"); 825 printf("\n\n");
700 826
701 print_usage(); 827 print_usage();
702 printf (UT_HELP_VRSN); 828 printf(UT_HELP_VRSN);
703 printf (UT_EXTRA_OPTS); 829 printf(UT_EXTRA_OPTS);
704 printf (UT_IPv46); 830 printf(UT_IPv46);
705 printf (UT_HOST_PORT, 'p', "123"); 831 printf(UT_HOST_PORT, 'p', "123");
706 printf (" %s\n", "-q, --quiet"); 832 printf(" %s\n", "-q, --quiet");
707 printf (" %s\n", _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); 833 printf(" %s\n",
708 printf (" %s\n", "-w, --warning=THRESHOLD"); 834 _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized"));
709 printf (" %s\n", _("Offset to result in warning status (seconds)")); 835 printf(" %s\n", "-w, --warning=THRESHOLD");
710 printf (" %s\n", "-c, --critical=THRESHOLD"); 836 printf(" %s\n", _("Offset to result in warning status (seconds)"));
711 printf (" %s\n", _("Offset to result in critical status (seconds)")); 837 printf(" %s\n", "-c, --critical=THRESHOLD");
712 printf (" %s\n", "-W, --swarn=THRESHOLD"); 838 printf(" %s\n", _("Offset to result in critical status (seconds)"));
713 printf (" %s\n", _("Warning threshold for stratum of server's synchronization peer")); 839 printf(" %s\n", "-W, --swarn=THRESHOLD");
714 printf (" %s\n", "-C, --scrit=THRESHOLD"); 840 printf(" %s\n", _("Warning threshold for stratum of server's synchronization peer"));
715 printf (" %s\n", _("Critical threshold for stratum of server's synchronization peer")); 841 printf(" %s\n", "-C, --scrit=THRESHOLD");
716 printf (" %s\n", "-j, --jwarn=THRESHOLD"); 842 printf(" %s\n", _("Critical threshold for stratum of server's synchronization peer"));
717 printf (" %s\n", _("Warning threshold for jitter")); 843 printf(" %s\n", "-j, --jwarn=THRESHOLD");
718 printf (" %s\n", "-k, --jcrit=THRESHOLD"); 844 printf(" %s\n", _("Warning threshold for jitter"));
719 printf (" %s\n", _("Critical threshold for jitter")); 845 printf(" %s\n", "-k, --jcrit=THRESHOLD");
720 printf (" %s\n", "-m, --twarn=THRESHOLD"); 846 printf(" %s\n", _("Critical threshold for jitter"));
721 printf (" %s\n", _("Warning threshold for number of usable time sources (\"truechimers\")")); 847 printf(" %s\n", "-m, --twarn=THRESHOLD");
722 printf (" %s\n", "-n, --tcrit=THRESHOLD"); 848 printf(" %s\n", _("Warning threshold for number of usable time sources (\"truechimers\")"));
723 printf (" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")")); 849 printf(" %s\n", "-n, --tcrit=THRESHOLD");
724 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 850 printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")"));
725 printf (UT_VERBOSE); 851 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
852 printf(UT_VERBOSE);
853 printf(UT_OUTPUT_FORMAT);
726 854
727 printf("\n"); 855 printf("\n");
728 printf("%s\n", _("This plugin checks an NTP server independent of any commandline")); 856 printf("%s\n", _("This plugin checks an NTP server independent of any commandline"));
@@ -741,7 +869,8 @@ void print_help(void){
741 printf(" %s\n", _("Simple NTP server check:")); 869 printf(" %s\n", _("Simple NTP server check:"));
742 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1")); 870 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1"));
743 printf("\n"); 871 printf("\n");
744 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); 872 printf(" %s\n",
873 _("Check jitter too, avoiding critical notifications if jitter isn't available"));
745 printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); 874 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
746 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); 875 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
747 printf("\n"); 876 printf("\n");
@@ -751,13 +880,11 @@ void print_help(void){
751 printf(" %s\n", _("Check only stratum:")); 880 printf(" %s\n", _("Check only stratum:"));
752 printf(" %s\n", ("./check_ntp_peer -H ntpserv -W 4 -C 6")); 881 printf(" %s\n", ("./check_ntp_peer -H ntpserv -W 4 -C 6"));
753 882
754 printf (UT_SUPPORT); 883 printf(UT_SUPPORT);
755} 884}
756 885
757void 886void print_usage(void) {
758print_usage(void) 887 printf("%s\n", _("Usage:"));
759{
760 printf ("%s\n", _("Usage:"));
761 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-W <warn>] [-C <crit>]\n", progname); 888 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-W <warn>] [-C <crit>]\n", progname);
762 printf(" [-j <warn>] [-k <crit>] [-v verbose]\n"); 889 printf(" [-j <warn>] [-k <crit>] [-v verbose]\n");
763} 890}
diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h
new file mode 100644
index 00000000..488d936c
--- /dev/null
+++ b/plugins/check_ntp_peer.d/config.h
@@ -0,0 +1,82 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "perfdata.h"
6#include "thresholds.h"
7#include <stddef.h>
8
9enum {
10 DEFAULT_NTP_PORT = 123,
11};
12
13typedef struct {
14 char *server_address;
15 int port;
16
17 bool quiet;
18
19 // truechimer stuff
20 bool do_truechimers;
21 mp_thresholds truechimer_thresholds;
22
23 // offset thresholds
24 mp_thresholds offset_thresholds;
25
26 // stratum stuff
27 bool do_stratum;
28 mp_thresholds stratum_thresholds;
29
30 // jitter stuff
31 bool do_jitter;
32 mp_thresholds jitter_thresholds;
33
34 bool output_format_is_set;
35 mp_output_format output_format;
36} check_ntp_peer_config;
37
38check_ntp_peer_config check_ntp_peer_config_init() {
39 check_ntp_peer_config tmp = {
40 .server_address = NULL,
41 .port = DEFAULT_NTP_PORT,
42
43 .quiet = false,
44 .do_truechimers = false,
45 .truechimer_thresholds = mp_thresholds_init(),
46
47 .offset_thresholds = mp_thresholds_init(),
48
49 .do_stratum = false,
50 .stratum_thresholds = mp_thresholds_init(),
51
52 .do_jitter = false,
53 .jitter_thresholds = mp_thresholds_init(),
54
55 .output_format_is_set = false,
56 };
57
58 mp_range stratum_default = mp_range_init();
59 stratum_default = mp_range_set_start(stratum_default, mp_create_pd_value(-1));
60 stratum_default = mp_range_set_end(stratum_default, mp_create_pd_value(16));
61 tmp.stratum_thresholds = mp_thresholds_set_warn(tmp.stratum_thresholds, stratum_default);
62 tmp.stratum_thresholds = mp_thresholds_set_crit(tmp.stratum_thresholds, stratum_default);
63
64 mp_range jitter_w_default = mp_range_init();
65 jitter_w_default = mp_range_set_start(jitter_w_default, mp_create_pd_value(-1));
66 jitter_w_default = mp_range_set_end(jitter_w_default, mp_create_pd_value(5000));
67 tmp.jitter_thresholds = mp_thresholds_set_warn(tmp.jitter_thresholds, jitter_w_default);
68
69 mp_range jitter_c_default = mp_range_init();
70 jitter_c_default = mp_range_set_start(jitter_c_default, mp_create_pd_value(-1));
71 jitter_c_default = mp_range_set_end(jitter_c_default, mp_create_pd_value(10000));
72 tmp.jitter_thresholds = mp_thresholds_set_crit(tmp.jitter_thresholds, jitter_c_default);
73
74 mp_range offset_w_default = mp_range_init();
75 offset_w_default = mp_range_set_end(offset_w_default, mp_create_pd_value(60));
76 tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, offset_w_default);
77
78 mp_range offset_c_default = mp_range_init();
79 offset_c_default = mp_range_set_end(offset_c_default, mp_create_pd_value(120));
80 tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, offset_c_default);
81 return tmp;
82}
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index b2e16556..4e3a55db 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -1,81 +1,83 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ntp_time plugin 3 * Monitoring check_ntp_time plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006 Sean Finney <seanius@seanius.net> 6 * Copyright (c) 2006 Sean Finney <seanius@seanius.net>
7* Copyright (c) 2006-2008 Monitoring Plugins Development Team 7 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_ntp_time plugin 11 * This file contains the check_ntp_time plugin
12* 12 *
13* This plugin checks the clock offset between the local host and a 13 * This plugin checks the clock offset between the local host and a
14* remote NTP server. It is independent of any commandline programs or 14 * remote NTP server. It is independent of any commandline programs or
15* external libraries. 15 * external libraries.
16* 16 *
17* If you'd rather want to monitor an NTP server, please use 17 * If you'd rather want to monitor an NTP server, please use
18* check_ntp_peer. 18 * check_ntp_peer.
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_ntp_time"; 37#include "output.h"
38const char *copyright = "2006-2008";
39const char *email = "devel@monitoring-plugins.org";
40
41#include "common.h" 38#include "common.h"
42#include "netutils.h" 39#include "netutils.h"
40#include "perfdata.h"
43#include "utils.h" 41#include "utils.h"
42#include "states.h"
43#include "thresholds.h"
44#include "check_ntp_time.d/config.h"
45#include <netinet/in.h>
46#include <sys/socket.h>
47
48static int verbose = 0;
49
50const char *progname = "check_ntp_time";
51const char *copyright = "2006-2024";
52const char *email = "devel@monitoring-plugins.org";
44 53
45static char *server_address=NULL; 54typedef struct {
46static char *port="123"; 55 int errorcode;
47static int verbose=0; 56 check_ntp_time_config config;
48static bool quiet = false; 57} check_ntp_time_config_wrapper;
49static char *owarn="60"; 58static check_ntp_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50static char *ocrit="120";
51static int time_offset=0;
52 59
53int process_arguments (int, char **); 60static void print_help(void);
54thresholds *offset_thresholds = NULL; 61void print_usage(void);
55void print_help (void);
56void print_usage (void);
57 62
58/* number of times to perform each request to get a good average. */ 63/* number of times to perform each request to get a good average. */
59#ifndef AVG_NUM 64#ifndef AVG_NUM
60#define AVG_NUM 4 65# define AVG_NUM 4
61#endif 66#endif
62 67
63/* max size of control message data */
64#define MAX_CM_SIZE 468
65
66/* this structure holds everything in an ntp request/response as per rfc1305 */ 68/* this structure holds everything in an ntp request/response as per rfc1305 */
67typedef struct { 69typedef struct {
68 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 70 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
69 uint8_t stratum; /* clock stratum */ 71 uint8_t stratum; /* clock stratum */
70 int8_t poll; /* polling interval */ 72 int8_t poll; /* polling interval */
71 int8_t precision; /* precision of the local clock */ 73 int8_t precision; /* precision of the local clock */
72 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */ 74 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */
73 uint32_t rtdisp; /* like above, but for max err to primary src */ 75 uint32_t rtdisp; /* like above, but for max err to primary src */
74 uint32_t refid; /* ref clock identifier */ 76 uint32_t refid; /* ref clock identifier */
75 uint64_t refts; /* reference timestamp. local time local clock */ 77 uint64_t refts; /* reference timestamp. local time local clock */
76 uint64_t origts; /* time at which request departed client */ 78 uint64_t origts; /* time at which request departed client */
77 uint64_t rxts; /* time at which request arrived at server */ 79 uint64_t rxts; /* time at which request arrived at server */
78 uint64_t txts; /* time at which request departed server */ 80 uint64_t txts; /* time at which request departed server */
79} ntp_message; 81} ntp_message;
80 82
81/* this structure holds data about results from querying offset from a peer */ 83/* this structure holds data about results from querying offset from a peer */
@@ -86,43 +88,55 @@ typedef struct {
86 double rtdelay; /* converted from the ntp_message */ 88 double rtdelay; /* converted from the ntp_message */
87 double rtdisp; /* converted from the ntp_message */ 89 double rtdisp; /* converted from the ntp_message */
88 double offset[AVG_NUM]; /* offsets from each response */ 90 double offset[AVG_NUM]; /* offsets from each response */
89 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 91 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
90} ntp_server_results; 92} ntp_server_results;
91 93
92/* bits 1,2 are the leap indicator */ 94/* bits 1,2 are the leap indicator */
93#define LI_MASK 0xc0 95#define LI_MASK 0xc0
94#define LI(x) ((x&LI_MASK)>>6) 96#define LI(x) ((x & LI_MASK) >> 6)
95#define LI_SET(x,y) do{ x |= ((y<<6)&LI_MASK); }while(0) 97#define LI_SET(x, y) \
98 do { \
99 x |= ((y << 6) & LI_MASK); \
100 } while (0)
96/* and these are the values of the leap indicator */ 101/* and these are the values of the leap indicator */
97#define LI_NOWARNING 0x00 102#define LI_NOWARNING 0x00
98#define LI_EXTRASEC 0x01 103#define LI_EXTRASEC 0x01
99#define LI_MISSINGSEC 0x02 104#define LI_MISSINGSEC 0x02
100#define LI_ALARM 0x03 105#define LI_ALARM 0x03
101/* bits 3,4,5 are the ntp version */ 106/* bits 3,4,5 are the ntp version */
102#define VN_MASK 0x38 107#define VN_MASK 0x38
103#define VN(x) ((x&VN_MASK)>>3) 108#define VN(x) ((x & VN_MASK) >> 3)
104#define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) 109#define VN_SET(x, y) \
110 do { \
111 x |= ((y << 3) & VN_MASK); \
112 } while (0)
105#define VN_RESERVED 0x02 113#define VN_RESERVED 0x02
106/* bits 6,7,8 are the ntp mode */ 114/* bits 6,7,8 are the ntp mode */
107#define MODE_MASK 0x07 115#define MODE_MASK 0x07
108#define MODE(x) (x&MODE_MASK) 116#define MODE(x) (x & MODE_MASK)
109#define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) 117#define MODE_SET(x, y) \
118 do { \
119 x |= (y & MODE_MASK); \
120 } while (0)
110/* here are some values */ 121/* here are some values */
111#define MODE_CLIENT 0x03 122#define MODE_CLIENT 0x03
112#define MODE_CONTROLMSG 0x06 123#define MODE_CONTROLMSG 0x06
113/* In control message, bits 8-10 are R,E,M bits */ 124/* In control message, bits 8-10 are R,E,M bits */
114#define REM_MASK 0xe0 125#define REM_MASK 0xe0
115#define REM_RESP 0x80 126#define REM_RESP 0x80
116#define REM_ERROR 0x40 127#define REM_ERROR 0x40
117#define REM_MORE 0x20 128#define REM_MORE 0x20
118/* In control message, bits 11 - 15 are opcode */ 129/* In control message, bits 11 - 15 are opcode */
119#define OP_MASK 0x1f 130#define OP_MASK 0x1f
120#define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) 131#define OP_SET(x, y) \
132 do { \
133 x |= (y & OP_MASK); \
134 } while (0)
121#define OP_READSTAT 0x01 135#define OP_READSTAT 0x01
122#define OP_READVAR 0x02 136#define OP_READVAR 0x02
123/* In peer status bytes, bits 6,7,8 determine clock selection status */ 137/* In peer status bytes, bits 6,7,8 determine clock selection status */
124#define PEER_SEL(x) ((ntohs(x)>>8)&0x07) 138#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07)
125#define PEER_INCLUDED 0x04 139#define PEER_INCLUDED 0x04
126#define PEER_SYNCSOURCE 0x06 140#define PEER_SYNCSOURCE 0x06
127 141
128/** 142/**
@@ -136,164 +150,175 @@ typedef struct {
136 150
137/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point" 151/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point"
138 number. note that these can be used as lvalues too */ 152 number. note that these can be used as lvalues too */
139#define L16(x) (((uint16_t*)&x)[0]) 153#define L16(x) (((uint16_t *)&x)[0])
140#define R16(x) (((uint16_t*)&x)[1]) 154#define R16(x) (((uint16_t *)&x)[1])
141/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point" 155/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
142 number. these too can be used as lvalues */ 156 number. these too can be used as lvalues */
143#define L32(x) (((uint32_t*)&x)[0]) 157#define L32(x) (((uint32_t *)&x)[0])
144#define R32(x) (((uint32_t*)&x)[1]) 158#define R32(x) (((uint32_t *)&x)[1])
145 159
146/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */ 160/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */
147#define EPOCHDIFF 0x83aa7e80UL 161#define EPOCHDIFF 0x83aa7e80UL
148 162
149/* extract a 32-bit ntp fixed point number into a double */ 163/* extract a 32-bit ntp fixed point number into a double */
150#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x))/65536.0) 164#define NTP32asDOUBLE(x) (ntohs(L16(x)) + ((double)ntohs(R16(x)) / 65536.0))
151 165
152/* likewise for a 64-bit ntp fp number */ 166/* likewise for a 64-bit ntp fp number */
153#define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\ 167#define NTP64asDOUBLE(n) \
154 (ntohl(L32(n))-EPOCHDIFF) + \ 168 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \
155 (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\ 169 (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \
156 0) 170 : 0)
157 171
158/* convert a struct timeval to a double */ 172/* convert a struct timeval to a double */
159#define TVasDOUBLE(x) (double)(x.tv_sec+(0.000001*x.tv_usec)) 173static double TVasDOUBLE(struct timeval time) {
174 return ((double)time.tv_sec + (0.000001 * (double)time.tv_usec));
175}
160 176
161/* convert an ntp 64-bit fp number to a struct timeval */ 177/* convert an ntp 64-bit fp number to a struct timeval */
162#define NTP64toTV(n,t) \ 178#define NTP64toTV(n, t) \
163 do{ if(!n) t.tv_sec = t.tv_usec = 0; \ 179 do { \
164 else { \ 180 if (!n) \
165 t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \ 181 t.tv_sec = t.tv_usec = 0; \
166 t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \ 182 else { \
167 } \ 183 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \
168 }while(0) 184 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \
185 } \
186 } while (0)
169 187
170/* convert a struct timeval to an ntp 64-bit fp number */ 188/* convert a struct timeval to an ntp 64-bit fp number */
171#define TVtoNTP64(t,n) \ 189#define TVtoNTP64(t, n) \
172 do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \ 190 do { \
173 else { \ 191 if (!t.tv_usec && !t.tv_sec) \
174 L32(n)=htonl(t.tv_sec + EPOCHDIFF); \ 192 n = 0x0UL; \
175 R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \ 193 else { \
176 } \ 194 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \
177 } while(0) 195 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \
196 } \
197 } while (0)
178 198
179/* NTP control message header is 12 bytes, plus any data in the data 199/* NTP control message header is 12 bytes, plus any data in the data
180 * field, plus null padding to the nearest 32-bit boundary per rfc. 200 * field, plus null padding to the nearest 32-bit boundary per rfc.
181 */ 201 */
182#define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((m.count)?4-(ntohs(m.count)%4):0)) 202#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((m.count) ? 4 - (ntohs(m.count) % 4) : 0))
183 203
184/* finally, a little helper or two for debugging: */ 204/* finally, a little helper or two for debugging: */
185#define DBG(x) do{if(verbose>1){ x; }}while(0); 205#define DBG(x) \
186#define PRINTSOCKADDR(x) \ 206 do { \
187 do{ \ 207 if (verbose > 1) { \
188 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ 208 x; \
189 }while(0); 209 } \
210 } while (0);
211#define PRINTSOCKADDR(x) \
212 do { \
213 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
214 } while (0);
190 215
191/* calculate the offset of the local clock */ 216/* calculate the offset of the local clock */
192static inline double calc_offset(const ntp_message *m, const struct timeval *t){ 217static inline double calc_offset(const ntp_message *message, const struct timeval *time_value) {
193 double client_tx, peer_rx, peer_tx, client_rx; 218 double client_tx = NTP64asDOUBLE(message->origts);
194 client_tx = NTP64asDOUBLE(m->origts); 219 double peer_rx = NTP64asDOUBLE(message->rxts);
195 peer_rx = NTP64asDOUBLE(m->rxts); 220 double peer_tx = NTP64asDOUBLE(message->txts);
196 peer_tx = NTP64asDOUBLE(m->txts); 221 double client_rx = TVasDOUBLE((*time_value));
197 client_rx=TVasDOUBLE((*t)); 222 return (((peer_tx - client_rx) + (peer_rx - client_tx)) / 2);
198 return (.5*((peer_tx-client_rx)+(peer_rx-client_tx)));
199} 223}
200 224
201/* print out a ntp packet in human readable/debuggable format */ 225/* print out a ntp packet in human readable/debuggable format */
202void print_ntp_message(const ntp_message *p){ 226void print_ntp_message(const ntp_message *message) {
203 struct timeval ref, orig, rx, tx; 227 struct timeval ref;
228 struct timeval orig;
204 229
205 NTP64toTV(p->refts,ref); 230 NTP64toTV(message->refts, ref);
206 NTP64toTV(p->origts,orig); 231 NTP64toTV(message->origts, orig);
207 NTP64toTV(p->rxts,rx);
208 NTP64toTV(p->txts,tx);
209 232
210 printf("packet contents:\n"); 233 printf("packet contents:\n");
211 printf("\tflags: 0x%.2x\n", p->flags); 234 printf("\tflags: 0x%.2x\n", message->flags);
212 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); 235 printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK);
213 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); 236 printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK);
214 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); 237 printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK);
215 printf("\tstratum = %d\n", p->stratum); 238 printf("\tstratum = %d\n", message->stratum);
216 printf("\tpoll = %g\n", pow(2, p->poll)); 239 printf("\tpoll = %g\n", pow(2, message->poll));
217 printf("\tprecision = %g\n", pow(2, p->precision)); 240 printf("\tprecision = %g\n", pow(2, message->precision));
218 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay)); 241 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(message->rtdelay));
219 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp)); 242 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(message->rtdisp));
220 printf("\trefid = %x\n", p->refid); 243 printf("\trefid = %x\n", message->refid);
221 printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts)); 244 printf("\trefts = %-.16g\n", NTP64asDOUBLE(message->refts));
222 printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts)); 245 printf("\torigts = %-.16g\n", NTP64asDOUBLE(message->origts));
223 printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts)); 246 printf("\trxts = %-.16g\n", NTP64asDOUBLE(message->rxts));
224 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); 247 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(message->txts));
225} 248}
226 249
227void setup_request(ntp_message *p){ 250void setup_request(ntp_message *message) {
228 struct timeval t; 251 memset(message, 0, sizeof(ntp_message));
229 252 LI_SET(message->flags, LI_ALARM);
230 memset(p, 0, sizeof(ntp_message)); 253 VN_SET(message->flags, 4);
231 LI_SET(p->flags, LI_ALARM); 254 MODE_SET(message->flags, MODE_CLIENT);
232 VN_SET(p->flags, 4); 255 message->poll = 4;
233 MODE_SET(p->flags, MODE_CLIENT); 256 message->precision = (int8_t)0xfa;
234 p->poll=4; 257 L16(message->rtdelay) = htons(1);
235 p->precision=(int8_t)0xfa; 258 L16(message->rtdisp) = htons(1);
236 L16(p->rtdelay)=htons(1);
237 L16(p->rtdisp)=htons(1);
238 259
260 struct timeval t;
239 gettimeofday(&t, NULL); 261 gettimeofday(&t, NULL);
240 TVtoNTP64(t,p->txts); 262 TVtoNTP64(t, message->txts);
241} 263}
242 264
243/* select the "best" server from a list of servers, and return its index. 265/* select the "best" server from a list of servers, and return its index.
244 * this is done by filtering servers based on stratum, dispersion, and 266 * this is done by filtering servers based on stratum, dispersion, and
245 * finally round-trip delay. */ 267 * finally round-trip delay. */
246int best_offset_server(const ntp_server_results *slist, int nservers){ 268static int best_offset_server(const ntp_server_results *slist, int nservers) {
247 int cserver=0, best_server=-1; 269 int best_server_index = -1;
248 270
249 /* for each server */ 271 /* for each server */
250 for(cserver=0; cserver<nservers; cserver++){ 272 for (int cserver = 0; cserver < nservers; cserver++) {
251 /* We don't want any servers that fails these tests */ 273 /* We don't want any servers that fails these tests */
252 /* Sort out servers that didn't respond or responede with a 0 stratum; 274 /* Sort out servers that didn't respond or responede with a 0 stratum;
253 * stratum 0 is for reference clocks so no NTP server should ever report 275 * stratum 0 is for reference clocks so no NTP server should ever report
254 * a stratum 0 */ 276 * a stratum 0 */
255 if ( slist[cserver].stratum == 0){ 277 if (slist[cserver].stratum == 0) {
256 if (verbose) printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); 278 if (verbose) {
279 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
280 }
257 continue; 281 continue;
258 } 282 }
259 /* Sort out servers with error flags */ 283 /* Sort out servers with error flags */
260 if ( LI(slist[cserver].flags) == LI_ALARM ){ 284 if (LI(slist[cserver].flags) == LI_ALARM) {
261 if (verbose) printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); 285 if (verbose) {
286 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
287 }
262 continue; 288 continue;
263 } 289 }
264 290
265 /* If we don't have a server yet, use the first one */ 291 /* If we don't have a server yet, use the first one */
266 if (best_server == -1) { 292 if (best_server_index == -1) {
267 best_server = cserver; 293 best_server_index = cserver;
268 DBG(printf("using peer %d as our first candidate\n", best_server)); 294 DBG(printf("using peer %d as our first candidate\n", best_server_index));
269 continue; 295 continue;
270 } 296 }
271 297
272 /* compare the server to the best one we've seen so far */ 298 /* compare the server to the best one we've seen so far */
273 /* does it have an equal or better stratum? */ 299 /* does it have an equal or better stratum? */
274 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); 300 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server_index));
275 if(slist[cserver].stratum <= slist[best_server].stratum){ 301 if (slist[cserver].stratum <= slist[best_server_index].stratum) {
276 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); 302 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server_index));
277 /* does it have an equal or better dispersion? */ 303 /* does it have an equal or better dispersion? */
278 if(slist[cserver].rtdisp <= slist[best_server].rtdisp){ 304 if (slist[cserver].rtdisp <= slist[best_server_index].rtdisp) {
279 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); 305 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server_index));
280 /* does it have a better rtdelay? */ 306 /* does it have a better rtdelay? */
281 if(slist[cserver].rtdelay < slist[best_server].rtdelay){ 307 if (slist[cserver].rtdelay < slist[best_server_index].rtdelay) {
282 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); 308 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server_index));
283 best_server = cserver; 309 best_server_index = cserver;
284 DBG(printf("peer %d is now our best candidate\n", best_server)); 310 DBG(printf("peer %d is now our best candidate\n", best_server_index));
285 } 311 }
286 } 312 }
287 } 313 }
288 } 314 }
289 315
290 if(best_server >= 0) { 316 if (best_server_index >= 0) {
291 DBG(printf("best server selected: peer %d\n", best_server)); 317 DBG(printf("best server selected: peer %d\n", best_server_index));
292 return best_server; 318 return best_server_index;
293 } else {
294 DBG(printf("no peers meeting synchronization criteria :(\n"));
295 return -1;
296 } 319 }
320 DBG(printf("no peers meeting synchronization criteria :(\n"));
321 return -1;
297} 322}
298 323
299/* do everything we need to get the total average offset 324/* do everything we need to get the total average offset
@@ -301,178 +326,269 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
301 * we don't waste time sitting around waiting for single packets. 326 * we don't waste time sitting around waiting for single packets.
302 * - we also "manually" handle resolving host names and connecting, because 327 * - we also "manually" handle resolving host names and connecting, because
303 * we have to do it in a way that our lazy macros don't handle currently :( */ 328 * we have to do it in a way that our lazy macros don't handle currently :( */
304double offset_request(const char *host, int *status){ 329typedef struct {
305 int i=0, j=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0; 330 mp_state_enum offset_result;
306 int servers_completed=0, one_read=0, servers_readable=0, best_index=-1; 331 double offset;
307 time_t now_time=0, start_ts=0; 332} offset_request_wrapper;
308 ntp_message *req=NULL; 333static offset_request_wrapper offset_request(const char *host, const char *port, int time_offset) {
309 double avg_offset=0.;
310 struct timeval recv_time;
311 struct addrinfo *ai=NULL, *ai_tmp=NULL, hints;
312 struct pollfd *ufds=NULL;
313 ntp_server_results *servers=NULL;
314
315 /* setup hints to only return results from getaddrinfo that we'd like */ 334 /* setup hints to only return results from getaddrinfo that we'd like */
335 struct addrinfo hints;
316 memset(&hints, 0, sizeof(struct addrinfo)); 336 memset(&hints, 0, sizeof(struct addrinfo));
317 hints.ai_family = address_family; 337 hints.ai_family = address_family;
318 hints.ai_protocol = IPPROTO_UDP; 338 hints.ai_protocol = IPPROTO_UDP;
319 hints.ai_socktype = SOCK_DGRAM; 339 hints.ai_socktype = SOCK_DGRAM;
320 340
321 /* fill in ai with the list of hosts resolved by the host name */ 341 bool is_socket;
322 ga_result = getaddrinfo(host, port, &hints, &ai); 342 struct addrinfo *addresses = NULL;
323 if(ga_result!=0){ 343 size_t num_hosts = 0;
324 die(STATE_UNKNOWN, "error getting address for %s: %s\n", 344 if (host[0] == '/') {
325 host, gai_strerror(ga_result)); 345 num_hosts = 1;
346 is_socket = true;
347 } else {
348 is_socket = false;
349
350 /* fill in ai with the list of hosts resolved by the host name */
351 int ga_result = getaddrinfo(host, port, &hints, &addresses);
352 if (ga_result != 0) {
353 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
354 }
355
356 /* count the number of returned hosts, and allocate stuff accordingly */
357 for (struct addrinfo *ai_tmp = addresses; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
358 num_hosts++;
359 }
360 }
361
362 ntp_message *req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts);
363
364 if (req == NULL) {
365 die(STATE_UNKNOWN, "can not allocate ntp message array");
326 } 366 }
367 int *socklist = (int *)malloc(sizeof(int) * num_hosts);
327 368
328 /* count the number of returned hosts, and allocate stuff accordingly */ 369 if (socklist == NULL) {
329 for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; } 370 die(STATE_UNKNOWN, "can not allocate socket array");
330 req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts); 371 }
331 if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array"); 372
332 socklist=(int*)malloc(sizeof(int)*num_hosts); 373 struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts);
333 if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); 374 if (ufds == NULL) {
334 ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts); 375 die(STATE_UNKNOWN, "can not allocate socket array");
335 if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); 376 }
336 servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts); 377
337 if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array"); 378 ntp_server_results *servers =
338 memset(servers, 0, sizeof(ntp_server_results)*num_hosts); 379 (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts);
339 DBG(printf("Found %d peers to check\n", num_hosts)); 380 if (servers == NULL) {
381 die(STATE_UNKNOWN, "can not allocate server array");
382 }
383 memset(servers, 0, sizeof(ntp_server_results) * num_hosts);
384 DBG(printf("Found %zu peers to check\n", num_hosts));
340 385
341 /* setup each socket for writing, and the corresponding struct pollfd */ 386 /* setup each socket for writing, and the corresponding struct pollfd */
342 ai_tmp=ai; 387 if (is_socket) {
343 for(i=0;ai_tmp;i++){ 388 socklist[0] = socket(AF_UNIX, SOCK_STREAM, 0);
344 socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); 389 if (socklist[0] == -1) {
345 if(socklist[i] == -1) { 390 DBG(printf("can't create socket: %s\n", strerror(errno)));
346 perror(NULL); 391 die(STATE_UNKNOWN, "can not create new socket\n");
347 die(STATE_UNKNOWN, "can not create new socket");
348 } 392 }
349 if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){ 393
394 struct sockaddr_un unix_socket = {
395 .sun_family = AF_UNIX,
396 };
397
398 strncpy(unix_socket.sun_path, host, strlen(host));
399
400 if (connect(socklist[0], &unix_socket, sizeof(unix_socket))) {
350 /* don't die here, because it is enough if there is one server 401 /* don't die here, because it is enough if there is one server
351 answering in time. This also would break for dual ipv4/6 stacked 402 answering in time. This also would break for dual ipv4/6 stacked
352 ntp servers when the client only supports on of them. 403 ntp servers when the client only supports on of them.
353 */ 404 */
354 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); 405 DBG(printf("can't create socket connection on peer %i: %s\n", 0, strerror(errno)));
355 } else { 406 } else {
356 ufds[i].fd=socklist[i]; 407 ufds[0].fd = socklist[0];
357 ufds[i].events=POLLIN; 408 ufds[0].events = POLLIN;
358 ufds[i].revents=0; 409 ufds[0].revents = 0;
410 }
411 } else {
412 struct addrinfo *ai_tmp = addresses;
413 for (int i = 0; ai_tmp; i++) {
414 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
415 if (socklist[i] == -1) {
416 perror(NULL);
417 die(STATE_UNKNOWN, "can not create new socket");
418 }
419 if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) {
420 /* don't die here, because it is enough if there is one server
421 answering in time. This also would break for dual ipv4/6 stacked
422 ntp servers when the client only supports on of them.
423 */
424 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno)));
425 } else {
426 ufds[i].fd = socklist[i];
427 ufds[i].events = POLLIN;
428 ufds[i].revents = 0;
429 }
430 ai_tmp = ai_tmp->ai_next;
359 } 431 }
360 ai_tmp = ai_tmp->ai_next;
361 } 432 }
362 433
363 /* now do AVG_NUM checks to each host. We stop before timeout/2 seconds 434 /* now do AVG_NUM checks to each host. We stop before timeout/2 seconds
364 * have passed in order to ensure post-processing and jitter time. */ 435 * have passed in order to ensure post-processing and jitter time. */
365 now_time=start_ts=time(NULL); 436 time_t start_ts = 0;
366 while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){ 437 time_t now_time = 0;
438 now_time = start_ts = time(NULL);
439 size_t servers_completed = 0;
440 bool one_read = false;
441 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) {
367 /* loop through each server and find each one which hasn't 442 /* loop through each server and find each one which hasn't
368 * been touched in the past second or so and is still lacking 443 * been touched in the past second or so and is still lacking
369 * some responses. For each of these servers, send a new request, 444 * some responses. For each of these servers, send a new request,
370 * and update the "waiting" timestamp with the current time. */ 445 * and update the "waiting" timestamp with the current time. */
371 now_time=time(NULL); 446 now_time = time(NULL);
372 447
373 for(i=0; i<num_hosts; i++){ 448 for (size_t i = 0; i < num_hosts; i++) {
374 if(servers[i].waiting<now_time && servers[i].num_responses<AVG_NUM){ 449 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) {
375 if(verbose && servers[i].waiting != 0) printf("re-"); 450 if (verbose && servers[i].waiting != 0) {
376 if(verbose) printf("sending request to peer %d\n", i); 451 printf("re-");
452 }
453 if (verbose) {
454 printf("sending request to peer %zu\n", i);
455 }
377 setup_request(&req[i]); 456 setup_request(&req[i]);
378 write(socklist[i], &req[i], sizeof(ntp_message)); 457 write(socklist[i], &req[i], sizeof(ntp_message));
379 servers[i].waiting=now_time; 458 servers[i].waiting = now_time;
380 break; 459 break;
381 } 460 }
382 } 461 }
383 462
384 /* quickly poll for any sockets with pending data */ 463 /* quickly poll for any sockets with pending data */
385 servers_readable=poll(ufds, num_hosts, 100); 464 int servers_readable = poll(ufds, num_hosts, 100);
386 if(servers_readable==-1){ 465 if (servers_readable == -1) {
387 perror("polling ntp sockets"); 466 perror("polling ntp sockets");
388 die(STATE_UNKNOWN, "communication errors"); 467 die(STATE_UNKNOWN, "communication errors");
389 } 468 }
390 469
391 /* read from any sockets with pending data */ 470 /* read from any sockets with pending data */
392 for(i=0; servers_readable && i<num_hosts; i++){ 471 for (size_t i = 0; servers_readable && i < num_hosts; i++) {
393 if(ufds[i].revents&POLLIN && servers[i].num_responses < AVG_NUM){ 472 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) {
394 if(verbose) { 473 if (verbose) {
395 printf("response from peer %d: ", i); 474 printf("response from peer %zu: ", i);
396 } 475 }
397 476
398 read(ufds[i].fd, &req[i], sizeof(ntp_message)); 477 read(ufds[i].fd, &req[i], sizeof(ntp_message));
478
479 struct timeval recv_time;
399 gettimeofday(&recv_time, NULL); 480 gettimeofday(&recv_time, NULL);
400 DBG(print_ntp_message(&req[i])); 481 DBG(print_ntp_message(&req[i]));
401 respnum=servers[i].num_responses++; 482 int respnum = servers[i].num_responses++;
402 servers[i].offset[respnum]=calc_offset(&req[i], &recv_time)+time_offset; 483 servers[i].offset[respnum] = calc_offset(&req[i], &recv_time) + time_offset;
403 if(verbose) { 484 if (verbose) {
404 printf("offset %.10g\n", servers[i].offset[respnum]); 485 printf("offset %.10g\n", servers[i].offset[respnum]);
405 } 486 }
406 servers[i].stratum=req[i].stratum; 487 servers[i].stratum = req[i].stratum;
407 servers[i].rtdisp=NTP32asDOUBLE(req[i].rtdisp); 488 servers[i].rtdisp = NTP32asDOUBLE(req[i].rtdisp);
408 servers[i].rtdelay=NTP32asDOUBLE(req[i].rtdelay); 489 servers[i].rtdelay = NTP32asDOUBLE(req[i].rtdelay);
409 servers[i].waiting=0; 490 servers[i].waiting = 0;
410 servers[i].flags=req[i].flags; 491 servers[i].flags = req[i].flags;
411 servers_readable--; 492 servers_readable--;
412 one_read = 1; 493 one_read = true;
413 if(servers[i].num_responses==AVG_NUM) servers_completed++; 494 if (servers[i].num_responses == AVG_NUM) {
495 servers_completed++;
496 }
414 } 497 }
415 } 498 }
416 /* lather, rinse, repeat. */ 499 /* lather, rinse, repeat. */
417 } 500 }
418 501
419 if (one_read == 0) { 502 if (!one_read) {
420 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 503 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
421 } 504 }
422 505
506 offset_request_wrapper result = {
507 .offset = 0,
508 .offset_result = STATE_UNKNOWN,
509 };
510
423 /* now, pick the best server from the list */ 511 /* now, pick the best server from the list */
424 best_index=best_offset_server(servers, num_hosts); 512 double avg_offset = 0.;
425 if(best_index < 0){ 513 int best_index = best_offset_server(servers, num_hosts);
426 *status=STATE_UNKNOWN; 514 if (best_index < 0) {
515 result.offset_result = STATE_UNKNOWN;
427 } else { 516 } else {
517 result.offset_result = STATE_OK;
428 /* finally, calculate the average offset */ 518 /* finally, calculate the average offset */
429 for(i=0; i<servers[best_index].num_responses;i++){ 519 for (int i = 0; i < servers[best_index].num_responses; i++) {
430 avg_offset+=servers[best_index].offset[i]; 520 avg_offset += servers[best_index].offset[i];
431 } 521 }
432 avg_offset/=servers[best_index].num_responses; 522 avg_offset /= servers[best_index].num_responses;
433 } 523 }
434 524
435 /* cleanup */ 525 /* cleanup */
436 for(j=0; j<num_hosts; j++){ close(socklist[j]); } 526 for (size_t j = 0; j < num_hosts; j++) {
527 close(socklist[j]);
528 }
437 free(socklist); 529 free(socklist);
438 free(ufds); 530 free(ufds);
439 free(servers); 531 free(servers);
440 free(req); 532 free(req);
441 freeaddrinfo(ai); 533 freeaddrinfo(addresses);
534
535 if (verbose) {
536 printf("overall average offset: %.10g\n", avg_offset);
537 }
442 538
443 if(verbose) printf("overall average offset: %.10g\n", avg_offset); 539 result.offset = avg_offset;
444 return avg_offset; 540 return result;
445} 541}
446 542
447int process_arguments(int argc, char **argv){ 543static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
448 int c; 544
449 int option=0; 545 enum {
450 static struct option longopts[] = { 546 output_format_index = CHAR_MAX + 1,
451 {"version", no_argument, 0, 'V'},
452 {"help", no_argument, 0, 'h'},
453 {"verbose", no_argument, 0, 'v'},
454 {"use-ipv4", no_argument, 0, '4'},
455 {"use-ipv6", no_argument, 0, '6'},
456 {"quiet", no_argument, 0, 'q'},
457 {"time-offset", optional_argument, 0, 'o'},
458 {"warning", required_argument, 0, 'w'},
459 {"critical", required_argument, 0, 'c'},
460 {"timeout", required_argument, 0, 't'},
461 {"hostname", required_argument, 0, 'H'},
462 {"port", required_argument, 0, 'p'},
463 {0, 0, 0, 0}
464 }; 547 };
465 548
549 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
550 {"help", no_argument, 0, 'h'},
551 {"verbose", no_argument, 0, 'v'},
552 {"use-ipv4", no_argument, 0, '4'},
553 {"use-ipv6", no_argument, 0, '6'},
554 {"quiet", no_argument, 0, 'q'},
555 {"time-offset", required_argument, 0, 'o'},
556 {"warning", required_argument, 0, 'w'},
557 {"critical", required_argument, 0, 'c'},
558 {"timeout", required_argument, 0, 't'},
559 {"hostname", required_argument, 0, 'H'},
560 {"port", required_argument, 0, 'p'},
561 {"output-format", required_argument, 0, output_format_index},
562 {0, 0, 0, 0}};
563
564 if (argc < 2) {
565 usage("\n");
566 }
466 567
467 if (argc < 2) 568 check_ntp_time_config_wrapper result = {
468 usage ("\n"); 569 .errorcode = OK,
570 .config = check_ntp_time_config_init(),
571 };
469 572
470 while (1) { 573 while (true) {
471 c = getopt_long (argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); 574 int option = 0;
472 if (c == -1 || c == EOF || c == 1) 575 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option);
576 if (option_char == -1 || option_char == EOF || option_char == 1) {
473 break; 577 break;
578 }
474 579
475 switch (c) { 580 switch (option_char) {
581 case output_format_index: {
582 parsed_output_format parser = mp_parse_output_format(optarg);
583 if (!parser.parsing_success) {
584 printf("Invalid output format: %s\n", optarg);
585 exit(STATE_UNKNOWN);
586 }
587
588 result.config.output_format_is_set = true;
589 result.config.output_format = parser.output_format;
590 break;
591 }
476 case 'h': 592 case 'h':
477 print_help(); 593 print_help();
478 exit(STATE_UNKNOWN); 594 exit(STATE_UNKNOWN);
@@ -485,143 +601,158 @@ int process_arguments(int argc, char **argv){
485 verbose++; 601 verbose++;
486 break; 602 break;
487 case 'q': 603 case 'q':
488 quiet = true; 604 result.config.quiet = true;
489 break;
490 case 'w':
491 owarn = optarg;
492 break;
493 case 'c':
494 ocrit = optarg;
495 break; 605 break;
606 case 'w': {
607 mp_range_parsed tmp = mp_parse_range_string(optarg);
608 if (tmp.error != MP_PARSING_SUCCESS) {
609 die(STATE_UNKNOWN, "failed to parse warning threshold");
610 }
611
612 result.config.offset_thresholds =
613 mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
614 } break;
615 case 'c': {
616 mp_range_parsed tmp = mp_parse_range_string(optarg);
617 if (tmp.error != MP_PARSING_SUCCESS) {
618 die(STATE_UNKNOWN, "failed to parse crit threshold");
619 }
620
621 result.config.offset_thresholds =
622 mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
623 } break;
496 case 'H': 624 case 'H':
497 if(!is_host(optarg)) 625 if (!is_host(optarg) && (optarg[0] != '/')) {
498 usage2(_("Invalid hostname/address"), optarg); 626 usage2(_("Invalid hostname/address"), optarg);
499 server_address = strdup(optarg); 627 }
628 result.config.server_address = strdup(optarg);
500 break; 629 break;
501 case 'p': 630 case 'p':
502 port = strdup(optarg); 631 result.config.port = strdup(optarg);
503 break; 632 break;
504 case 't': 633 case 't':
505 socket_timeout=atoi(optarg); 634 socket_timeout = atoi(optarg);
506 break; 635 break;
507 case 'o': 636 case 'o':
508 time_offset=atoi(optarg); 637 result.config.time_offset = atoi(optarg);
509 break; 638 break;
510 case '4': 639 case '4':
511 address_family = AF_INET; 640 address_family = AF_INET;
512 break; 641 break;
513 case '6': 642 case '6':
514#ifdef USE_IPV6
515 address_family = AF_INET6; 643 address_family = AF_INET6;
516#else
517 usage4 (_("IPv6 support not available"));
518#endif
519 break; 644 break;
520 case '?': 645 case '?':
521 /* print short usage statement if args not parsable */ 646 /* print short usage statement if args not parsable */
522 usage5 (); 647 usage5();
523 break; 648 break;
524 } 649 }
525 } 650 }
526 651
527 if(server_address == NULL){ 652 if (result.config.server_address == NULL) {
528 usage4(_("Hostname was not supplied")); 653 usage4(_("Hostname was not supplied"));
529 } 654 }
530 655
531 return 0; 656 return result;
532} 657}
533 658
534char *perfd_offset (double offset) { 659int main(int argc, char *argv[]) {
535 return fperfdata ("offset", offset, "s", 660#ifdef __OpenBSD__
536 true, offset_thresholds->warning->end, 661 /* - rpath is required to read --extra-opts (given up later)
537 true, offset_thresholds->critical->end, 662 * - inet is required for sockets
538 false, 0, false, 0); 663 * - unix is required for Unix domain sockets
539} 664 * - dns is required for name lookups */
665 pledge("stdio rpath inet unix dns", NULL);
666#endif // __OpenBSD__
540 667
541int main(int argc, char *argv[]){ 668 setlocale(LC_ALL, "");
542 int result, offset_result; 669 bindtextdomain(PACKAGE, LOCALEDIR);
543 double offset=0; 670 textdomain(PACKAGE);
544 char *result_line, *perfdata_line;
545 671
546 setlocale (LC_ALL, ""); 672 /* Parse extra opts if any */
547 bindtextdomain (PACKAGE, LOCALEDIR); 673 argv = np_extra_opts(&argc, argv, progname);
548 textdomain (PACKAGE);
549 674
550 result = offset_result = STATE_OK; 675 check_ntp_time_config_wrapper tmp_config = process_arguments(argc, argv);
551 676
552 /* Parse extra opts if any */ 677 if (tmp_config.errorcode == ERROR) {
553 argv=np_extra_opts (&argc, argv, progname); 678 usage4(_("Could not parse arguments"));
679 }
680
681#ifdef __OpenBSD__
682 pledge("stdio inet unix dns", NULL);
683#endif // __OpenBSD__
554 684
555 if (process_arguments (argc, argv) == ERROR) 685 const check_ntp_time_config config = tmp_config.config;
556 usage4 (_("Could not parse arguments"));
557 686
558 set_thresholds(&offset_thresholds, owarn, ocrit); 687 if (config.output_format_is_set) {
688 mp_set_format(config.output_format);
689 }
559 690
560 /* initialize alarm signal handling */ 691 /* initialize alarm signal handling */
561 signal (SIGALRM, socket_timeout_alarm_handler); 692 signal(SIGALRM, socket_timeout_alarm_handler);
562 693
563 /* set socket timeout */ 694 /* set socket timeout */
564 alarm (socket_timeout); 695 alarm(socket_timeout);
565 696
566 offset = offset_request(server_address, &offset_result); 697 mp_check overall = mp_check_init();
567 if (offset_result == STATE_UNKNOWN) {
568 result = ( (!quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
569 } else {
570 result = get_status(fabs(offset), offset_thresholds);
571 }
572 698
573 switch (result) { 699 mp_subcheck sc_offset = mp_subcheck_init();
574 case STATE_CRITICAL : 700 offset_request_wrapper offset_result =
575 xasprintf(&result_line, _("NTP CRITICAL:")); 701 offset_request(config.server_address, config.port, config.time_offset);
576 break; 702
577 case STATE_WARNING : 703 if (offset_result.offset_result == STATE_UNKNOWN) {
578 xasprintf(&result_line, _("NTP WARNING:")); 704 sc_offset =
579 break; 705 mp_set_subcheck_state(sc_offset, (!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
580 case STATE_OK : 706 xasprintf(&sc_offset.output, "Offset unknown");
581 xasprintf(&result_line, _("NTP OK:")); 707 mp_add_subcheck_to_check(&overall, sc_offset);
582 break; 708 mp_exit(overall);
583 default :
584 xasprintf(&result_line, _("NTP UNKNOWN:"));
585 break;
586 }
587 if(offset_result == STATE_UNKNOWN){
588 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
589 xasprintf(&perfdata_line, "");
590 } else {
591 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
592 xasprintf(&perfdata_line, "%s", perfd_offset(offset));
593 } 709 }
594 printf("%s|%s\n", result_line, perfdata_line);
595 710
596 if(server_address!=NULL) free(server_address); 711 xasprintf(&sc_offset.output, "Offset: %.6fs", offset_result.offset);
597 return result; 712
713 mp_perfdata pd_offset = perfdata_init();
714 pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset));
715 pd_offset.label = "offset";
716 pd_offset.uom = "s";
717 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
718
719 sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset));
720
721 mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
722 mp_add_subcheck_to_check(&overall, sc_offset);
723
724 if (config.server_address != NULL) {
725 free(config.server_address);
726 }
727 mp_exit(overall);
598} 728}
599 729
600void print_help(void){ 730void print_help(void) {
601 print_revision(progname, NP_VERSION); 731 print_revision(progname, NP_VERSION);
602 732
603 printf ("Copyright (c) 2006 Sean Finney\n"); 733 printf("Copyright (c) 2006 Sean Finney\n");
604 printf (COPYRIGHT, copyright, email); 734 printf(COPYRIGHT, copyright, email);
605 735
606 printf ("%s\n", _("This plugin checks the clock offset with the ntp server")); 736 printf("%s\n", _("This plugin checks the clock offset with the ntp server"));
607 737
608 printf ("\n\n"); 738 printf("\n\n");
609 739
610 print_usage(); 740 print_usage();
611 printf (UT_HELP_VRSN); 741 printf(UT_HELP_VRSN);
612 printf (UT_EXTRA_OPTS); 742 printf(UT_EXTRA_OPTS);
613 printf (UT_IPv46); 743 printf(UT_IPv46);
614 printf (UT_HOST_PORT, 'p', "123"); 744 printf(UT_HOST_PORT, 'p', "123");
615 printf (" %s\n", "-q, --quiet"); 745 printf(" %s\n", "-q, --quiet");
616 printf (" %s\n", _("Returns UNKNOWN instead of CRITICAL if offset cannot be found")); 746 printf(" %s\n", _("Returns UNKNOWN instead of CRITICAL if offset cannot be found"));
617 printf (" %s\n", "-w, --warning=THRESHOLD"); 747 printf(" %s\n", "-w, --warning=THRESHOLD");
618 printf (" %s\n", _("Offset to result in warning status (seconds)")); 748 printf(" %s\n", _("Offset to result in warning status (seconds)"));
619 printf (" %s\n", "-c, --critical=THRESHOLD"); 749 printf(" %s\n", "-c, --critical=THRESHOLD");
620 printf (" %s\n", _("Offset to result in critical status (seconds)")); 750 printf(" %s\n", _("Offset to result in critical status (seconds)"));
621 printf (" %s\n", "-o, --time_offset=INTEGER"); 751 printf(" %s\n", "-o, --time-offset=INTEGER");
622 printf (" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); 752 printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)"));
623 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 753 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
624 printf (UT_VERBOSE); 754 printf(UT_VERBOSE);
755 printf(UT_OUTPUT_FORMAT);
625 756
626 printf("\n"); 757 printf("\n");
627 printf("%s\n", _("This plugin checks the clock offset between the local host and a")); 758 printf("%s\n", _("This plugin checks the clock offset between the local host and a"));
@@ -641,13 +772,11 @@ void print_help(void){
641 printf("%s\n", _("Examples:")); 772 printf("%s\n", _("Examples:"));
642 printf(" %s\n", ("./check_ntp_time -H ntpserv -w 0.5 -c 1")); 773 printf(" %s\n", ("./check_ntp_time -H ntpserv -w 0.5 -c 1"));
643 774
644 printf (UT_SUPPORT); 775 printf(UT_SUPPORT);
645} 776}
646 777
647void 778void print_usage(void) {
648print_usage(void) 779 printf("%s\n", _("Usage:"));
649{ 780 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n",
650 printf ("%s\n", _("Usage:")); 781 progname);
651 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n", progname);
652} 782}
653
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h
new file mode 100644
index 00000000..9bbd82aa
--- /dev/null
+++ b/plugins/check_ntp_time.d/config.h
@@ -0,0 +1,43 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
6#include <stddef.h>
7
8typedef struct {
9 char *server_address;
10 char *port;
11
12 bool quiet;
13 int time_offset;
14
15 mp_thresholds offset_thresholds;
16
17 bool output_format_is_set;
18 mp_output_format output_format;
19} check_ntp_time_config;
20
21check_ntp_time_config check_ntp_time_config_init() {
22 check_ntp_time_config tmp = {
23 .server_address = NULL,
24 .port = "123",
25
26 .quiet = false,
27 .time_offset = 0,
28
29 .offset_thresholds = mp_thresholds_init(),
30
31 .output_format_is_set = false,
32 };
33
34 mp_range warning = mp_range_init();
35 warning = mp_range_set_end(warning, mp_create_pd_value(60));
36 tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, warning);
37
38 mp_range critical = mp_range_init();
39 critical = mp_range_set_end(warning, mp_create_pd_value(120));
40 tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, critical);
41
42 return tmp;
43}
diff --git a/plugins/check_nwstat.c b/plugins/check_nwstat.c
deleted file mode 100644
index 10c493b6..00000000
--- a/plugins/check_nwstat.c
+++ /dev/null
@@ -1,1740 +0,0 @@
1/*****************************************************************************
2*
3* Monitoring check_nwstat plugin
4*
5* License: GPL
6* Copyright (c) 2000-2007 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-2007";
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
92char *server_address=NULL;
93char *volume_name=NULL;
94char *nlm_name=NULL;
95char *nrmp_name=NULL;
96char *nrmm_name=NULL;
97char *nrms_name=NULL;
98char *nss1_name=NULL;
99char *nss2_name=NULL;
100char *nss3_name=NULL;
101char *nss4_name=NULL;
102char *nss5_name=NULL;
103char *nss6_name=NULL;
104char *nss7_name=NULL;
105int server_port=PORT;
106unsigned long warning_value=0L;
107unsigned long critical_value=0L;
108bool check_warning_value = false;
109bool check_critical_value = false;
110bool check_netware_version = false;
111enum checkvar vars_to_check = NONE;
112int sap_number=-1;
113
114int process_arguments(int, char **);
115void print_help(void);
116void print_usage(void);
117
118
119
120int
121main(int argc, char **argv) {
122 int result = STATE_UNKNOWN;
123 int sd;
124 char *send_buffer=NULL;
125 char recv_buffer[MAX_INPUT_BUFFER];
126 char *output_message=NULL;
127 char *temp_buffer=NULL;
128 char *netware_version=NULL;
129
130 int time_sync_status=0;
131 int nrm_health_status=0;
132 unsigned long total_cache_buffers=0;
133 unsigned long dirty_cache_buffers=0;
134 unsigned long open_files=0;
135 unsigned long abended_threads=0;
136 unsigned long max_service_processes=0;
137 unsigned long current_service_processes=0;
138 unsigned long free_disk_space=0L;
139 unsigned long nrmp_value=0L;
140 unsigned long nrmm_value=0L;
141 unsigned long nrms_value=0L;
142 unsigned long nss1_value=0L;
143 unsigned long nss2_value=0L;
144 unsigned long nss3_value=0L;
145 unsigned long nss4_value=0L;
146 unsigned long nss5_value=0L;
147 unsigned long nss6_value=0L;
148 unsigned long nss7_value=0L;
149 unsigned long total_disk_space=0L;
150 unsigned long used_disk_space=0L;
151 unsigned long percent_used_disk_space=0L;
152 unsigned long purgeable_disk_space=0L;
153 unsigned long non_purgeable_disk_space=0L;
154 unsigned long percent_free_space=0;
155 unsigned long percent_purgeable_space=0;
156 unsigned long percent_non_purgeable_space=0;
157 unsigned long current_connections=0L;
158 unsigned long utilization=0L;
159 unsigned long cache_hits=0;
160 unsigned long cache_buffers=0L;
161 unsigned long lru_time=0L;
162 unsigned long max_packet_receive_buffers=0;
163 unsigned long used_packet_receive_buffers=0;
164 unsigned long percent_used_packet_receive_buffers=0L;
165 unsigned long sap_entries=0;
166 char uptime[MAX_INPUT_BUFFER];
167
168 setlocale (LC_ALL, "");
169 bindtextdomain (PACKAGE, LOCALEDIR);
170 textdomain (PACKAGE);
171
172 /* Parse extra opts if any */
173 argv=np_extra_opts(&argc, argv, progname);
174
175 if (process_arguments(argc,argv) == ERROR)
176 usage4 (_("Could not parse arguments"));
177
178 /* initialize alarm signal handling */
179 signal(SIGALRM,socket_timeout_alarm_handler);
180
181 /* set socket timeout */
182 alarm(socket_timeout);
183
184 /* open connection */
185 my_tcp_connect (server_address, server_port, &sd);
186
187 /* get OS version string */
188 if (check_netware_version) {
189 send_buffer = strdup ("S19\r\n");
190 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
191 if (result!=STATE_OK)
192 return result;
193 if (!strcmp(recv_buffer,"-1\n"))
194 netware_version = strdup("");
195 else {
196 recv_buffer[strlen(recv_buffer)-1]=0;
197 xasprintf (&netware_version,_("NetWare %s: "),recv_buffer);
198 }
199 } else
200 netware_version = strdup("");
201
202
203 /* check CPU load */
204 if (vars_to_check==LOAD1 || vars_to_check==LOAD5 || vars_to_check==LOAD15) {
205
206 switch(vars_to_check) {
207 case LOAD1:
208 temp_buffer = strdup ("1");
209 break;
210 case LOAD5:
211 temp_buffer = strdup ("5");
212 break;
213 default:
214 temp_buffer = strdup ("15");
215 break;
216 }
217
218 close(sd);
219 my_tcp_connect (server_address, server_port, &sd);
220
221 xasprintf (&send_buffer,"UTIL%s\r\n",temp_buffer);
222 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
223 if (result!=STATE_OK)
224 return result;
225 utilization=strtoul(recv_buffer,NULL,10);
226
227 close(sd);
228 my_tcp_connect (server_address, server_port, &sd);
229
230 send_buffer = strdup ("UPTIME\r\n");
231 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
232 if (result!=STATE_OK)
233 return result;
234 recv_buffer[strlen(recv_buffer)-1]=0;
235 sprintf(uptime,_("Up %s,"),recv_buffer);
236
237 if (check_critical_value && utilization >= critical_value)
238 result=STATE_CRITICAL;
239 else if (check_warning_value && utilization >= warning_value)
240 result=STATE_WARNING;
241
242 xasprintf (&output_message,
243 _("Load %s - %s %s-min load average = %lu%%|load%s=%lu;%lu;%lu;0;100"),
244 state_text(result),
245 uptime,
246 temp_buffer,
247 utilization,
248 temp_buffer,
249 utilization,
250 warning_value,
251 critical_value);
252
253 /* check number of user connections */
254 } else if (vars_to_check==CONNS) {
255
256 close(sd);
257 my_tcp_connect (server_address, server_port, &sd);
258
259 send_buffer = strdup ("CONNECT\r\n");
260 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
261 if (result!=STATE_OK)
262 return result;
263 current_connections=strtoul(recv_buffer,NULL,10);
264
265 if (check_critical_value && current_connections >= critical_value)
266 result=STATE_CRITICAL;
267 else if (check_warning_value && current_connections >= warning_value)
268 result=STATE_WARNING;
269
270 xasprintf (&output_message,
271 _("Conns %s - %lu current connections|Conns=%lu;%lu;%lu;;"),
272 state_text(result),
273 current_connections,
274 current_connections,
275 warning_value,
276 critical_value);
277
278 /* check % long term cache hits */
279 } else if (vars_to_check==LTCH) {
280
281 close(sd);
282 my_tcp_connect (server_address, server_port, &sd);
283
284 send_buffer = strdup ("S1\r\n");
285 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
286 if (result!=STATE_OK)
287 return result;
288 cache_hits=atoi(recv_buffer);
289
290 if (check_critical_value && cache_hits <= critical_value)
291 result=STATE_CRITICAL;
292 else if (check_warning_value && cache_hits <= warning_value)
293 result=STATE_WARNING;
294
295 xasprintf (&output_message,
296 _("%s: Long term cache hits = %lu%%"),
297 state_text(result),
298 cache_hits);
299
300 /* check cache buffers */
301 } else if (vars_to_check==CBUFF) {
302
303 close(sd);
304 my_tcp_connect (server_address, server_port, &sd);
305
306 send_buffer = strdup ("S2\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,
318 _("%s: Total cache buffers = %lu|Cachebuffers=%lu;%lu;%lu;;"),
319 state_text(result),
320 cache_buffers,
321 cache_buffers,
322 warning_value,
323 critical_value);
324
325 /* check dirty cache buffers */
326 } else if (vars_to_check==CDBUFF) {
327
328 close(sd);
329 my_tcp_connect (server_address, server_port, &sd);
330
331 send_buffer = strdup ("S3\r\n");
332 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
333 if (result!=STATE_OK)
334 return result;
335 cache_buffers=strtoul(recv_buffer,NULL,10);
336
337 if (check_critical_value && cache_buffers >= critical_value)
338 result=STATE_CRITICAL;
339 else if (check_warning_value && cache_buffers >= warning_value)
340 result=STATE_WARNING;
341
342 xasprintf (&output_message,
343 _("%s: Dirty cache buffers = %lu|Dirty-Cache-Buffers=%lu;%lu;%lu;;"),
344 state_text(result),
345 cache_buffers,
346 cache_buffers,
347 warning_value,
348 critical_value);
349
350 /* check LRU sitting time in minutes */
351 } else if (vars_to_check==LRUM) {
352
353 close(sd);
354 my_tcp_connect (server_address, server_port, &sd);
355
356 send_buffer = strdup ("S5\r\n");
357 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
358 if (result!=STATE_OK)
359 return result;
360 lru_time=strtoul(recv_buffer,NULL,10);
361
362 if (check_critical_value && lru_time <= critical_value)
363 result=STATE_CRITICAL;
364 else if (check_warning_value && lru_time <= warning_value)
365 result=STATE_WARNING;
366
367 xasprintf (&output_message,
368 _("%s: LRU sitting time = %lu minutes"),
369 state_text(result),
370 lru_time);
371
372
373 /* check KB free space on volume */
374 } else if (vars_to_check==VKF) {
375
376 close(sd);
377 my_tcp_connect (server_address, server_port, &sd);
378
379 xasprintf (&send_buffer,"VKF%s\r\n",volume_name);
380 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
381 if (result!=STATE_OK)
382 return result;
383
384 if (!strcmp(recv_buffer,"-1\n")) {
385 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
386 result=STATE_CRITICAL;
387 } else {
388 free_disk_space=strtoul(recv_buffer,NULL,10);
389 if (check_critical_value && free_disk_space <= critical_value)
390 result=STATE_CRITICAL;
391 else if (check_warning_value && free_disk_space <= warning_value)
392 result=STATE_WARNING;
393 xasprintf (&output_message,
394 _("%s%lu KB free on volume %s|KBFree%s=%lu;%lu;%lu;;"),
395 (result==STATE_OK)?"":_("Only "),
396 free_disk_space,
397 volume_name,
398 volume_name,
399 free_disk_space,
400 warning_value,
401 critical_value);
402 }
403
404 /* check MB free space on volume */
405 } else if (vars_to_check==VMF) {
406
407 xasprintf (&send_buffer,"VMF%s\r\n",volume_name);
408 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
409 if (result!=STATE_OK)
410 return result;
411
412 if (!strcmp(recv_buffer,"-1\n")) {
413 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
414 result=STATE_CRITICAL;
415 } else {
416 free_disk_space=strtoul(recv_buffer,NULL,10);
417 if (check_critical_value && free_disk_space <= critical_value)
418 result=STATE_CRITICAL;
419 else if (check_warning_value && free_disk_space <= warning_value)
420 result=STATE_WARNING;
421 xasprintf (&output_message,
422 _("%s%lu MB free on volume %s|MBFree%s=%lu;%lu;%lu;;"),
423 (result==STATE_OK)?"":_("Only "),
424 free_disk_space,
425 volume_name,
426 volume_name,
427 free_disk_space,
428 warning_value,
429 critical_value);
430 }
431 /* check MB used space on volume */
432 } else if (vars_to_check==VMU) {
433
434 xasprintf (&send_buffer,"VMU%s\r\n",volume_name);
435 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
436 if (result!=STATE_OK)
437 return result;
438
439 if (!strcmp(recv_buffer,"-1\n")) {
440 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
441 result=STATE_CRITICAL;
442 } else {
443 free_disk_space=strtoul(recv_buffer,NULL,10);
444 if (check_critical_value && free_disk_space <= critical_value)
445 result=STATE_CRITICAL;
446 else if (check_warning_value && free_disk_space <= warning_value)
447 result=STATE_WARNING;
448 xasprintf (&output_message,
449 _("%s%lu MB used on volume %s|MBUsed%s=%lu;%lu;%lu;;"),
450 (result==STATE_OK)?"":_("Only "),
451 free_disk_space,
452 volume_name,
453 volume_name,
454 free_disk_space,
455 warning_value,
456 critical_value);
457 }
458 /* check % used space on volume */
459 } else if (vars_to_check==VPU) {
460 close(sd);
461 my_tcp_connect (server_address, server_port, &sd);
462
463 asprintf (&send_buffer,"VMU%s\r\n",volume_name);
464 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
465
466 if (result!=STATE_OK)
467 return result;
468
469 if (!strcmp(recv_buffer,"-1\n")) {
470 asprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
471 result=STATE_CRITICAL;
472
473 } else {
474 used_disk_space=strtoul(recv_buffer,NULL,10);
475 close(sd);
476 my_tcp_connect (server_address, server_port, &sd);
477 /* get total volume in MB */
478 asprintf (&send_buffer,"VMS%s\r\n",volume_name);
479 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
480 if (result!=STATE_OK)
481 return result;
482 total_disk_space=strtoul(recv_buffer,NULL,10);
483 /* calculate percent used on volume */
484 percent_used_disk_space=(unsigned long)(((double)used_disk_space/(double)total_disk_space)*100.0);
485
486 if (check_critical_value && percent_used_disk_space >= critical_value)
487 result=STATE_CRITICAL;
488 else if (check_warning_value && percent_used_disk_space >= warning_value)
489 result=STATE_WARNING;
490
491 asprintf (&output_message,_("%lu MB (%lu%%) used on volume %s - total %lu MB|Used space in percent on %s=%lu;%lu;%lu;0;100"),
492 used_disk_space,
493 percent_used_disk_space,
494 volume_name,
495 total_disk_space,
496 volume_name,
497 percent_used_disk_space,
498 warning_value,
499 critical_value
500 );
501 }
502
503 /* check % free space on volume */
504 } else if (vars_to_check==VPF) {
505
506 close(sd);
507 my_tcp_connect (server_address, server_port, &sd);
508
509 xasprintf (&send_buffer,"VKF%s\r\n",volume_name);
510 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
511 if (result!=STATE_OK)
512 return result;
513
514 if (!strcmp(recv_buffer,"-1\n")) {
515
516 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
517 result=STATE_CRITICAL;
518
519 } else {
520
521 free_disk_space=strtoul(recv_buffer,NULL,10);
522
523 close(sd);
524 my_tcp_connect (server_address, server_port, &sd);
525
526 xasprintf (&send_buffer,"VKS%s\r\n",volume_name);
527 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
528 if (result!=STATE_OK)
529 return result;
530 total_disk_space=strtoul(recv_buffer,NULL,10);
531
532 percent_free_space=(unsigned long)(((double)free_disk_space/(double)total_disk_space)*100.0);
533
534 if (check_critical_value && percent_free_space <= critical_value)
535 result=STATE_CRITICAL;
536 else if (check_warning_value && percent_free_space <= warning_value)
537 result=STATE_WARNING;
538 free_disk_space/=1024;
539 total_disk_space/=1024;
540 xasprintf (&output_message,_("%lu MB (%lu%%) free on volume %s - total %lu MB|FreeMB%s=%lu;%lu;%lu;0;100"),
541 free_disk_space,
542 percent_free_space,
543 volume_name,
544 total_disk_space,
545 volume_name,
546 percent_free_space,
547 warning_value,
548 critical_value
549 );
550 }
551
552 /* check to see if DS Database is open or closed */
553 } else if (vars_to_check==DSDB) {
554
555 close(sd);
556 my_tcp_connect (server_address, server_port, &sd);
557
558 send_buffer = strdup ("S11\r\n");
559 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
560 if (result!=STATE_OK)
561 return result;
562 if (atoi(recv_buffer)==1)
563 result=STATE_OK;
564 else
565 result=STATE_WARNING;
566
567 close(sd);
568 my_tcp_connect (server_address, server_port, &sd);
569
570 send_buffer = strdup ("S13\r\n");
571 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
572 temp_buffer=strtok(recv_buffer,"\r\n");
573
574 xasprintf (&output_message,_("Directory Services Database is %s (DS version %s)"),(result==STATE_OK)?"open":"closed",temp_buffer);
575
576 /* check to see if logins are enabled */
577 } else if (vars_to_check==LOGINS) {
578
579 close(sd);
580 my_tcp_connect (server_address, server_port, &sd);
581
582 send_buffer = strdup ("S12\r\n");
583 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
584 if (result!=STATE_OK)
585 return result;
586 if (atoi(recv_buffer)==1)
587 result=STATE_OK;
588 else
589 result=STATE_WARNING;
590
591 xasprintf (&output_message,_("Logins are %s"),(result==STATE_OK)?_("enabled"):_("disabled"));
592
593
594 /* check NRM Health Status Summary*/
595 } else if (vars_to_check==NRMH) {
596
597 xasprintf (&send_buffer,"NRMH\r\n");
598 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
599 if (result!=STATE_OK)
600 return result;
601
602 nrm_health_status=atoi(recv_buffer);
603
604 if (nrm_health_status==2) {
605 result=STATE_OK;
606 xasprintf (&output_message,_("CRITICAL - NRM Status is bad!"));
607 }
608 else {
609 if (nrm_health_status==1) {
610 result=STATE_WARNING;
611 xasprintf (&output_message,_("Warning - NRM Status is suspect!"));
612 }
613
614 xasprintf (&output_message,_("OK - NRM Status is good!"));
615 }
616
617
618
619 /* check packet receive buffers */
620 } else if (vars_to_check==UPRB || vars_to_check==PUPRB) {
621
622 close(sd);
623 my_tcp_connect (server_address, server_port, &sd);
624
625 xasprintf (&send_buffer,"S15\r\n");
626 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
627 if (result!=STATE_OK)
628 return result;
629
630 used_packet_receive_buffers=atoi(recv_buffer);
631
632 close(sd);
633 my_tcp_connect (server_address, server_port, &sd);
634
635 xasprintf (&send_buffer,"S16\r\n");
636 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
637 if (result!=STATE_OK)
638 return result;
639
640 max_packet_receive_buffers=atoi(recv_buffer);
641
642 percent_used_packet_receive_buffers=(unsigned long)(((double)used_packet_receive_buffers/(double)max_packet_receive_buffers)*100.0);
643
644 if (vars_to_check==UPRB) {
645 if (check_critical_value && used_packet_receive_buffers >= critical_value)
646 result=STATE_CRITICAL;
647 else if (check_warning_value && used_packet_receive_buffers >= warning_value)
648 result=STATE_WARNING;
649 } else {
650 if (check_critical_value && percent_used_packet_receive_buffers >= critical_value)
651 result=STATE_CRITICAL;
652 else if (check_warning_value && percent_used_packet_receive_buffers >= warning_value)
653 result=STATE_WARNING;
654 }
655
656 xasprintf (&output_message,_("%lu of %lu (%lu%%) packet receive buffers used"),used_packet_receive_buffers,max_packet_receive_buffers,percent_used_packet_receive_buffers);
657
658 /* check SAP table entries */
659 } else if (vars_to_check==SAPENTRIES) {
660
661 close(sd);
662 my_tcp_connect (server_address, server_port, &sd);
663
664 if (sap_number==-1)
665 xasprintf (&send_buffer,"S9\r\n");
666 else
667 xasprintf (&send_buffer,"S9.%d\r\n",sap_number);
668 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
669 if (result!=STATE_OK)
670 return result;
671
672 sap_entries=atoi(recv_buffer);
673
674 if (check_critical_value && sap_entries >= critical_value)
675 result=STATE_CRITICAL;
676 else if (check_warning_value && sap_entries >= warning_value)
677 result=STATE_WARNING;
678
679 if (sap_number==-1)
680 xasprintf (&output_message,_("%lu entries in SAP table"),sap_entries);
681 else
682 xasprintf (&output_message,_("%lu entries in SAP table for SAP type %d"),sap_entries,sap_number);
683
684 /* check KB purgeable space on volume */
685 } else if (vars_to_check==VKP) {
686
687 close(sd);
688 my_tcp_connect (server_address, server_port, &sd);
689
690 xasprintf (&send_buffer,"VKP%s\r\n",volume_name);
691 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
692 if (result!=STATE_OK)
693 return result;
694
695 if (!strcmp(recv_buffer,"-1\n")) {
696 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
697 result=STATE_CRITICAL;
698 } else {
699 purgeable_disk_space=strtoul(recv_buffer,NULL,10);
700 if (check_critical_value && purgeable_disk_space >= critical_value)
701 result=STATE_CRITICAL;
702 else if (check_warning_value && purgeable_disk_space >= warning_value)
703 result=STATE_WARNING;
704 xasprintf (&output_message,_("%s%lu KB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"),
705 (result==STATE_OK)?"":_("Only "),
706 purgeable_disk_space,
707 volume_name,
708 volume_name,
709 purgeable_disk_space,
710 warning_value,
711 critical_value);
712 }
713 /* check MB purgeable space on volume */
714 } else if (vars_to_check==VMP) {
715
716 xasprintf (&send_buffer,"VMP%s\r\n",volume_name);
717 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
718 if (result!=STATE_OK)
719 return result;
720
721 if (!strcmp(recv_buffer,"-1\n")) {
722 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
723 result=STATE_CRITICAL;
724 } else {
725 purgeable_disk_space=strtoul(recv_buffer,NULL,10);
726 if (check_critical_value && purgeable_disk_space >= critical_value)
727 result=STATE_CRITICAL;
728 else if (check_warning_value && purgeable_disk_space >= warning_value)
729 result=STATE_WARNING;
730 xasprintf (&output_message,_("%s%lu MB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"),
731 (result==STATE_OK)?"":_("Only "),
732 purgeable_disk_space,
733 volume_name,
734 volume_name,
735 purgeable_disk_space,
736 warning_value,
737 critical_value);
738 }
739
740 /* check % purgeable space on volume */
741 } else if (vars_to_check==VPP) {
742
743 close(sd);
744 my_tcp_connect (server_address, server_port, &sd);
745
746 xasprintf (&send_buffer,"VKP%s\r\n",volume_name);
747 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
748 if (result!=STATE_OK)
749 return result;
750
751 if (!strcmp(recv_buffer,"-1\n")) {
752
753 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
754 result=STATE_CRITICAL;
755
756 } else {
757
758 purgeable_disk_space=strtoul(recv_buffer,NULL,10);
759
760 close(sd);
761 my_tcp_connect (server_address, server_port, &sd);
762
763 xasprintf (&send_buffer,"VKS%s\r\n",volume_name);
764 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
765 if (result!=STATE_OK)
766 return result;
767 total_disk_space=strtoul(recv_buffer,NULL,10);
768
769 percent_purgeable_space=(unsigned long)(((double)purgeable_disk_space/(double)total_disk_space)*100.0);
770
771 if (check_critical_value && percent_purgeable_space >= critical_value)
772 result=STATE_CRITICAL;
773 else if (check_warning_value && percent_purgeable_space >= warning_value)
774 result=STATE_WARNING;
775 purgeable_disk_space/=1024;
776 xasprintf (&output_message,_("%lu MB (%lu%%) purgeable on volume %s|Purgeable%s=%lu;%lu;%lu;0;100"),
777 purgeable_disk_space,
778 percent_purgeable_space,
779 volume_name,
780 volume_name,
781 percent_purgeable_space,
782 warning_value,
783 critical_value
784 );
785 }
786
787 /* check KB not yet purgeable space on volume */
788 } else if (vars_to_check==VKNP) {
789
790 close(sd);
791 my_tcp_connect (server_address, server_port, &sd);
792
793 xasprintf (&send_buffer,"VKNP%s\r\n",volume_name);
794 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
795 if (result!=STATE_OK)
796 return result;
797
798 if (!strcmp(recv_buffer,"-1\n")) {
799 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
800 result=STATE_CRITICAL;
801 } else {
802 non_purgeable_disk_space=strtoul(recv_buffer,NULL,10);
803 if (check_critical_value && non_purgeable_disk_space >= critical_value)
804 result=STATE_CRITICAL;
805 else if (check_warning_value && non_purgeable_disk_space >= warning_value)
806 result=STATE_WARNING;
807 xasprintf (&output_message,_("%s%lu KB not yet purgeable on volume %s"),(result==STATE_OK)?"":_("Only "),non_purgeable_disk_space,volume_name);
808 }
809
810 /* check % not yet purgeable space on volume */
811 } else if (vars_to_check==VPNP) {
812
813 close(sd);
814 my_tcp_connect (server_address, server_port, &sd);
815
816 xasprintf (&send_buffer,"VKNP%s\r\n",volume_name);
817 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
818 if (result!=STATE_OK)
819 return result;
820
821 if (!strcmp(recv_buffer,"-1\n")) {
822
823 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
824 result=STATE_CRITICAL;
825
826 } else {
827
828 non_purgeable_disk_space=strtoul(recv_buffer,NULL,10);
829
830 close(sd);
831 my_tcp_connect (server_address, server_port, &sd);
832
833 xasprintf (&send_buffer,"VKS%s\r\n",volume_name);
834 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
835 if (result!=STATE_OK)
836 return result;
837 total_disk_space=strtoul(recv_buffer,NULL,10);
838
839 percent_non_purgeable_space=(unsigned long)(((double)non_purgeable_disk_space/(double)total_disk_space)*100.0);
840
841 if (check_critical_value && percent_non_purgeable_space >= critical_value)
842 result=STATE_CRITICAL;
843 else if (check_warning_value && percent_non_purgeable_space >= warning_value)
844 result=STATE_WARNING;
845 purgeable_disk_space/=1024;
846 xasprintf (&output_message,_("%lu MB (%lu%%) not yet purgeable on volume %s"),non_purgeable_disk_space,percent_non_purgeable_space,volume_name);
847 }
848
849 /* check # of open files */
850 } else if (vars_to_check==OFILES) {
851
852 close(sd);
853 my_tcp_connect (server_address, server_port, &sd);
854
855 xasprintf (&send_buffer,"S18\r\n");
856 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
857 if (result!=STATE_OK)
858 return result;
859
860 open_files=atoi(recv_buffer);
861
862 if (check_critical_value && open_files >= critical_value)
863 result=STATE_CRITICAL;
864 else if (check_warning_value && open_files >= warning_value)
865 result=STATE_WARNING;
866
867 xasprintf (&output_message,_("%lu open files|Openfiles=%lu;%lu;%lu;0,0"),
868 open_files,
869 open_files,
870 warning_value,
871 critical_value);
872
873
874 /* check # of abended threads (Netware > 5.x only) */
875 } else if (vars_to_check==ABENDS) {
876
877 close(sd);
878 my_tcp_connect (server_address, server_port, &sd);
879
880 xasprintf (&send_buffer,"S17\r\n");
881 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
882 if (result!=STATE_OK)
883 return result;
884
885 abended_threads=atoi(recv_buffer);
886
887 if (check_critical_value && abended_threads >= critical_value)
888 result=STATE_CRITICAL;
889 else if (check_warning_value && abended_threads >= warning_value)
890 result=STATE_WARNING;
891
892 xasprintf (&output_message,_("%lu abended threads|Abends=%lu;%lu;%lu;;"),
893 abended_threads,
894 abended_threads,
895 warning_value,
896 critical_value);
897
898 /* check # of current service processes (Netware 5.x only) */
899 } else if (vars_to_check==CSPROCS) {
900
901 close(sd);
902 my_tcp_connect (server_address, server_port, &sd);
903
904 xasprintf (&send_buffer,"S20\r\n");
905 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
906 if (result!=STATE_OK)
907 return result;
908
909 max_service_processes=atoi(recv_buffer);
910
911 close(sd);
912 my_tcp_connect (server_address, server_port, &sd);
913
914 xasprintf (&send_buffer,"S21\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 current_service_processes=atoi(recv_buffer);
920
921 if (check_critical_value && current_service_processes >= critical_value)
922 result=STATE_CRITICAL;
923 else if (check_warning_value && current_service_processes >= warning_value)
924 result=STATE_WARNING;
925
926 xasprintf (&output_message,
927 _("%lu current service processes (%lu max)|Processes=%lu;%lu;%lu;0;%lu"),
928 current_service_processes,
929 max_service_processes,
930 current_service_processes,
931 warning_value,
932 critical_value,
933 max_service_processes);
934
935 /* check # Timesync Status */
936 } else if (vars_to_check==TSYNC) {
937
938 close(sd);
939 my_tcp_connect (server_address, server_port, &sd);
940
941 xasprintf (&send_buffer,"S22\r\n");
942 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
943 if (result!=STATE_OK)
944 return result;
945
946 time_sync_status=atoi(recv_buffer);
947
948 if (time_sync_status==0) {
949 result=STATE_CRITICAL;
950 xasprintf (&output_message,_("CRITICAL - Time not in sync with network!"));
951 }
952 else {
953 xasprintf (&output_message,_("OK - Time in sync with network!"));
954 }
955
956
957
958
959
960 /* check LRU sitting time in secondss */
961 } else if (vars_to_check==LRUS) {
962
963 close(sd);
964 my_tcp_connect (server_address, server_port, &sd);
965
966 send_buffer = strdup ("S4\r\n");
967 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
968 if (result!=STATE_OK)
969 return result;
970 lru_time=strtoul(recv_buffer,NULL,10);
971
972 if (check_critical_value && lru_time <= critical_value)
973 result=STATE_CRITICAL;
974 else if (check_warning_value && lru_time <= warning_value)
975 result=STATE_WARNING;
976 xasprintf (&output_message,_("LRU sitting time = %lu seconds"),lru_time);
977
978
979 /* check % dirty cacheobuffers as a percentage of the total*/
980 } else if (vars_to_check==DCB) {
981
982 close(sd);
983 my_tcp_connect (server_address, server_port, &sd);
984
985 send_buffer = strdup ("S6\r\n");
986 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
987 if (result!=STATE_OK)
988 return result;
989 dirty_cache_buffers=atoi(recv_buffer);
990
991 if (check_critical_value && dirty_cache_buffers <= critical_value)
992 result=STATE_CRITICAL;
993 else if (check_warning_value && dirty_cache_buffers <= warning_value)
994 result=STATE_WARNING;
995 xasprintf (&output_message,_("Dirty cache buffers = %lu%% of the total|DCB=%lu;%lu;%lu;0;100"),
996 dirty_cache_buffers,
997 dirty_cache_buffers,
998 warning_value,
999 critical_value);
1000
1001 /* check % total cache buffers as a percentage of the original*/
1002 } else if (vars_to_check==TCB) {
1003
1004 close(sd);
1005 my_tcp_connect (server_address, server_port, &sd);
1006
1007 send_buffer = strdup ("S7\r\n");
1008 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1009 if (result!=STATE_OK)
1010 return result;
1011 total_cache_buffers=atoi(recv_buffer);
1012
1013 if (check_critical_value && total_cache_buffers <= critical_value)
1014 result=STATE_CRITICAL;
1015 else if (check_warning_value && total_cache_buffers <= warning_value)
1016 result=STATE_WARNING;
1017 xasprintf (&output_message,_("Total cache buffers = %lu%% of the original|TCB=%lu;%lu;%lu;0;100"),
1018 total_cache_buffers,
1019 total_cache_buffers,
1020 warning_value,
1021 critical_value);
1022
1023 } else if (vars_to_check==DSVER) {
1024
1025 close(sd);
1026 my_tcp_connect (server_address, server_port, &sd);
1027
1028 xasprintf (&send_buffer,"S13\r\n");
1029 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1030 if (result!=STATE_OK)
1031 return result;
1032
1033 recv_buffer[strlen(recv_buffer)-1]=0;
1034
1035 xasprintf (&output_message,_("NDS Version %s"),recv_buffer);
1036
1037 } else if (vars_to_check==UPTIME) {
1038
1039 close(sd);
1040 my_tcp_connect (server_address, server_port, &sd);
1041
1042 xasprintf (&send_buffer,"UPTIME\r\n");
1043 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1044 if (result!=STATE_OK)
1045 return result;
1046
1047
1048 recv_buffer[sizeof(recv_buffer)-1]=0;
1049 recv_buffer[strlen(recv_buffer)-1]=0;
1050
1051 xasprintf (&output_message,_("Up %s"),recv_buffer);
1052
1053 } else if (vars_to_check==NLM) {
1054
1055 close(sd);
1056 my_tcp_connect (server_address, server_port, &sd);
1057
1058 xasprintf (&send_buffer,"S24:%s\r\n",nlm_name);
1059 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1060 if (result!=STATE_OK)
1061 return result;
1062
1063 recv_buffer[strlen(recv_buffer)-1]=0;
1064 if (strcmp(recv_buffer,"-1")) {
1065 xasprintf (&output_message,_("Module %s version %s is loaded"),nlm_name,recv_buffer);
1066 } else {
1067 result=STATE_CRITICAL;
1068 xasprintf (&output_message,_("Module %s is not loaded"),nlm_name);
1069
1070 }
1071 } else if (vars_to_check==NRMP) {
1072
1073 xasprintf (&send_buffer,"NRMP:%s\r\n",nrmp_name);
1074 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1075 if (result!=STATE_OK)
1076 return result;
1077
1078 if (!strcmp(recv_buffer,"-1\n")) {
1079 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nrmp_name);
1080 result=STATE_CRITICAL;
1081 } else {
1082 nrmp_value=strtoul(recv_buffer,NULL,10);
1083 if (check_critical_value && nrmp_value <= critical_value)
1084 result=STATE_CRITICAL;
1085 else if (check_warning_value && nrmp_value <= warning_value)
1086 result=STATE_WARNING;
1087 xasprintf (&output_message,
1088 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1089 nrmp_name,
1090 nrmp_value,
1091 nrmp_name,
1092 nrmp_value,
1093 warning_value,
1094 critical_value);
1095 }
1096
1097 } else if (vars_to_check==NRMM) {
1098
1099 xasprintf (&send_buffer,"NRMM:%s\r\n",nrmm_name);
1100 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1101 if (result!=STATE_OK)
1102 return result;
1103
1104 if (!strcmp(recv_buffer,"-1\n")) {
1105 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nrmm_name);
1106 result=STATE_CRITICAL;
1107 } else {
1108 nrmm_value=strtoul(recv_buffer,NULL,10);
1109 if (check_critical_value && nrmm_value <= critical_value)
1110 result=STATE_CRITICAL;
1111 else if (check_warning_value && nrmm_value <= warning_value)
1112 result=STATE_WARNING;
1113 xasprintf (&output_message,
1114 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1115 nrmm_name,
1116 nrmm_value,
1117 nrmm_name,
1118 nrmm_value,
1119 warning_value,
1120 critical_value);
1121 }
1122
1123 } else if (vars_to_check==NRMS) {
1124
1125 xasprintf (&send_buffer,"NRMS:%s\r\n",nrms_name);
1126 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1127 if (result!=STATE_OK)
1128 return result;
1129
1130 if (!strcmp(recv_buffer,"-1\n")) {
1131 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nrms_name);
1132 result=STATE_CRITICAL;
1133 } else {
1134 nrms_value=strtoul(recv_buffer,NULL,10);
1135 if (check_critical_value && nrms_value >= critical_value)
1136 result=STATE_CRITICAL;
1137 else if (check_warning_value && nrms_value >= warning_value)
1138 result=STATE_WARNING;
1139 xasprintf (&output_message,
1140 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1141 nrms_name,
1142 nrms_value,
1143 nrms_name,
1144 nrms_value,
1145 warning_value,
1146 critical_value);
1147 }
1148
1149 } else if (vars_to_check==NSS1) {
1150
1151 xasprintf (&send_buffer,"NSS1:%s\r\n",nss1_name);
1152 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1153 if (result!=STATE_OK)
1154 return result;
1155
1156 if (!strcmp(recv_buffer,"-1\n")) {
1157 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss1_name);
1158 result=STATE_CRITICAL;
1159 } else {
1160 nss1_value=strtoul(recv_buffer,NULL,10);
1161 if (check_critical_value && nss1_value >= critical_value)
1162 result=STATE_CRITICAL;
1163 else if (check_warning_value && nss1_value >= warning_value)
1164 result=STATE_WARNING;
1165 xasprintf (&output_message,
1166 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1167 nss1_name,
1168 nss1_value,
1169 nss1_name,
1170 nss1_value,
1171 warning_value,
1172 critical_value);
1173 }
1174
1175 } else if (vars_to_check==NSS2) {
1176
1177 xasprintf (&send_buffer,"NSS2:%s\r\n",nss2_name);
1178 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1179 if (result!=STATE_OK)
1180 return result;
1181
1182 if (!strcmp(recv_buffer,"-1\n")) {
1183 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss2_name);
1184 result=STATE_CRITICAL;
1185 } else {
1186 nss2_value=strtoul(recv_buffer,NULL,10);
1187 if (check_critical_value && nss2_value >= critical_value)
1188 result=STATE_CRITICAL;
1189 else if (check_warning_value && nss2_value >= warning_value)
1190 result=STATE_WARNING;
1191 xasprintf (&output_message,
1192 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1193 nss2_name,
1194 nss2_value,
1195 nss2_name,
1196 nss2_value,
1197 warning_value,
1198 critical_value);
1199 }
1200
1201 } else if (vars_to_check==NSS3) {
1202
1203 xasprintf (&send_buffer,"NSS3:%s\r\n",nss3_name);
1204 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1205 if (result!=STATE_OK)
1206 return result;
1207
1208 if (!strcmp(recv_buffer,"-1\n")) {
1209 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss3_name);
1210 result=STATE_CRITICAL;
1211 } else {
1212 nss3_value=strtoul(recv_buffer,NULL,10);
1213 if (check_critical_value && nss3_value >= critical_value)
1214 result=STATE_CRITICAL;
1215 else if (check_warning_value && nss3_value >= warning_value)
1216 result=STATE_WARNING;
1217 xasprintf (&output_message,
1218 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1219 nss3_name,
1220 nss3_value,
1221 nss3_name,
1222 nss3_value,
1223 warning_value,
1224 critical_value);
1225 }
1226
1227 } else if (vars_to_check==NSS4) {
1228
1229 xasprintf (&send_buffer,"NSS4:%s\r\n",nss4_name);
1230 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1231 if (result!=STATE_OK)
1232 return result;
1233
1234 if (!strcmp(recv_buffer,"-1\n")) {
1235 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss4_name);
1236 result=STATE_CRITICAL;
1237 } else {
1238 nss4_value=strtoul(recv_buffer,NULL,10);
1239 if (check_critical_value && nss4_value >= critical_value)
1240 result=STATE_CRITICAL;
1241 else if (check_warning_value && nss4_value >= warning_value)
1242 result=STATE_WARNING;
1243 xasprintf (&output_message,
1244 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1245 nss4_name,
1246 nss4_value,
1247 nss4_name,
1248 nss4_value,
1249 warning_value,
1250 critical_value);
1251 }
1252
1253 } else if (vars_to_check==NSS5) {
1254
1255 xasprintf (&send_buffer,"NSS5:%s\r\n",nss5_name);
1256 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1257 if (result!=STATE_OK)
1258 return result;
1259
1260 if (!strcmp(recv_buffer,"-1\n")) {
1261 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss5_name);
1262 result=STATE_CRITICAL;
1263 } else {
1264 nss5_value=strtoul(recv_buffer,NULL,10);
1265 if (check_critical_value && nss5_value >= critical_value)
1266 result=STATE_CRITICAL;
1267 else if (check_warning_value && nss5_value >= warning_value)
1268 result=STATE_WARNING;
1269 xasprintf (&output_message,
1270 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1271 nss5_name,
1272 nss5_value,
1273 nss5_name,
1274 nss5_value,
1275 warning_value,
1276 critical_value);
1277 }
1278
1279 } else if (vars_to_check==NSS6) {
1280
1281 xasprintf (&send_buffer,"NSS6:%s\r\n",nss6_name);
1282 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1283 if (result!=STATE_OK)
1284 return result;
1285
1286 if (!strcmp(recv_buffer,"-1\n")) {
1287 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss6_name);
1288 result=STATE_CRITICAL;
1289 } else {
1290 nss6_value=strtoul(recv_buffer,NULL,10);
1291 if (check_critical_value && nss6_value >= critical_value)
1292 result=STATE_CRITICAL;
1293 else if (check_warning_value && nss6_value >= warning_value)
1294 result=STATE_WARNING;
1295 xasprintf (&output_message,
1296 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1297 nss6_name,
1298 nss6_value,
1299 nss6_name,
1300 nss6_value,
1301 warning_value,
1302 critical_value);
1303 }
1304
1305 } else if (vars_to_check==NSS7) {
1306
1307 xasprintf (&send_buffer,"NSS7:%s\r\n",nss7_name);
1308 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1309 if (result!=STATE_OK)
1310 return result;
1311
1312 if (!strcmp(recv_buffer,"-1\n")) {
1313 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss7_name);
1314 result=STATE_CRITICAL;
1315 } else {
1316 nss7_value=strtoul(recv_buffer,NULL,10);
1317 if (check_critical_value && nss7_value >= critical_value)
1318 result=STATE_CRITICAL;
1319 else if (check_warning_value && nss7_value >= warning_value)
1320 result=STATE_WARNING;
1321 xasprintf (&output_message,
1322 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1323 nss7_name,
1324 nss7_value,
1325 nss7_name,
1326 nss7_value,
1327 warning_value,
1328 critical_value);
1329 }
1330
1331
1332}
1333 else {
1334
1335 output_message = strdup (_("Nothing to check!\n"));
1336 result=STATE_UNKNOWN;
1337
1338 }
1339
1340 close (sd);
1341
1342 /* reset timeout */
1343 alarm(0);
1344
1345 printf("%s%s\n",netware_version,output_message);
1346
1347 return result;
1348}
1349
1350
1351
1352/* process command-line arguments */
1353int process_arguments(int argc, char **argv) {
1354 int c;
1355
1356 int option = 0;
1357 static struct option longopts[] =
1358 {
1359 {"port", required_argument,0,'p'},
1360 {"timeout", required_argument,0,'t'},
1361 {"critical", required_argument,0,'c'},
1362 {"warning", required_argument,0,'w'},
1363 {"variable", required_argument,0,'v'},
1364 {"hostname", required_argument,0,'H'},
1365 {"osversion",no_argument, 0,'o'},
1366 {"version", no_argument, 0,'V'},
1367 {"help", no_argument, 0,'h'},
1368 {0,0,0,0}
1369 };
1370
1371 /* no options were supplied */
1372 if (argc<2) return ERROR;
1373
1374 /* backwards compatibility */
1375 if (! is_option(argv[1])) {
1376 server_address=argv[1];
1377 argv[1]=argv[0];
1378 argv=&argv[1];
1379 argc--;
1380 }
1381
1382 for (c=1;c<argc;c++) {
1383 if (strcmp("-to",argv[c])==0)
1384 strcpy(argv[c],"-t");
1385 else if (strcmp("-wv",argv[c])==0)
1386 strcpy(argv[c],"-w");
1387 else if (strcmp("-cv",argv[c])==0)
1388 strcpy(argv[c],"-c");
1389 }
1390
1391 while (1) {
1392 c = getopt_long(argc,argv,"+hoVH:t:c:w:p:v:",longopts,&option);
1393
1394 if (c==-1||c==EOF||c==1)
1395 break;
1396
1397 switch (c)
1398 {
1399 case '?': /* print short usage statement if args not parsable */
1400 usage5 ();
1401 case 'h': /* help */
1402 print_help();
1403 exit(STATE_UNKNOWN);
1404 case 'V': /* version */
1405 print_revision(progname, NP_VERSION);
1406 exit(STATE_UNKNOWN);
1407 case 'H': /* hostname */
1408 server_address=optarg;
1409 break;
1410 case 'o': /* display nos version */
1411 check_netware_version = true;
1412 break;
1413 case 'p': /* port */
1414 if (is_intnonneg(optarg))
1415 server_port=atoi(optarg);
1416 else
1417 die(STATE_UNKNOWN,_("Server port an integer\n"));
1418 break;
1419 case 'v':
1420 if (strlen(optarg)<3)
1421 return ERROR;
1422 if (!strcmp(optarg,"LOAD1"))
1423 vars_to_check=LOAD1;
1424 else if (!strcmp(optarg,"LOAD5"))
1425 vars_to_check=LOAD5;
1426 else if (!strcmp(optarg,"LOAD15"))
1427 vars_to_check=LOAD15;
1428 else if (!strcmp(optarg,"CONNS"))
1429 vars_to_check=CONNS;
1430 else if (!strcmp(optarg,"LTCH"))
1431 vars_to_check=LTCH;
1432 else if (!strcmp(optarg,"DCB"))
1433 vars_to_check=DCB;
1434 else if (!strcmp(optarg,"TCB"))
1435 vars_to_check=TCB;
1436 else if (!strcmp(optarg,"CBUFF"))
1437 vars_to_check=CBUFF;
1438 else if (!strcmp(optarg,"CDBUFF"))
1439 vars_to_check=CDBUFF;
1440 else if (!strcmp(optarg,"LRUM"))
1441 vars_to_check=LRUM;
1442 else if (!strcmp(optarg,"LRUS"))
1443 vars_to_check=LRUS;
1444 else if (strncmp(optarg,"VPF",3)==0) {
1445 vars_to_check=VPF;
1446 volume_name = strdup (optarg+3);
1447 if (!strcmp(volume_name,""))
1448 volume_name = strdup ("SYS");
1449 }
1450 else if (strncmp(optarg,"VKF",3)==0) {
1451 vars_to_check=VKF;
1452 volume_name = strdup (optarg+3);
1453 if (!strcmp(volume_name,""))
1454 volume_name = strdup ("SYS");
1455 }
1456 else if (strncmp(optarg,"VMF",3)==0) {
1457 vars_to_check=VMF;
1458 volume_name = strdup (optarg+3);
1459 if (!strcmp(volume_name,""))
1460 volume_name = strdup ("SYS");
1461 }
1462 else if (!strcmp(optarg,"DSDB"))
1463 vars_to_check=DSDB;
1464 else if (!strcmp(optarg,"LOGINS"))
1465 vars_to_check=LOGINS;
1466 else if (!strcmp(optarg,"NRMH"))
1467 vars_to_check=NRMH;
1468 else if (!strcmp(optarg,"UPRB"))
1469 vars_to_check=UPRB;
1470 else if (!strcmp(optarg,"PUPRB"))
1471 vars_to_check=PUPRB;
1472 else if (!strncmp(optarg,"SAPENTRIES",10)) {
1473 vars_to_check=SAPENTRIES;
1474 if (strlen(optarg)>10)
1475 sap_number=atoi(optarg+10);
1476 else
1477 sap_number=-1;
1478 }
1479 else if (!strcmp(optarg,"OFILES"))
1480 vars_to_check=OFILES;
1481 else if (strncmp(optarg,"VKP",3)==0) {
1482 vars_to_check=VKP;
1483 volume_name = strdup (optarg+3);
1484 if (!strcmp(volume_name,""))
1485 volume_name = strdup ("SYS");
1486 }
1487 else if (strncmp(optarg,"VMP",3)==0) {
1488 vars_to_check=VMP;
1489 volume_name = strdup (optarg+3);
1490 if (!strcmp(volume_name,""))
1491 volume_name = strdup ("SYS");
1492 }
1493 else if (strncmp(optarg,"VMU",3)==0) {
1494 vars_to_check=VMU;
1495 volume_name = strdup (optarg+3);
1496 if (!strcmp(volume_name,""))
1497 volume_name = strdup ("SYS");
1498 }
1499 else if (strncmp(optarg,"VPU",3)==0) {
1500 vars_to_check=VPU;
1501 volume_name = strdup (optarg+3);
1502 if (!strcmp(volume_name,""))
1503 volume_name = strdup ("SYS");
1504 }
1505 else if (strncmp(optarg,"VPP",3)==0) {
1506 vars_to_check=VPP;
1507 volume_name = strdup (optarg+3);
1508 if (!strcmp(volume_name,""))
1509 volume_name = strdup ("SYS");
1510 }
1511 else if (strncmp(optarg,"VKNP",4)==0) {
1512 vars_to_check=VKNP;
1513 volume_name = strdup (optarg+4);
1514 if (!strcmp(volume_name,""))
1515 volume_name = strdup ("SYS");
1516 }
1517 else if (strncmp(optarg,"VPNP",4)==0) {
1518 vars_to_check=VPNP;
1519 volume_name = strdup (optarg+4);
1520 if (!strcmp(volume_name,""))
1521 volume_name = strdup("SYS");
1522 }
1523 else if (!strcmp(optarg,"ABENDS"))
1524 vars_to_check=ABENDS;
1525 else if (!strcmp(optarg,"CSPROCS"))
1526 vars_to_check=CSPROCS;
1527 else if (!strcmp(optarg,"TSYNC"))
1528 vars_to_check=TSYNC;
1529 else if (!strcmp(optarg,"DSVER"))
1530 vars_to_check=DSVER;
1531 else if (!strcmp(optarg,"UPTIME")) {
1532 vars_to_check=UPTIME;
1533 }
1534 else if (strncmp(optarg,"NLM:",4)==0) {
1535 vars_to_check=NLM;
1536 nlm_name=strdup (optarg+4);
1537 }
1538 else if (strncmp(optarg,"NRMP",4)==0) {
1539 vars_to_check=NRMP;
1540 nrmp_name = strdup (optarg+4);
1541 if (!strcmp(nrmp_name,""))
1542 nrmp_name = strdup ("AVAILABLE_MEMORY");
1543 }
1544 else if (strncmp(optarg,"NRMM",4)==0) {
1545 vars_to_check=NRMM;
1546 nrmm_name = strdup (optarg+4);
1547 if (!strcmp(nrmm_name,""))
1548 nrmm_name = strdup ("AVAILABLE_CACHE_MEMORY");
1549
1550 }
1551
1552 else if (strncmp(optarg,"NRMS",4)==0) {
1553 vars_to_check=NRMS;
1554 nrms_name = strdup (optarg+4);
1555 if (!strcmp(nrms_name,""))
1556 nrms_name = strdup ("USED_SWAP_SPACE");
1557
1558 }
1559
1560 else if (strncmp(optarg,"NSS1",4)==0) {
1561 vars_to_check=NSS1;
1562 nss1_name = strdup (optarg+4);
1563 if (!strcmp(nss1_name,""))
1564 nss1_name = strdup ("CURRENTBUFFERCACHESIZE");
1565
1566 }
1567
1568 else if (strncmp(optarg,"NSS2",4)==0) {
1569 vars_to_check=NSS2;
1570 nss2_name = strdup (optarg+4);
1571 if (!strcmp(nss2_name,""))
1572 nss2_name = strdup ("CACHEHITS");
1573
1574 }
1575
1576 else if (strncmp(optarg,"NSS3",4)==0) {
1577 vars_to_check=NSS3;
1578 nss3_name = strdup (optarg+4);
1579 if (!strcmp(nss3_name,""))
1580 nss3_name = strdup ("CACHEGITPERCENT");
1581
1582 }
1583
1584 else if (strncmp(optarg,"NSS4",4)==0) {
1585 vars_to_check=NSS4;
1586 nss4_name = strdup (optarg+4);
1587 if (!strcmp(nss4_name,""))
1588 nss4_name = strdup ("CURRENTOPENCOUNT");
1589
1590 }
1591
1592 else if (strncmp(optarg,"NSS5",4)==0) {
1593 vars_to_check=NSS5;
1594 nss5_name = strdup (optarg+4);
1595 if (!strcmp(nss5_name,""))
1596 nss5_name = strdup ("CACHEMISSES");
1597
1598 }
1599
1600
1601 else if (strncmp(optarg,"NSS6",4)==0) {
1602 vars_to_check=NSS6;
1603 nss6_name = strdup (optarg+4);
1604 if (!strcmp(nss6_name,""))
1605 nss6_name = strdup ("PENDINGWORKSCOUNT");
1606
1607 }
1608
1609
1610 else if (strncmp(optarg,"NSS7",4)==0) {
1611 vars_to_check=NSS7;
1612 nss7_name = strdup (optarg+4);
1613 if (!strcmp(nss7_name,""))
1614 nss7_name = strdup ("CACHESIZE");
1615
1616 }
1617
1618
1619 else
1620 return ERROR;
1621 break;
1622 case 'w': /* warning threshold */
1623 warning_value=strtoul(optarg,NULL,10);
1624 check_warning_value = true;
1625 break;
1626 case 'c': /* critical threshold */
1627 critical_value=strtoul(optarg,NULL,10);
1628 check_critical_value = true;
1629 break;
1630 case 't': /* timeout */
1631 socket_timeout=atoi(optarg);
1632 if (socket_timeout<=0)
1633 return ERROR;
1634 }
1635
1636 }
1637
1638 return OK;
1639}
1640
1641
1642
1643void print_help(void)
1644{
1645 char *myport;
1646 xasprintf (&myport, "%d", PORT);
1647
1648 print_revision (progname, NP_VERSION);
1649
1650 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1651 printf (COPYRIGHT, copyright, email);
1652
1653 printf ("%s\n", _("This plugin attempts to contact the MRTGEXT NLM running on a"));
1654 printf ("%s\n", _("Novell server to gather the requested system information."));
1655
1656 printf ("\n\n");
1657
1658 print_usage();
1659
1660 printf (UT_HELP_VRSN);
1661 printf (UT_EXTRA_OPTS);
1662
1663 printf (UT_HOST_PORT, 'p', myport);
1664
1665 printf (" %s\n", "-v, --variable=STRING");
1666 printf (" %s\n", _("Variable to check. Valid variables include:"));
1667 printf (" %s\n", _("LOAD1 = 1 minute average CPU load"));
1668 printf (" %s\n", _("LOAD5 = 5 minute average CPU load"));
1669 printf (" %s\n", _("LOAD15 = 15 minute average CPU load"));
1670 printf (" %s\n", _("CSPROCS = number of current service processes (NW 5.x only)"));
1671 printf (" %s\n", _("ABENDS = number of abended threads (NW 5.x only)"));
1672 printf (" %s\n", _("UPTIME = server uptime"));
1673 printf (" %s\n", _("LTCH = percent long term cache hits"));
1674 printf (" %s\n", _("CBUFF = current number of cache buffers"));
1675 printf (" %s\n", _("CDBUFF = current number of dirty cache buffers"));
1676 printf (" %s\n", _("DCB = dirty cache buffers as a percentage of the total"));
1677 printf (" %s\n", _("TCB = dirty cache buffers as a percentage of the original"));
1678 printf (" %s\n", _("OFILES = number of open files"));
1679 printf (" %s\n", _(" VMF<vol> = MB of free space on Volume <vol>"));
1680 printf (" %s\n", _(" VMU<vol> = MB used space on Volume <vol>"));
1681 printf (" %s\n", _(" VPU<vol> = percent used space on Volume <vol>"));
1682 printf (" %s\n", _(" VMP<vol> = MB of purgeable space on Volume <vol>"));
1683 printf (" %s\n", _(" VPF<vol> = percent free space on volume <vol>"));
1684 printf (" %s\n", _(" VKF<vol> = KB of free space on volume <vol>"));
1685 printf (" %s\n", _(" VPP<vol> = percent purgeable space on volume <vol>"));
1686 printf (" %s\n", _(" VKP<vol> = KB of purgeable space on volume <vol>"));
1687 printf (" %s\n", _(" VPNP<vol> = percent not yet purgeable space on volume <vol>"));
1688 printf (" %s\n", _(" VKNP<vol> = KB of not yet purgeable space on volume <vol>"));
1689 printf (" %s\n", _(" LRUM = LRU sitting time in minutes"));
1690 printf (" %s\n", _(" LRUS = LRU sitting time in seconds"));
1691 printf (" %s\n", _(" DSDB = check to see if DS Database is open"));
1692 printf (" %s\n", _(" DSVER = NDS version"));
1693 printf (" %s\n", _(" UPRB = used packet receive buffers"));
1694 printf (" %s\n", _(" PUPRB = percent (of max) used packet receive buffers"));
1695 printf (" %s\n", _(" SAPENTRIES = number of entries in the SAP table"));
1696 printf (" %s\n", _(" SAPENTRIES<n> = number of entries in the SAP table for SAP type <n>"));
1697 printf (" %s\n", _(" TSYNC = timesync status"));
1698 printf (" %s\n", _(" LOGINS = check to see if logins are enabled"));
1699 printf (" %s\n", _(" CONNS = number of currently licensed connections"));
1700 printf (" %s\n", _(" NRMH = NRM Summary Status"));
1701 printf (" %s\n", _(" NRMP<stat> = Returns the current value for a NRM health item"));
1702 printf (" %s\n", _(" NRMM<stat> = Returns the current memory stats from NRM"));
1703 printf (" %s\n", _(" NRMS<stat> = Returns the current Swapfile stats from NRM"));
1704 printf (" %s\n", _(" NSS1<stat> = Statistics from _Admin:Manage_NSS\\GeneralStats.xml"));
1705 printf (" %s\n", _(" NSS3<stat> = Statistics from _Admin:Manage_NSS\\NameCache.xml"));
1706 printf (" %s\n", _(" NSS4<stat> = Statistics from _Admin:Manage_NSS\\FileStats.xml"));
1707 printf (" %s\n", _(" NSS5<stat> = Statistics from _Admin:Manage_NSS\\ObjectCache.xml"));
1708 printf (" %s\n", _(" NSS6<stat> = Statistics from _Admin:Manage_NSS\\Thread.xml"));
1709 printf (" %s\n", _(" NSS7<stat> = Statistics from _Admin:Manage_NSS\\AuthorizationCache.xml"));
1710 printf (" %s\n", _(" NLM:<nlm> = check if NLM is loaded and report version"));
1711 printf (" %s\n", _(" (e.g. NLM:TSANDS.NLM)"));
1712 printf ("\n");
1713 printf (" %s\n", "-w, --warning=INTEGER");
1714 printf (" %s\n", _("Threshold which will result in a warning status"));
1715 printf (" %s\n", "-c, --critical=INTEGER");
1716 printf (" %s\n", _("Threshold which will result in a critical status"));
1717 printf (" %s\n", "-o, --osversion");
1718 printf (" %s\n", _("Include server version string in results"));
1719
1720 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1721
1722 printf ("\n");
1723 printf ("%s\n", _("Notes:"));
1724 printf (" %s\n", _("- This plugin requires that the MRTGEXT.NLM file from James Drews' MRTG"));
1725 printf (" %s\n", _(" extension for NetWare be loaded on the Novell servers you wish to check."));
1726 printf (" %s\n", _(" (available from http://www.engr.wisc.edu/~drews/mrtg/)"));
1727 printf (" %s\n", _("- Values for critical thresholds should be lower than warning thresholds"));
1728 printf (" %s\n", _(" when the following variables are checked: VPF, VKF, LTCH, CBUFF, DCB, "));
1729 printf (" %s\n", _(" TCB, LRUS and LRUM."));
1730
1731 printf (UT_SUPPORT);
1732}
1733
1734
1735
1736void print_usage(void)
1737{
1738 printf ("%s\n", _("Usage:"));
1739 printf ("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n",progname);
1740}
diff --git a/plugins/check_overcr.c b/plugins/check_overcr.c
deleted file mode 100644
index 5165c828..00000000
--- a/plugins/check_overcr.c
+++ /dev/null
@@ -1,469 +0,0 @@
1/*****************************************************************************
2*
3* Monitoring check_overcr plugin
4*
5* License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team
7*
8* Description:
9*
10* This file contains the check_overcr plugin
11*
12* This plugin attempts to contact the Over-CR collector daemon running on the
13* remote UNIX server in order 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_overcr";
33const char *copyright = "2000-2007";
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,
43 LOAD5,
44 LOAD15,
45 DPU,
46 PROCS,
47 NETSTAT,
48 UPTIME
49};
50
51enum {
52 PORT = 2000
53};
54
55char *server_address = NULL;
56int server_port = PORT;
57double warning_value = 0L;
58double critical_value = 0L;
59bool check_warning_value = false;
60bool check_critical_value = false;
61enum checkvar vars_to_check = NONE;
62int cmd_timeout = 1;
63
64int netstat_port = 0;
65char *disk_name = NULL;
66char *process_name = NULL;
67char send_buffer[MAX_INPUT_BUFFER];
68
69int process_arguments (int, char **);
70void print_usage (void);
71void print_help (void);
72
73int
74main (int argc, char **argv)
75{
76 int result = STATE_UNKNOWN;
77 char recv_buffer[MAX_INPUT_BUFFER];
78 char temp_buffer[MAX_INPUT_BUFFER];
79 char *temp_ptr = NULL;
80 bool found_disk = false;
81 unsigned long percent_used_disk_space = 100;
82 double load;
83 double load_1min;
84 double load_5min;
85 double load_15min;
86 int port_connections = 0;
87 int processes = 0;
88 double uptime_raw_hours;
89 int uptime_raw_minutes = 0;
90 int uptime_days = 0;
91 int uptime_hours = 0;
92 int uptime_minutes = 0;
93
94 setlocale (LC_ALL, "");
95 bindtextdomain (PACKAGE, LOCALEDIR);
96 textdomain (PACKAGE);
97
98 /* Parse extra opts if any */
99 argv=np_extra_opts (&argc, argv, progname);
100
101 if (process_arguments (argc, argv) == ERROR)
102 usage4 (_("Could not parse arguments"));
103
104 /* initialize alarm signal handling */
105 signal (SIGALRM, socket_timeout_alarm_handler);
106
107 /* set socket timeout */
108 alarm (socket_timeout);
109
110 result = process_tcp_request2 (server_address,
111 server_port,
112 send_buffer,
113 recv_buffer,
114 sizeof (recv_buffer));
115
116 switch (vars_to_check) {
117
118 case LOAD1:
119 case LOAD5:
120 case LOAD15:
121
122 if (result != STATE_OK)
123 die (result, _("Unknown error fetching load data\n"));
124
125 temp_ptr = (char *) strtok (recv_buffer, "\r\n");
126 if (temp_ptr == NULL)
127 die (STATE_CRITICAL, _("Invalid response from server - no load information\n"));
128 else
129 load_1min = strtod (temp_ptr, NULL);
130
131 temp_ptr = (char *) strtok (NULL, "\r\n");
132 if (temp_ptr == NULL)
133 die (STATE_CRITICAL, _("Invalid response from server after load 1\n"));
134 else
135 load_5min = strtod (temp_ptr, NULL);
136
137 temp_ptr = (char *) strtok (NULL, "\r\n");
138 if (temp_ptr == NULL)
139 die (STATE_CRITICAL, _("Invalid response from server after load 5\n"));
140 else
141 load_15min = strtod (temp_ptr, NULL);
142
143 switch (vars_to_check) {
144 case LOAD1:
145 strcpy (temp_buffer, "1");
146 load = load_1min;
147 break;
148 case LOAD5:
149 strcpy (temp_buffer, "5");
150 load = load_5min;
151 break;
152 default:
153 strcpy (temp_buffer, "15");
154 load = load_15min;
155 break;
156 }
157
158 if (check_critical_value && (load >= critical_value))
159 result = STATE_CRITICAL;
160 else if (check_warning_value && (load >= warning_value))
161 result = STATE_WARNING;
162
163 die (result,
164 _("Load %s - %s-min load average = %0.2f"),
165 state_text(result),
166 temp_buffer,
167 load);
168
169 break;
170
171 case DPU:
172
173 if (result != STATE_OK)
174 die (result, _("Unknown error fetching disk data\n"));
175
176 for (temp_ptr = (char *) strtok (recv_buffer, " ");
177 temp_ptr != NULL;
178 temp_ptr = (char *) strtok (NULL, " ")) {
179
180 if (!strcmp (temp_ptr, disk_name)) {
181 found_disk = true;
182 temp_ptr = (char *) strtok (NULL, "%");
183 if (temp_ptr == NULL)
184 die (STATE_CRITICAL, _("Invalid response from server\n"));
185 else
186 percent_used_disk_space = strtoul (temp_ptr, NULL, 10);
187 break;
188 }
189
190 temp_ptr = (char *) strtok (NULL, "\r\n");
191 }
192
193 /* error if we couldn't find the info for the disk */
194 if (!found_disk)
195 die (STATE_CRITICAL,
196 "CRITICAL - Disk '%s' non-existent or not mounted",
197 disk_name);
198
199 if (check_critical_value && (percent_used_disk_space >= critical_value))
200 result = STATE_CRITICAL;
201 else if (check_warning_value && (percent_used_disk_space >= warning_value))
202 result = STATE_WARNING;
203
204 die (result, "Disk %s - %lu%% used on %s", state_text(result), percent_used_disk_space, disk_name);
205
206 break;
207
208 case NETSTAT:
209
210 if (result != STATE_OK)
211 die (result, _("Unknown error fetching network status\n"));
212 else
213 port_connections = strtod (recv_buffer, NULL);
214
215 if (check_critical_value && (port_connections >= critical_value))
216 result = STATE_CRITICAL;
217 else if (check_warning_value && (port_connections >= warning_value))
218 result = STATE_WARNING;
219
220 die (result,
221 _("Net %s - %d connection%s on port %d"),
222 state_text(result),
223 port_connections,
224 (port_connections == 1) ? "" : "s",
225 netstat_port);
226
227 break;
228
229 case PROCS:
230
231 if (result != STATE_OK)
232 die (result, _("Unknown error fetching process status\n"));
233
234 temp_ptr = (char *) strtok (recv_buffer, "(");
235 if (temp_ptr == NULL)
236 die (STATE_CRITICAL, _("Invalid response from server\n"));
237
238 temp_ptr = (char *) strtok (NULL, ")");
239 if (temp_ptr == NULL)
240 die (STATE_CRITICAL, _("Invalid response from server\n"));
241 else
242 processes = strtod (temp_ptr, NULL);
243
244 if (check_critical_value && (processes >= critical_value))
245 result = STATE_CRITICAL;
246 else if (check_warning_value && (processes >= warning_value))
247 result = STATE_WARNING;
248
249 die (result,
250 _("Process %s - %d instance%s of %s running"),
251 state_text(result),
252 processes,
253 (processes == 1) ? "" : "s",
254 process_name);
255 break;
256
257 case UPTIME:
258
259 if (result != STATE_OK)
260 return result;
261
262 uptime_raw_hours = strtod (recv_buffer, NULL);
263 uptime_raw_minutes = (unsigned long) (uptime_raw_hours * 60.0);
264
265 if (check_critical_value && (uptime_raw_minutes <= critical_value))
266 result = STATE_CRITICAL;
267 else if (check_warning_value && (uptime_raw_minutes <= warning_value))
268 result = STATE_WARNING;
269
270 uptime_days = uptime_raw_minutes / 1440;
271 uptime_raw_minutes %= 1440;
272 uptime_hours = uptime_raw_minutes / 60;
273 uptime_raw_minutes %= 60;
274 uptime_minutes = uptime_raw_minutes;
275
276 die (result,
277 _("Uptime %s - Up %d days %d hours %d minutes"),
278 state_text(result),
279 uptime_days,
280 uptime_hours,
281 uptime_minutes);
282 break;
283
284 default:
285 die (STATE_UNKNOWN, _("Nothing to check!\n"));
286 break;
287 }
288}
289
290
291/* process command-line arguments */
292int
293process_arguments (int argc, char **argv)
294{
295 int c;
296
297 int option = 0;
298 static struct option longopts[] = {
299 {"port", required_argument, 0, 'p'},
300 {"timeout", required_argument, 0, 't'},
301 {"critical", required_argument, 0, 'c'},
302 {"warning", required_argument, 0, 'w'},
303 {"variable", required_argument, 0, 'v'},
304 {"hostname", required_argument, 0, 'H'},
305 {"version", no_argument, 0, 'V'},
306 {"help", no_argument, 0, 'h'},
307 {0, 0, 0, 0}
308 };
309
310 /* no options were supplied */
311 if (argc < 2)
312 return ERROR;
313
314 /* backwards compatibility */
315 if (!is_option (argv[1])) {
316 server_address = argv[1];
317 argv[1] = argv[0];
318 argv = &argv[1];
319 argc--;
320 }
321
322 for (c = 1; c < argc; c++) {
323 if (strcmp ("-to", argv[c]) == 0)
324 strcpy (argv[c], "-t");
325 else if (strcmp ("-wv", argv[c]) == 0)
326 strcpy (argv[c], "-w");
327 else if (strcmp ("-cv", argv[c]) == 0)
328 strcpy (argv[c], "-c");
329 }
330
331 while (1) {
332 c = getopt_long (argc, argv, "+hVH:t:c:w:p:v:", longopts,
333 &option);
334
335 if (c == -1 || c == EOF || c == 1)
336 break;
337
338 switch (c) {
339 case '?': /* print short usage statement if args not parsable */
340 usage5 ();
341 case 'h': /* help */
342 print_help ();
343 exit (STATE_UNKNOWN);
344 case 'V': /* version */
345 print_revision (progname, NP_VERSION);
346 exit (STATE_UNKNOWN);
347 case 'H': /* hostname */
348 server_address = optarg;
349 break;
350 case 'p': /* port */
351 if (is_intnonneg (optarg))
352 server_port = atoi (optarg);
353 else
354 die (STATE_UNKNOWN,
355 _("Server port an integer\n"));
356 break;
357 case 'v': /* variable */
358 if (strcmp (optarg, "LOAD") == 0) {
359 strcpy (send_buffer, "LOAD\r\nQUIT\r\n");
360 if (strcmp (optarg, "LOAD1") == 0)
361 vars_to_check = LOAD1;
362 else if (strcmp (optarg, "LOAD5") == 0)
363 vars_to_check = LOAD5;
364 else if (strcmp (optarg, "LOAD15") == 0)
365 vars_to_check = LOAD15;
366 }
367 else if (strcmp (optarg, "UPTIME") == 0) {
368 vars_to_check = UPTIME;
369 strcpy (send_buffer, "UPTIME\r\n");
370 }
371 else if (strstr (optarg, "PROC") == optarg) {
372 vars_to_check = PROCS;
373 process_name = strscpy (process_name, optarg + 4);
374 sprintf (send_buffer, "PROCESS %s\r\n", process_name);
375 }
376 else if (strstr (optarg, "NET") == optarg) {
377 vars_to_check = NETSTAT;
378 netstat_port = atoi (optarg + 3);
379 sprintf (send_buffer, "NETSTAT %d\r\n", netstat_port);
380 }
381 else if (strstr (optarg, "DPU") == optarg) {
382 vars_to_check = DPU;
383 strcpy (send_buffer, "DISKSPACE\r\n");
384 disk_name = strscpy (disk_name, optarg + 3);
385 }
386 else
387 return ERROR;
388 break;
389 case 'w': /* warning threshold */
390 warning_value = strtoul (optarg, NULL, 10);
391 check_warning_value = true;
392 break;
393 case 'c': /* critical threshold */
394 critical_value = strtoul (optarg, NULL, 10);
395 check_critical_value = true;
396 break;
397 case 't': /* timeout */
398 socket_timeout = atoi (optarg);
399 if (socket_timeout <= 0)
400 return ERROR;
401 }
402
403 }
404 return OK;
405}
406
407
408void
409print_help (void)
410{
411 char *myport;
412 xasprintf (&myport, "%d", PORT);
413
414 print_revision (progname, NP_VERSION);
415
416 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
417 printf (COPYRIGHT, copyright, email);
418
419 printf ("%s\n", _("This plugin attempts to contact the Over-CR collector daemon running on the"));
420 printf ("%s\n", _("remote UNIX server in order to gather the requested system information."));
421
422 printf ("\n\n");
423
424 print_usage ();
425
426 printf (UT_HELP_VRSN);
427 printf (UT_EXTRA_OPTS);
428
429 printf (UT_HOST_PORT, 'p', myport);
430
431 printf (" %s\n", "-w, --warning=INTEGER");
432 printf (" %s\n", _("Threshold which will result in a warning status"));
433 printf (" %s\n", "-c, --critical=INTEGER");
434 printf (" %s\n", _("Threshold which will result in a critical status"));
435 printf (" %s\n", "-v, --variable=STRING");
436 printf (" %s\n", _("Variable to check. Valid variables include:"));
437 printf (" %s\n", _("LOAD1 = 1 minute average CPU load"));
438 printf (" %s\n", _("LOAD5 = 5 minute average CPU load"));
439 printf (" %s\n", _("LOAD15 = 15 minute average CPU load"));
440 printf (" %s\n", _("DPU<filesys> = percent used disk space on filesystem <filesys>"));
441 printf (" %s\n", _("PROC<process> = number of running processes with name <process>"));
442 printf (" %s\n", _("NET<port> = number of active connections on TCP port <port>"));
443 printf (" %s\n", _("UPTIME = system uptime in seconds"));
444
445 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
446
447 printf (UT_VERBOSE);
448
449 printf ("\n");
450 printf ("%s\n", _("This plugin requires that Eric Molitors' Over-CR collector daemon be"));
451 printf ("%s\n", _("running on the remote server."));
452 printf ("%s\n", _("Over-CR can be downloaded from http://www.molitor.org/overcr"));
453 printf ("%s\n", _("This plugin was tested with version 0.99.53 of the Over-CR collector"));
454
455 printf ("\n");
456 printf ("%s\n", _("Notes:"));
457 printf (" %s\n", _("For the available options, the critical threshold value should always be"));
458 printf (" %s\n", _("higher than the warning threshold value, EXCEPT with the uptime variable"));
459
460 printf (UT_SUPPORT);
461}
462
463
464void
465print_usage (void)
466{
467 printf ("%s\n", _("Usage:"));
468 printf ("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname);
469}
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index 94d589e1..8cbaaeeb 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -1,95 +1,89 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_pgsql plugin 3 * Monitoring check_pgsql plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2011 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_pgsql plugin 10 * This file contains the check_pgsql plugin
11* 11 *
12* Test whether a PostgreSQL Database is accepting connections. 12 * Test whether a PostgreSQL Database 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_pgsql"; 31#include "output.h"
32const char *copyright = "1999-2011"; 32#include "perfdata.h"
33const char *email = "devel@monitoring-plugins.org"; 33#include "states.h"
34
35#include "common.h" 34#include "common.h"
36#include "utils.h" 35#include "utils.h"
37#include "utils_cmd.h" 36#include "utils_cmd.h"
38 37#include "check_pgsql.d/config.h"
38#include "thresholds.h"
39#include "netutils.h" 39#include "netutils.h"
40#include <libpq-fe.h> 40#include <libpq-fe.h>
41#include <pg_config_manual.h> 41#include <pg_config_manual.h>
42 42
43#define DEFAULT_DB "template1" 43const char *progname = "check_pgsql";
44const char *copyright = "1999-2024";
45const char *email = "devel@monitoring-plugins.org";
46
44#define DEFAULT_HOST "127.0.0.1" 47#define DEFAULT_HOST "127.0.0.1"
45 48
46/* return the PSQL server version as a 3-tuple */ 49/* return the PSQL server version as a 3-tuple */
47#define PSQL_SERVER_VERSION3(server_version) \ 50#define PSQL_SERVER_VERSION3(server_version) \
48 (server_version) / 10000, \ 51 ((server_version) / 10000), \
49 (server_version) / 100 - (int)((server_version) / 10000) * 100, \ 52 (((server_version) / 100) - (int)(((server_version) / 10000) * 100)), \
50 (server_version) - (int)((server_version) / 100) * 100 53 (server_version) - (int)(((server_version) / 100) * 100)
51/* return true if the given host is a UNIX domain socket */ 54/* return true if the given host is a UNIX domain socket */
52#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) \ 55#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
53 ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
54/* return a 3-tuple identifying a host/port independent of the socket type */ 56/* return a 3-tuple identifying a host/port independent of the socket type */
55#define PSQL_SOCKET3(host, port) \ 57#define PSQL_SOCKET3(host, port) \
56 ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \ 58 ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \
57 PSQL_IS_UNIX_DOMAIN_SOCKET (host) ? "/.s.PGSQL." : ":", \ 59 PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port
58 port 60
59 61typedef struct {
60enum { 62 int errorcode;
61 DEFAULT_PORT = 5432, 63 check_pgsql_config config;
62 DEFAULT_WARN = 2, 64} check_pgsql_config_wrapper;
63 DEFAULT_CRIT = 8 65static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
64}; 66
65 67static void print_help(void);
66 68static bool is_pg_logname(char * /*username*/);
67 69
68int process_arguments (int, char **); 70typedef enum {
69int validate_arguments (void); 71 QUERY_OK,
70void print_usage (void); 72 ERROR_WITH_QUERY, // critical
71void print_help (void); 73 NO_ROWS_RETURNED, // warning
72bool is_pg_logname (char *); 74 NO_COLUMNS_RETURNED, // warning
73int do_query (PGconn *, char *); 75 NO_DATA_RETURNED, // critica/
74 76 RESULT_IS_NOT_NUMERIC // critical
75char *pghost = NULL; /* host name of the backend server */ 77} do_query_errorcode;
76char *pgport = NULL; /* port of the backend server */ 78
77int default_port = DEFAULT_PORT; 79typedef struct {
78char *pgoptions = NULL; 80 do_query_errorcode error_code;
79char *pgtty = NULL; 81 double numerical_result;
80char dbName[NAMEDATALEN] = DEFAULT_DB; 82} do_query_wrapper;
81char *pguser = NULL; 83static do_query_wrapper do_query(PGconn * /*conn*/, char * /*query*/);
82char *pgpasswd = NULL; 84void print_usage(void);
83char *pgparams = NULL; 85
84double twarn = (double)DEFAULT_WARN; 86static int verbose = 0;
85double tcrit = (double)DEFAULT_CRIT;
86char *pgquery = NULL;
87#define OPTID_QUERYNAME -1000
88char *pgqueryname = NULL;
89char *query_warning = NULL;
90char *query_critical = NULL;
91thresholds *qthresholds = NULL;
92int verbose = 0;
93 87
94/****************************************************************************** 88/******************************************************************************
95 89
@@ -141,237 +135,347 @@ Please note that all tags must be lowercase to use the DocBook XML DTD.
141-@@ 135-@@
142******************************************************************************/ 136******************************************************************************/
143 137
138int main(int argc, char **argv) {
139 setlocale(LC_ALL, "");
140 bindtextdomain(PACKAGE, LOCALEDIR);
141 textdomain(PACKAGE);
144 142
143 /* Parse extra opts if any */
144 argv = np_extra_opts(&argc, argv, progname);
145 145
146int 146 check_pgsql_config_wrapper tmp_config = process_arguments(argc, argv);
147main (int argc, char **argv) 147 if (tmp_config.errorcode == ERROR) {
148{ 148 usage4(_("Could not parse arguments"));
149 PGconn *conn; 149 }
150 char *conninfo = NULL;
151
152 struct timeval start_timeval;
153 struct timeval end_timeval;
154 double elapsed_time;
155 int status = STATE_UNKNOWN;
156 int query_status = STATE_UNKNOWN;
157
158 /* begin, by setting the parameters for a backend connection if the
159 * parameters are null, then the system will try to use reasonable
160 * defaults by looking up environment variables or, failing that,
161 * using hardwired constants */
162 150
163 pgoptions = NULL; /* special options to start up the backend server */ 151 const check_pgsql_config config = tmp_config.config;
164 pgtty = NULL; /* debugging tty for the backend server */
165 152
166 setlocale (LC_ALL, ""); 153 if (config.output_format_is_set) {
167 bindtextdomain (PACKAGE, LOCALEDIR); 154 mp_set_format(config.output_format);
168 textdomain (PACKAGE); 155 }
169 156
170 /* Parse extra opts if any */ 157 /* Set signal handling and alarm */
171 argv=np_extra_opts (&argc, argv, progname); 158 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
159 usage4(_("Cannot catch SIGALRM"));
160 }
161 alarm(timeout_interval);
172 162
173 if (process_arguments (argc, argv) == ERROR) 163 char *conninfo = NULL;
174 usage4 (_("Could not parse arguments")); 164 if (config.pgparams) {
175 if (verbose > 2) 165 asprintf(&conninfo, "%s ", config.pgparams);
176 printf("Arguments initialized\n"); 166 }
177 167
178 /* Set signal handling and alarm */ 168 asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", config.dbName);
179 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 169 if (config.pghost) {
180 usage4 (_("Cannot catch SIGALRM")); 170 asprintf(&conninfo, "%s host = '%s'", conninfo, config.pghost);
171 }
172 if (config.pgport) {
173 asprintf(&conninfo, "%s port = '%s'", conninfo, config.pgport);
174 }
175 if (config.pgoptions) {
176 asprintf(&conninfo, "%s options = '%s'", conninfo, config.pgoptions);
181 } 177 }
182 alarm (timeout_interval);
183
184 if (pgparams)
185 asprintf (&conninfo, "%s ", pgparams);
186
187 asprintf (&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", dbName);
188 if (pghost)
189 asprintf (&conninfo, "%s host = '%s'", conninfo, pghost);
190 if (pgport)
191 asprintf (&conninfo, "%s port = '%s'", conninfo, pgport);
192 if (pgoptions)
193 asprintf (&conninfo, "%s options = '%s'", conninfo, pgoptions);
194 /* if (pgtty) -- ignored by PQconnectdb */ 178 /* if (pgtty) -- ignored by PQconnectdb */
195 if (pguser) 179 if (config.pguser) {
196 asprintf (&conninfo, "%s user = '%s'", conninfo, pguser); 180 asprintf(&conninfo, "%s user = '%s'", conninfo, config.pguser);
181 }
197 182
198 if (verbose) /* do not include password (see right below) in output */ 183 if (verbose) { /* do not include password (see right below) in output */
199 printf ("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo, 184 printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo,
200 pgpasswd ? " password = <hidden>" : ""); 185 config.pgpasswd ? " password = <hidden>" : "");
186 }
201 187
202 if (pgpasswd) 188 if (config.pgpasswd) {
203 asprintf (&conninfo, "%s password = '%s'", conninfo, pgpasswd); 189 asprintf(&conninfo, "%s password = '%s'", conninfo, config.pgpasswd);
190 }
204 191
205 /* make a connection to the database */ 192 /* make a connection to the database */
206 gettimeofday (&start_timeval, NULL); 193 struct timeval start_timeval;
207 conn = PQconnectdb (conninfo); 194 gettimeofday(&start_timeval, NULL);
208 gettimeofday (&end_timeval, NULL); 195 PGconn *conn = PQconnectdb(conninfo);
196 struct timeval end_timeval;
197 gettimeofday(&end_timeval, NULL);
209 198
210 while (start_timeval.tv_usec > end_timeval.tv_usec) { 199 while (start_timeval.tv_usec > end_timeval.tv_usec) {
211 --end_timeval.tv_sec; 200 --end_timeval.tv_sec;
212 end_timeval.tv_usec += 1000000; 201 end_timeval.tv_usec += 1000000;
213 } 202 }
214 elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) 203 double elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) +
215 + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0; 204 ((double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0);
216 205
217 if (verbose) 206 if (verbose) {
218 printf("Time elapsed: %f\n", elapsed_time); 207 printf("Time elapsed: %f\n", elapsed_time);
208 }
219 209
220 /* check to see that the backend connection was successfully made */ 210 /* check to see that the backend connection was successfully made */
221 if (verbose) 211 if (verbose) {
222 printf("Verifying connection\n"); 212 printf("Verifying connection\n");
223 if (PQstatus (conn) == CONNECTION_BAD) {
224 printf (_("CRITICAL - no connection to '%s' (%s).\n"),
225 dbName, PQerrorMessage (conn));
226 PQfinish (conn);
227 return STATE_CRITICAL;
228 }
229 else if (elapsed_time > tcrit) {
230 status = STATE_CRITICAL;
231 } 213 }
232 else if (elapsed_time > twarn) { 214
233 status = STATE_WARNING; 215 mp_check overall = mp_check_init();
234 } 216
235 else { 217 mp_subcheck sc_connection = mp_subcheck_init();
236 status = STATE_OK; 218
219 if (PQstatus(conn) == CONNECTION_BAD) {
220 sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL);
221 xasprintf(&sc_connection.output, "no connection to '%s' (%s)", config.dbName,
222 PQerrorMessage(conn));
223 PQfinish(conn);
224 mp_add_subcheck_to_check(&overall, sc_connection);
225 mp_exit(overall);
226 } else {
227 sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK);
228 xasprintf(&sc_connection.output, "connected to '%s'", config.dbName);
229 mp_add_subcheck_to_check(&overall, sc_connection);
237 } 230 }
238 231
232 mp_subcheck sc_connection_time = mp_subcheck_init();
233 sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_UNKNOWN);
234
235 xasprintf(&sc_connection_time.output, "connection time: %.10g", elapsed_time);
236
237 mp_perfdata pd_connection_time = perfdata_init();
238 pd_connection_time.label = "time";
239 pd_connection_time.uom = "s";
240 pd_connection_time = mp_set_pd_value(pd_connection_time, elapsed_time);
241 pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.time_thresholds);
242
243 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time);
244 sc_connection_time =
245 mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_connection_time));
246
247 mp_add_subcheck_to_check(&overall, sc_connection_time);
248
239 if (verbose) { 249 if (verbose) {
240 char *server_host = PQhost (conn); 250 char *server_host = PQhost(conn);
241 int server_version = PQserverVersion (conn); 251 int server_version = PQserverVersion(conn);
242 252
243 printf ("Successfully connected to database %s (user %s) " 253 printf("Successfully connected to database %s (user %s) "
244 "at server %s%s%s (server version: %d.%d.%d, " 254 "at server %s%s%s (server version: %d.%d.%d, "
245 "protocol version: %d, pid: %d)\n", 255 "protocol version: %d, pid: %d)\n",
246 PQdb (conn), PQuser (conn), 256 PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)),
247 PSQL_SOCKET3 (server_host, PQport (conn)), 257 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn));
248 PSQL_SERVER_VERSION3 (server_version),
249 PQprotocolVersion (conn), PQbackendPID (conn));
250 } 258 }
251 259
252 printf (_(" %s - database %s (%f sec.)|%s\n"), 260 if (config.pgquery) {
253 state_text(status), dbName, elapsed_time, 261 mp_subcheck sc_query = mp_subcheck_init();
254 fperfdata("time", elapsed_time, "s", 262 sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN);
255 !!(twarn > 0.0), twarn, !!(tcrit > 0.0), tcrit, true, 0, false,0)); 263 if (config.pgqueryname) {
264 xasprintf(&sc_query.output, "query '%s'", config.pgqueryname);
265 } else {
266 xasprintf(&sc_query.output, "query '%s'", config.pgquery);
267 }
268
269 do_query_wrapper query_result = do_query(conn, config.pgquery);
270
271 switch (query_result.error_code) {
272 case QUERY_OK: {
273 // Query was successful and there is a numerical result
274 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
275 xasprintf(&sc_query.output, "%s succeeded", sc_query.output);
276
277 mp_perfdata pd_query = perfdata_init();
278 pd_query = mp_set_pd_value(pd_query, query_result.numerical_result);
279 pd_query = mp_pd_set_thresholds(pd_query, config.qthresholds);
280 pd_query.label = "query";
256 281
257 if (pgquery) 282 mp_subcheck sc_query_compare = mp_subcheck_init();
258 query_status = do_query (conn, pgquery); 283 mp_state_enum query_compare_state = mp_get_pd_status(pd_query);
284
285 sc_query_compare = mp_set_subcheck_state(sc_query_compare, query_compare_state);
286 mp_add_perfdata_to_subcheck(&sc_query_compare, pd_query);
287
288 if (query_compare_state == STATE_OK) {
289 xasprintf(&sc_query_compare.output, "query result '%f' is within thresholds",
290 query_result.numerical_result);
291 } else {
292 xasprintf(&sc_query_compare.output, "query result '%f' is violating thresholds",
293 query_result.numerical_result);
294 }
295 mp_add_subcheck_to_check(&overall, sc_query_compare);
296
297 } break;
298 case ERROR_WITH_QUERY:
299 xasprintf(&sc_query.output, "%s - Error with query: %s", sc_query.output,
300 PQerrorMessage(conn));
301 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
302 break;
303 case NO_ROWS_RETURNED:
304 xasprintf(&sc_query.output, "%s - no rows were returned by the query", sc_query.output);
305 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
306 break;
307 case NO_COLUMNS_RETURNED:
308 xasprintf(&sc_query.output, "%s - no columns were returned by the query",
309 sc_query.output);
310 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
311 break;
312 case NO_DATA_RETURNED:
313 xasprintf(&sc_query.output, "%s - no data was returned by the query", sc_query.output);
314 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
315 break;
316 case RESULT_IS_NOT_NUMERIC:
317 xasprintf(&sc_query.output, "%s - result of the query is not numeric", sc_query.output);
318 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
319 break;
320 };
259 321
260 if (verbose) 322 mp_add_subcheck_to_check(&overall, sc_query);
323 }
324
325 if (verbose) {
261 printf("Closing connection\n"); 326 printf("Closing connection\n");
262 PQfinish (conn); 327 }
263 return (pgquery && query_status > status) ? query_status : status; 328 PQfinish(conn);
329
330 mp_exit(overall);
264} 331}
265 332
333/* process command-line arguments */
334static check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
266 335
336 enum {
337 OPTID_QUERYNAME = CHAR_MAX + 1,
338 output_format_index,
339 };
267 340
268/* process command-line arguments */ 341 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
269int 342 {"version", no_argument, 0, 'V'},
270process_arguments (int argc, char **argv) 343 {"timeout", required_argument, 0, 't'},
271{ 344 {"critical", required_argument, 0, 'c'},
272 int c; 345 {"warning", required_argument, 0, 'w'},
273 346 {"hostname", required_argument, 0, 'H'},
274 int option = 0; 347 {"logname", required_argument, 0, 'l'},
275 static struct option longopts[] = { 348 {"password", required_argument, 0, 'p'},
276 {"help", no_argument, 0, 'h'}, 349 {"authorization", required_argument, 0, 'a'},
277 {"version", no_argument, 0, 'V'}, 350 {"port", required_argument, 0, 'P'},
278 {"timeout", required_argument, 0, 't'}, 351 {"database", required_argument, 0, 'd'},
279 {"critical", required_argument, 0, 'c'}, 352 {"option", required_argument, 0, 'o'},
280 {"warning", required_argument, 0, 'w'}, 353 {"query", required_argument, 0, 'q'},
281 {"hostname", required_argument, 0, 'H'}, 354 {"queryname", required_argument, 0, OPTID_QUERYNAME},
282 {"logname", required_argument, 0, 'l'}, 355 {"query_critical", required_argument, 0, 'C'},
283 {"password", required_argument, 0, 'p'}, 356 {"query_warning", required_argument, 0, 'W'},
284 {"authorization", required_argument, 0, 'a'}, 357 {"verbose", no_argument, 0, 'v'},
285 {"port", required_argument, 0, 'P'}, 358 {"output-format", required_argument, 0, output_format_index},
286 {"database", required_argument, 0, 'd'}, 359 {0, 0, 0, 0}};
287 {"option", required_argument, 0, 'o'}, 360
288 {"query", required_argument, 0, 'q'}, 361 check_pgsql_config_wrapper result = {
289 {"queryname", required_argument, 0, OPTID_QUERYNAME}, 362 .errorcode = OK,
290 {"query_critical", required_argument, 0, 'C'}, 363 .config = check_pgsql_config_init(),
291 {"query_warning", required_argument, 0, 'W'},
292 {"verbose", no_argument, 0, 'v'},
293 {0, 0, 0, 0}
294 }; 364 };
295 365
296 while (1) { 366 while (true) {
297 c = getopt_long (argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", 367 int option = 0;
298 longopts, &option); 368 int option_char =
369 getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option);
299 370
300 if (c == EOF) 371 if (option_char == EOF) {
301 break; 372 break;
373 }
302 374
303 switch (c) { 375 switch (option_char) {
304 case '?': /* usage */ 376 case output_format_index: {
305 usage5 (); 377 parsed_output_format parser = mp_parse_output_format(optarg);
306 case 'h': /* help */ 378 if (!parser.parsing_success) {
307 print_help (); 379 printf("Invalid output format: %s\n", optarg);
308 exit (STATE_UNKNOWN); 380 exit(STATE_UNKNOWN);
309 case 'V': /* version */ 381 }
310 print_revision (progname, NP_VERSION); 382
311 exit (STATE_UNKNOWN); 383 result.config.output_format_is_set = true;
312 case 't': /* timeout period */ 384 result.config.output_format = parser.output_format;
313 if (!is_integer (optarg))
314 usage2 (_("Timeout interval must be a positive integer"), optarg);
315 else
316 timeout_interval = atoi (optarg);
317 break;
318 case 'c': /* critical time threshold */
319 if (!is_nonnegative (optarg))
320 usage2 (_("Critical threshold must be a positive integer"), optarg);
321 else
322 tcrit = strtod (optarg, NULL);
323 break;
324 case 'w': /* warning time threshold */
325 if (!is_nonnegative (optarg))
326 usage2 (_("Warning threshold must be a positive integer"), optarg);
327 else
328 twarn = strtod (optarg, NULL);
329 break;
330 case 'C': /* critical query threshold */
331 query_critical = optarg;
332 break; 385 break;
333 case 'W': /* warning query threshold */ 386 }
334 query_warning = optarg; 387 case '?': /* usage */
388 usage5();
389 case 'h': /* help */
390 print_help();
391 exit(STATE_UNKNOWN);
392 case 'V': /* version */
393 print_revision(progname, NP_VERSION);
394 exit(STATE_UNKNOWN);
395 case 't': /* timeout period */
396 if (!is_integer(optarg)) {
397 usage2(_("Timeout interval must be a positive integer"), optarg);
398 } else {
399 timeout_interval = atoi(optarg);
400 }
335 break; 401 break;
336 case 'H': /* host */ 402 case 'c': /* critical time threshold */ {
337 if ((*optarg != '/') && (!is_host (optarg))) 403 mp_range_parsed tmp = mp_parse_range_string(optarg);
338 usage2 (_("Invalid hostname/address"), optarg); 404 if (tmp.error != MP_PARSING_SUCCESS) {
339 else 405 die(STATE_UNKNOWN, "failed to parse critical time threshold");
340 pghost = optarg; 406 }
407 result.config.time_thresholds =
408 mp_thresholds_set_crit(result.config.time_thresholds, tmp.range);
409 } break;
410 case 'w': /* warning time threshold */ {
411 mp_range_parsed tmp = mp_parse_range_string(optarg);
412 if (tmp.error != MP_PARSING_SUCCESS) {
413 die(STATE_UNKNOWN, "failed to parse warning time threshold");
414 }
415 result.config.time_thresholds =
416 mp_thresholds_set_warn(result.config.time_thresholds, tmp.range);
417 } break;
418 case 'C': /* critical query threshold */ {
419 mp_range_parsed tmp = mp_parse_range_string(optarg);
420 if (tmp.error != MP_PARSING_SUCCESS) {
421 die(STATE_UNKNOWN, "failed to parse critical query threshold");
422 }
423
424 result.config.qthresholds =
425 mp_thresholds_set_crit(result.config.qthresholds, tmp.range);
426
427 } break;
428 case 'W': /* warning query threshold */ {
429 mp_range_parsed tmp = mp_parse_range_string(optarg);
430 if (tmp.error != MP_PARSING_SUCCESS) {
431 die(STATE_UNKNOWN, "failed to parse warning query threshold");
432 }
433 result.config.qthresholds =
434 mp_thresholds_set_warn(result.config.qthresholds, tmp.range);
435 } break;
436 case 'H': /* host */
437 if ((*optarg != '/') && (!is_host(optarg))) {
438 usage2(_("Invalid hostname/address"), optarg);
439 } else {
440 result.config.pghost = optarg;
441 }
341 break; 442 break;
342 case 'P': /* port */ 443 case 'P': /* port */
343 if (!is_integer (optarg)) 444 if (!is_integer(optarg)) {
344 usage2 (_("Port must be a positive integer"), optarg); 445 usage2(_("Port must be a positive integer"), optarg);
345 else 446 } else {
346 pgport = optarg; 447 result.config.pgport = optarg;
448 }
347 break; 449 break;
348 case 'd': /* database name */ 450 case 'd': /* database name */
349 if (strlen(optarg) >= NAMEDATALEN) { 451 if (strlen(optarg) >= NAMEDATALEN) {
350 usage2 (_("Database name exceeds the maximum length"), optarg); 452 usage2(_("Database name exceeds the maximum length"), optarg);
351 } 453 }
352 snprintf(dbName, NAMEDATALEN, "%s", optarg); 454 snprintf(result.config.dbName, NAMEDATALEN, "%s", optarg);
353 break; 455 break;
354 case 'l': /* login name */ 456 case 'l': /* login name */
355 if (!is_pg_logname (optarg)) 457 if (!is_pg_logname(optarg)) {
356 usage2 (_("User name is not valid"), optarg); 458 usage2(_("User name is not valid"), optarg);
357 else 459 } else {
358 pguser = optarg; 460 result.config.pguser = optarg;
461 }
359 break; 462 break;
360 case 'p': /* authentication password */ 463 case 'p': /* authentication password */
361 case 'a': 464 case 'a':
362 pgpasswd = optarg; 465 result.config.pgpasswd = optarg;
363 break; 466 break;
364 case 'o': 467 case 'o':
365 if (pgparams) 468 if (result.config.pgparams) {
366 asprintf (&pgparams, "%s %s", pgparams, optarg); 469 asprintf(&result.config.pgparams, "%s %s", result.config.pgparams, optarg);
367 else 470 } else {
368 asprintf (&pgparams, "%s", optarg); 471 asprintf(&result.config.pgparams, "%s", optarg);
472 }
369 break; 473 break;
370 case 'q': 474 case 'q':
371 pgquery = optarg; 475 result.config.pgquery = optarg;
372 break; 476 break;
373 case OPTID_QUERYNAME: 477 case OPTID_QUERYNAME:
374 pgqueryname = optarg; 478 result.config.pgqueryname = optarg;
375 break; 479 break;
376 case 'v': 480 case 'v':
377 verbose++; 481 verbose++;
@@ -379,38 +483,7 @@ process_arguments (int argc, char **argv)
379 } 483 }
380 } 484 }
381 485
382 set_thresholds (&qthresholds, query_warning, query_critical); 486 return result;
383
384 return validate_arguments ();
385}
386
387
388/******************************************************************************
389
390@@-
391<sect3>
392<title>validate_arguments</title>
393
394<para>&PROTO_validate_arguments;</para>
395
396<para>Given a database name, this function returns true if the string
397is a valid PostgreSQL database name, and returns false if it is
398not.</para>
399
400<para>Valid PostgreSQL database names are less than &NAMEDATALEN;
401characters long and consist of letters, numbers, and underscores. The
402first character cannot be a number, however.</para>
403
404</sect3>
405-@@
406******************************************************************************/
407
408
409
410int
411validate_arguments ()
412{
413 return OK;
414} 487}
415 488
416/** 489/**
@@ -437,11 +510,10 @@ should be added.</para>
437-@@ 510-@@
438******************************************************************************/ 511******************************************************************************/
439 512
440 513static bool is_pg_logname(char *username) {
441 514 if (strlen(username) > NAMEDATALEN - 1) {
442bool is_pg_logname (char *username) {
443 if (strlen (username) > NAMEDATALEN - 1)
444 return (false); 515 return (false);
516 }
445 return (true); 517 return (true);
446} 518}
447 519
@@ -453,182 +525,168 @@ bool is_pg_logname (char *username) {
453-@@ 525-@@
454******************************************************************************/ 526******************************************************************************/
455 527
456 528void print_help(void) {
457
458void
459print_help (void)
460{
461 char *myport; 529 char *myport;
462 530
463 xasprintf (&myport, "%d", DEFAULT_PORT); 531 xasprintf(&myport, "%d", 5432);
464 532
465 print_revision (progname, NP_VERSION); 533 print_revision(progname, NP_VERSION);
466 534
467 printf (COPYRIGHT, copyright, email); 535 printf(COPYRIGHT, copyright, email);
468 536
469 printf (_("Test whether a PostgreSQL Database is accepting connections.")); 537 printf(_("Test whether a PostgreSQL Database is accepting connections."));
470 538
471 printf ("\n\n"); 539 printf("\n\n");
472 540
473 print_usage (); 541 print_usage();
474 542
475 printf (UT_HELP_VRSN); 543 printf(UT_HELP_VRSN);
476 printf (UT_EXTRA_OPTS); 544 printf(UT_EXTRA_OPTS);
477 545
478 printf (UT_HOST_PORT, 'P', myport); 546 printf(UT_HOST_PORT, 'P', myport);
479 547
480 printf (" %s\n", "-d, --database=STRING"); 548 printf(" %s\n", "-d, --database=STRING");
481 printf (" %s", _("Database to check ")); 549 printf(" %s", _("Database to check "));
482 printf (_("(default: %s)\n"), DEFAULT_DB); 550 printf(_("(default: %s)\n"), DEFAULT_DB);
483 printf (" %s\n", "-l, --logname = STRING"); 551 printf(" %s\n", "-l, --logname = STRING");
484 printf (" %s\n", _("Login name of user")); 552 printf(" %s\n", _("Login name of user"));
485 printf (" %s\n", "-p, --password = STRING"); 553 printf(" %s\n", "-p, --password = STRING");
486 printf (" %s\n", _("Password (BIG SECURITY ISSUE)")); 554 printf(" %s\n", _("Password (BIG SECURITY ISSUE)"));
487 printf (" %s\n", "-o, --option = STRING"); 555 printf(" %s\n", "-o, --option = STRING");
488 printf (" %s\n", _("Connection parameters (keyword = value), see below")); 556 printf(" %s\n", _("Connection parameters (keyword = value), see below"));
489 557
490 printf (UT_WARN_CRIT); 558 printf(UT_WARN_CRIT);
491 559
492 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 560 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
493 561
494 printf (" %s\n", "-q, --query=STRING"); 562 printf(" %s\n", "-q, --query=STRING");
495 printf (" %s\n", _("SQL query to run. Only first column in first row will be read")); 563 printf(" %s\n", _("SQL query to run. Only first column in first row will be read"));
496 printf (" %s\n", "--queryname=STRING"); 564 printf(" %s\n", "--queryname=STRING");
497 printf (" %s\n", _("A name for the query, this string is used instead of the query")); 565 printf(" %s\n", _("A name for the query, this string is used instead of the query"));
498 printf (" %s\n", _("in the long output of the plugin")); 566 printf(" %s\n", _("in the long output of the plugin"));
499 printf (" %s\n", "-W, --query-warning=RANGE"); 567 printf(" %s\n", "-W, --query_warning=RANGE");
500 printf (" %s\n", _("SQL query value to result in warning status (double)")); 568 printf(" %s\n", _("SQL query value to result in warning status (double)"));
501 printf (" %s\n", "-C, --query-critical=RANGE"); 569 printf(" %s\n", "-C, --query_critical=RANGE");
502 printf (" %s\n", _("SQL query value to result in critical status (double)")); 570 printf(" %s\n", _("SQL query value to result in critical status (double)"));
503 571
504 printf (UT_VERBOSE); 572 printf(UT_VERBOSE);
505 573 printf(UT_OUTPUT_FORMAT);
506 printf ("\n"); 574
507 printf (" %s\n", _("All parameters are optional.")); 575 printf("\n");
508 printf (" %s\n", _("This plugin tests a PostgreSQL DBMS to determine whether it is active and")); 576 printf(" %s\n", _("All parameters are optional."));
509 printf (" %s\n", _("accepting queries. In its current operation, it simply connects to the")); 577 printf(" %s\n", _("This plugin tests a PostgreSQL DBMS to determine whether it is active and"));
510 printf (" %s\n", _("specified database, and then disconnects. If no database is specified, it")); 578 printf(" %s\n", _("accepting queries. In its current operation, it simply connects to the"));
511 printf (" %s\n", _("connects to the template1 database, which is present in every functioning")); 579 printf(" %s\n", _("specified database, and then disconnects. If no database is specified, it"));
512 printf (" %s\n\n", _("PostgreSQL DBMS.")); 580 printf(" %s\n", _("connects to the template1 database, which is present in every functioning"));
513 581 printf(" %s\n\n", _("PostgreSQL DBMS."));
514 printf (" %s\n", _("If a query is specified using the -q option, it will be executed after")); 582
515 printf (" %s\n", _("connecting to the server. The result from the query has to be numeric.")); 583 printf(" %s\n", _("If a query is specified using the -q option, it will be executed after"));
516 printf (" %s\n", _("Multiple SQL commands, separated by semicolon, are allowed but the result ")); 584 printf(" %s\n", _("connecting to the server. The result from the query has to be numeric."));
517 printf (" %s\n", _("of the last command is taken into account only. The value of the first")); 585 printf(" %s\n",
518 printf (" %s\n", _("column in the first row is used as the check result. If a second column is")); 586 _("Multiple SQL commands, separated by semicolon, are allowed but the result "));
519 printf (" %s\n", _("present in the result set, this is added to the plugin output with a")); 587 printf(" %s\n", _("of the last command is taken into account only. The value of the first"));
520 printf (" %s\n", _("prefix of \"Extra Info:\". This information can be displayed in the system")); 588 printf(" %s\n",
521 printf (" %s\n\n", _("executing the plugin.")); 589 _("column in the first row is used as the check result. If a second column is"));
522 590 printf(" %s\n", _("present in the result set, this is added to the plugin output with a"));
523 printf (" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual")); 591 printf(" %s\n",
524 printf (" %s\n\n", _("for details about how to access internal statistics of the database server.")); 592 _("prefix of \"Extra Info:\". This information can be displayed in the system"));
525 593 printf(" %s\n\n", _("executing the plugin."));
526 printf (" %s\n", _("For a list of available connection parameters which may be used with the -o")); 594
527 printf (" %s\n", _("command line option, see the documentation for PQconnectdb() in the chapter")); 595 printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual"));
528 printf (" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be")); 596 printf(" %s\n\n",
529 printf (" %s\n", _("used to specify a service name in pg_service.conf to be used for additional")); 597 _("for details about how to access internal statistics of the database server."));
530 printf (" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:")); 598
531 printf (" %s\n\n", _("-o 'sslmode=require'.")); 599 printf(" %s\n",
532 600 _("For a list of available connection parameters which may be used with the -o"));
533 printf (" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To")); 601 printf(" %s\n",
534 printf (" %s\n", _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP")); 602 _("command line option, see the documentation for PQconnectdb() in the chapter"));
535 printf (" %s\n\n", _("connections (start the postmaster with the -i option).")); 603 printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be"));
536 604 printf(" %s\n",
537 printf (" %s\n", _("Typically, the monitoring user (unless the --logname option is used) should be")); 605 _("used to specify a service name in pg_service.conf to be used for additional"));
538 printf (" %s\n", _("able to connect to the database without a password. The plugin can also send")); 606 printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:"));
539 printf (" %s\n", _("a password, but no effort is made to obscure or encrypt the password.")); 607 printf(" %s\n\n", _("-o 'sslmode=require'."));
540 608
541 printf (UT_SUPPORT); 609 printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To"));
610 printf(" %s\n",
611 _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP"));
612 printf(" %s\n\n", _("connections (start the postmaster with the -i option)."));
613
614 printf(" %s\n",
615 _("Typically, the monitoring user (unless the --logname option is used) should be"));
616 printf(" %s\n",
617 _("able to connect to the database without a password. The plugin can also send"));
618 printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password."));
619
620 printf(UT_SUPPORT);
542} 621}
543 622
544 623void print_usage(void) {
545 624 printf("%s\n", _("Usage:"));
546void 625 printf("%s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n", progname);
547print_usage (void) 626 printf(" [-t <timeout>] [-d <database>] [-l <logname>] [-p <password>]\n"
548{ 627 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
549 printf ("%s\n", _("Usage:"));
550 printf ("%s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n", progname);
551 printf (" [-t <timeout>] [-d <database>] [-l <logname>] [-p <password>]\n"
552 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
553} 628}
554 629
555int 630static do_query_wrapper do_query(PGconn *conn, char *query) {
556do_query (PGconn *conn, char *query) 631 if (verbose) {
557{ 632 printf("Executing SQL query \"%s\".\n", query);
558 PGresult *res; 633 }
559 634 PGresult *res = PQexec(conn, query);
560 char *val_str;
561 char *extra_info;
562 double value;
563
564 char *endptr = NULL;
565
566 int my_status = STATE_UNKNOWN;
567 635
568 if (verbose) 636 do_query_wrapper result = {
569 printf ("Executing SQL query \"%s\".\n", query); 637 .error_code = QUERY_OK,
570 res = PQexec (conn, query); 638 };
571 639
572 if (PGRES_TUPLES_OK != PQresultStatus (res)) { 640 if (PGRES_TUPLES_OK != PQresultStatus(res)) {
573 printf (_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), 641 // TODO
574 PQerrorMessage (conn)); 642 // printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"),
575 return STATE_CRITICAL; 643 // PQerrorMessage(conn));
644 result.error_code = ERROR_WITH_QUERY;
645 return result;
576 } 646 }
577 647
578 if (PQntuples (res) < 1) { 648 if (PQntuples(res) < 1) {
579 printf ("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); 649 // TODO
580 return STATE_WARNING; 650 // printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned"));
651 result.error_code = NO_ROWS_RETURNED;
652 return result;
581 } 653 }
582 654
583 if (PQnfields (res) < 1) { 655 if (PQnfields(res) < 1) {
584 printf ("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); 656 // TODO
585 return STATE_WARNING; 657 // printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned"));
658 result.error_code = NO_COLUMNS_RETURNED;
659 return result;
586 } 660 }
587 661
588 val_str = PQgetvalue (res, 0, 0); 662 char *val_str = PQgetvalue(res, 0, 0);
589 if (! val_str) { 663 if (!val_str) {
590 printf ("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); 664 // TODO
591 return STATE_CRITICAL; 665 // printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned"));
666 result.error_code = NO_DATA_RETURNED;
667 return result;
592 } 668 }
593 669
594 value = strtod (val_str, &endptr); 670 char *endptr = NULL;
595 if (verbose) 671 double value = strtod(val_str, &endptr);
596 printf ("Query result: %f\n", value); 672 if (verbose) {
597 673 printf("Query result: %f\n", value);
598 if (endptr == val_str) {
599 printf ("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str);
600 return STATE_CRITICAL;
601 }
602 else if ((endptr != NULL) && (*endptr != '\0')) {
603 if (verbose)
604 printf ("Garbage after value: %s.\n", endptr);
605 } 674 }
606 675
607 my_status = get_status (value, qthresholds); 676 if (endptr == val_str) {
608 printf ("QUERY %s - ", 677 // TODO
609 (my_status == STATE_OK) 678 // printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str);
610 ? _("OK") 679 result.error_code = RESULT_IS_NOT_NUMERIC;
611 : (my_status == STATE_WARNING) 680 return result;
612 ? _("WARNING")
613 : (my_status == STATE_CRITICAL)
614 ? _("CRITICAL")
615 : _("UNKNOWN"));
616 if(pgqueryname) {
617 printf (_("%s returned %f"), pgqueryname, value);
618 }
619 else {
620 printf (_("'%s' returned %f"), query, value);
621 } 681 }
622 682
623 printf ("|query=%f;%s;%s;;\n", value, 683 if ((endptr != NULL) && (*endptr != '\0')) {
624 query_warning ? query_warning : "", 684 if (verbose) {
625 query_critical ? query_critical : ""); 685 printf("Garbage after value: %s.\n", endptr);
626 if (PQnfields (res) > 1) {
627 extra_info = PQgetvalue (res, 0, 1);
628 if (extra_info != NULL) {
629 printf ("Extra Info: %s\n", extra_info);
630 } 686 }
631 } 687 }
632 return my_status;
633}
634 688
689 result.numerical_result = value;
690
691 return result;
692}
diff --git a/plugins/check_pgsql.d/config.h b/plugins/check_pgsql.d/config.h
new file mode 100644
index 00000000..7cf0637b
--- /dev/null
+++ b/plugins/check_pgsql.d/config.h
@@ -0,0 +1,70 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "perfdata.h"
6#include "thresholds.h"
7#include <stddef.h>
8#include <pg_config_manual.h>
9
10#define DEFAULT_DB "template1"
11
12enum {
13 DEFAULT_WARN = 2,
14 DEFAULT_CRIT = 8,
15};
16
17typedef struct {
18 char *pghost; /* host name of the backend server */
19 char *pgport; /* port of the backend server */
20 char *pgoptions; /* special options to start up the backend server */
21 char *pgtty; /* debugging tty for the backend server */
22 char dbName[NAMEDATALEN];
23 char *pguser;
24 char *pgpasswd;
25 char *pgparams;
26 char *pgquery;
27 char *pgqueryname;
28
29 mp_thresholds time_thresholds;
30 mp_thresholds qthresholds;
31
32 bool output_format_is_set;
33 mp_output_format output_format;
34} check_pgsql_config;
35
36/* begin, by setting the parameters for a backend connection if the
37 * parameters are null, then the system will try to use reasonable
38 * defaults by looking up environment variables or, failing that,
39 * using hardwired constants
40 * this targets .pgoptions and .pgtty
41 */
42
43check_pgsql_config check_pgsql_config_init() {
44 check_pgsql_config tmp = {
45 .pghost = NULL,
46 .pgport = NULL,
47 .pgoptions = NULL,
48 .pgtty = NULL,
49 .dbName = DEFAULT_DB,
50 .pguser = NULL,
51 .pgpasswd = NULL,
52 .pgparams = NULL,
53 .pgquery = NULL,
54 .pgqueryname = NULL,
55
56 .time_thresholds = mp_thresholds_init(),
57 .qthresholds = mp_thresholds_init(),
58
59 .output_format_is_set = false,
60 };
61
62 mp_range tmp_range = mp_range_init();
63 tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_WARN));
64 tmp.time_thresholds = mp_thresholds_set_warn(tmp.time_thresholds, tmp_range);
65
66 tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_CRIT));
67 tmp.time_thresholds = mp_thresholds_set_crit(tmp.time_thresholds, tmp_range);
68
69 return tmp;
70}
diff --git a/plugins/check_ping.c b/plugins/check_ping.c
index 6e162e6a..0c9cb19d 100644
--- a/plugins/check_ping.c
+++ b/plugins/check_ping.c
@@ -1,620 +1,669 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ping plugin 3 * Monitoring check_ping plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 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_ping plugin 10 * This file contains the check_ping plugin
11* 11 *
12* Use the ping program to check connection statistics for a remote host. 12 * Use the ping program to check connection statistics for a remote host.
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_ping"; 31const char *progname = "check_ping";
32const char *copyright = "2000-2007"; 32const char *copyright = "2000-2024";
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 "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
45enum {
46 UNKNOWN_PACKET_LOSS = 200, /* 200% */
47 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */
48};
49
50int process_arguments (int, char **);
51int get_threshold (char *, float *, int *);
52int validate_arguments (void);
53int run_ping (const char *cmd, const char *addr);
54int error_scan (char buf[MAX_INPUT_BUFFER], const char *addr);
55void print_usage (void);
56void print_help (void);
57
58bool display_html = false;
59int wpl = UNKNOWN_PACKET_LOSS;
60int cpl = UNKNOWN_PACKET_LOSS;
61float wrta = UNKNOWN_TRIP_TIME;
62float crta = UNKNOWN_TRIP_TIME;
63char **addresses = NULL;
64int n_addresses = 0;
65int max_addr = 1;
66int max_packets = -1;
67int verbose = 0;
68
69float rta = UNKNOWN_TRIP_TIME;
70int pl = UNKNOWN_PACKET_LOSS;
71
72char *warn_text;
73
74
75
76int
77main (int argc, char **argv)
78{
79 char *cmd = NULL;
80 char *rawcmd = NULL;
81 int result = STATE_UNKNOWN;
82 int this_result = STATE_UNKNOWN;
83 int i;
84 45
85 setlocale (LC_ALL, ""); 46typedef struct {
86 setlocale (LC_NUMERIC, "C"); 47 int errorcode;
87 bindtextdomain (PACKAGE, LOCALEDIR); 48 check_ping_config config;
88 textdomain (PACKAGE); 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*/);
52
53static int get_threshold(char * /*arg*/, double * /*trta*/, int * /*tpl*/);
54
55typedef struct {
56 mp_state_enum state;
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);
63static void print_help(void);
64void print_usage(void);
65
66static int verbose = 0;
89 67
90 addresses = malloc (sizeof(char*) * max_addr); 68static char *warn_text;
91 addresses[0] = NULL; 69
70int main(int argc, char **argv) {
71 setlocale(LC_ALL, "");
72 setlocale(LC_NUMERIC, "C");
73 bindtextdomain(PACKAGE, LOCALEDIR);
74 textdomain(PACKAGE);
92 75
93 /* Parse extra opts if any */ 76 /* Parse extra opts if any */
94 argv=np_extra_opts (&argc, argv, progname); 77 argv = np_extra_opts(&argc, argv, progname);
78
79 check_ping_config_wrapper tmp_config = process_arguments(argc, argv);
80 if (tmp_config.errorcode == ERROR) {
81 usage4(_("Could not parse arguments"));
82 }
95 83
96 if (process_arguments (argc, argv) == ERROR) 84 const check_ping_config config = tmp_config.config;
97 usage4 (_("Could not parse arguments"));
98 85
99 /* Set signal handling and alarm */ 86 /* Set signal handling and alarm */
100 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) { 87 if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
101 usage4 (_("Cannot catch SIGALRM")); 88 usage4(_("Cannot catch SIGALRM"));
102 } 89 }
103 90
104 /* If ./configure finds ping has timeout values, set plugin alarm slightly 91 /* If ./configure finds ping has timeout values, set plugin alarm slightly
105 * higher so that we can use response from command line ping */ 92 * higher so that we can use response from command line ping */
106#if defined(PING_PACKETS_FIRST) && defined(PING_HAS_TIMEOUT) 93#if defined(PING_PACKETS_FIRST) && defined(PING_HAS_TIMEOUT)
107 alarm (timeout_interval + 1); 94 alarm(timeout_interval + 1);
108#else 95#else
109 alarm (timeout_interval); 96 alarm(timeout_interval);
110#endif 97#endif
111 98
112 for (i = 0 ; i < n_addresses ; i++) { 99 int result = STATE_UNKNOWN;
113 100 char *rawcmd = NULL;
101 for (size_t i = 0; i < config.n_addresses; i++) {
114#ifdef PING6_COMMAND 102#ifdef PING6_COMMAND
115 if (address_family != AF_INET && is_inet6_addr(addresses[i])) 103 if (address_family != AF_INET && is_inet6_addr(config.addresses[i])) {
116 rawcmd = strdup(PING6_COMMAND); 104 rawcmd = strdup(PING6_COMMAND);
117 else 105 } else {
118 rawcmd = strdup(PING_COMMAND); 106 rawcmd = strdup(PING_COMMAND);
107 }
119#else 108#else
120 rawcmd = strdup(PING_COMMAND); 109 rawcmd = strdup(PING_COMMAND);
121#endif 110#endif
122 111
112 char *cmd = NULL;
113
123 /* does the host address of number of packets argument come first? */ 114 /* does the host address of number of packets argument come first? */
124#ifdef PING_PACKETS_FIRST 115#ifdef PING_PACKETS_FIRST
125# ifdef PING_HAS_TIMEOUT 116# ifdef PING_HAS_TIMEOUT
126 xasprintf (&cmd, rawcmd, timeout_interval, max_packets, addresses[i]); 117 xasprintf(&cmd, rawcmd, timeout_interval, config.max_packets, config.addresses[i]);
127# else 118# else
128 xasprintf (&cmd, rawcmd, max_packets, addresses[i]); 119 xasprintf(&cmd, rawcmd, config.max_packets, config.addresses[i]);
129# endif 120# endif
130#else 121#else
131 xasprintf (&cmd, rawcmd, addresses[i], max_packets); 122 xasprintf(&cmd, rawcmd, config.addresses[i], config.max_packets);
132#endif 123#endif
133 124
134 if (verbose >= 2) 125 if (verbose >= 2) {
135 printf ("CMD: %s\n", cmd); 126 printf("CMD: %s\n", cmd);
127 }
136 128
137 /* run the command */ 129 /* run the command */
138 this_result = run_ping (cmd, addresses[i]);
139 130
140 if (pl == UNKNOWN_PACKET_LOSS || rta < 0.0) { 131 ping_result pinged = run_ping(cmd, config.addresses[i], config.crta);
141 printf ("%s\n", cmd); 132
142 die (STATE_UNKNOWN, 133 if (pinged.packet_loss == UNKNOWN_PACKET_LOSS || pinged.round_trip_average < 0.0) {
143 _("CRITICAL - Could not interpret output from ping command\n")); 134 printf("%s\n", cmd);
135 die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n"));
144 } 136 }
145 137
146 if (pl >= cpl || rta >= crta || rta < 0) 138 if (pinged.packet_loss >= config.cpl || pinged.round_trip_average >= config.crta ||
147 this_result = STATE_CRITICAL; 139 pinged.round_trip_average < 0) {
148 else if (pl >= wpl || rta >= wrta) 140 pinged.state = STATE_CRITICAL;
149 this_result = STATE_WARNING; 141 } else if (pinged.packet_loss >= config.wpl || pinged.round_trip_average >= config.wrta) {
150 else if (pl >= 0 && rta >= 0) 142 pinged.state = STATE_WARNING;
151 this_result = max_state (STATE_OK, this_result); 143 } else if (pinged.packet_loss >= 0 && pinged.round_trip_average >= 0) {
152 144 pinged.state = max_state(STATE_OK, pinged.state);
153 if (n_addresses > 1 && this_result != STATE_UNKNOWN) 145 }
154 die (STATE_OK, "%s is alive\n", addresses[i]); 146
155 147 if (config.n_addresses > 1 && pinged.state != STATE_UNKNOWN) {
156 if (display_html == true) 148 die(STATE_OK, "%s is alive\n", config.addresses[i]);
157 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]); 149 }
158 if (pl == 100) 150
159 printf (_("PING %s - %sPacket loss = %d%%"), state_text (this_result), warn_text, 151 if (config.display_html) {
160 pl); 152 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, config.addresses[i]);
161 else 153 }
162 printf (_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), 154 if (pinged.packet_loss == 100) {
163 state_text (this_result), warn_text, pl, rta); 155 printf(_("PING %s - %sPacket loss = %d%%"), state_text(pinged.state), warn_text,
164 if (display_html == true) 156 pinged.packet_loss);
165 printf ("</A>"); 157 } else {
158 printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(pinged.state),
159 warn_text, pinged.packet_loss, pinged.round_trip_average);
160 }
161 if (config.display_html) {
162 printf("</A>");
163 }
166 164
167 /* Print performance data */ 165 /* Print performance data */
168 if (pl != 100) { 166 if (pinged.packet_loss != 100) {
169 printf("|%s", fperfdata ("rta", (double) rta, "ms", 167 printf("|%s",
170 wrta>0?true:false, wrta, 168 fperfdata("rta", pinged.round_trip_average, "ms", (bool)(config.wrta > 0),
171 crta>0?true:false, crta, 169 config.wrta, (bool)(config.crta > 0), config.crta, true, 0, false, 0));
172 true, 0, false, 0));
173 } else { 170 } else {
174 printf("| rta=U;%f;%f;;", wrta, crta); 171 printf("| rta=U;%f;%f;;", config.wrta, config.crta);
175 } 172 }
176 printf(" %s\n", perfdata ("pl", (long) pl, "%",
177 wpl>0?true:false, wpl,
178 cpl>0?true:false, cpl,
179 true, 0, false, 0));
180 173
181 if (verbose >= 2) 174 printf(" %s\n",
182 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl); 175 perfdata("pl", (long)pinged.packet_loss, "%", (bool)(config.wpl > 0), config.wpl,
176 (bool)(config.cpl > 0), config.cpl, true, 0, false, 0));
183 177
184 result = max_state (result, this_result); 178 if (verbose >= 2) {
185 free (rawcmd); 179 printf("%f:%d%% %f:%d%%\n", config.wrta, config.wpl, config.crta, config.cpl);
186 free (cmd); 180 }
181
182 result = max_state(result, pinged.state);
183 free(rawcmd);
184 free(cmd);
187 } 185 }
188 186
189 return result; 187 return result;
190} 188}
191 189
192
193
194/* process command-line arguments */ 190/* process command-line arguments */
195int 191check_ping_config_wrapper process_arguments(int argc, char **argv) {
196process_arguments (int argc, char **argv) 192 static struct option longopts[] = {STD_LONG_OPTS,
197{ 193 {"packets", required_argument, 0, 'p'},
198 int c = 1; 194 {"nohtml", no_argument, 0, 'n'},
199 char *ptr; 195 {"link", no_argument, 0, 'L'},
200 196 {"use-ipv4", no_argument, 0, '4'},
201 int option = 0; 197 {"use-ipv6", no_argument, 0, '6'},
202 static struct option longopts[] = { 198 {0, 0, 0, 0}};
203 STD_LONG_OPTS, 199
204 {"packets", required_argument, 0, 'p'}, 200 check_ping_config_wrapper result = {
205 {"nohtml", no_argument, 0, 'n'}, 201 .errorcode = OK,
206 {"link", no_argument, 0, 'L'}, 202 .config = check_ping_config_init(),
207 {"use-ipv4", no_argument, 0, '4'},
208 {"use-ipv6", no_argument, 0, '6'},
209 {0, 0, 0, 0}
210 }; 203 };
211 204
212 if (argc < 2) 205 if (argc < 2) {
213 return ERROR; 206 result.errorcode = ERROR;
207 return result;
208 }
214 209
215 for (c = 1; c < argc; c++) { 210 for (int index = 1; index < argc; index++) {
216 if (strcmp ("-to", argv[c]) == 0) 211 if (strcmp("-to", argv[index]) == 0) {
217 strcpy (argv[c], "-t"); 212 strcpy(argv[index], "-t");
218 if (strcmp ("-nohtml", argv[c]) == 0) 213 }
219 strcpy (argv[c], "-n"); 214 if (strcmp("-nohtml", argv[index]) == 0) {
215 strcpy(argv[index], "-n");
216 }
220 } 217 }
221 218
222 while (1) { 219 int option = 0;
223 c = getopt_long (argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option); 220 size_t max_addr = MAX_ADDR_START;
221 while (true) {
222 int option_index = getopt_long(argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option);
224 223
225 if (c == -1 || c == EOF) 224 if (CHECK_EOF(option_index)) {
226 break; 225 break;
226 }
227 227
228 switch (c) { 228 switch (option_index) {
229 case '?': /* usage */ 229 case '?': /* usage */
230 usage5 (); 230 usage5();
231 case 'h': /* help */ 231 case 'h': /* help */
232 print_help (); 232 print_help();
233 exit (STATE_UNKNOWN); 233 exit(STATE_UNKNOWN);
234 break; 234 break;
235 case 'V': /* version */ 235 case 'V': /* version */
236 print_revision (progname, NP_VERSION); 236 print_revision(progname, NP_VERSION);
237 exit (STATE_UNKNOWN); 237 exit(STATE_UNKNOWN);
238 break; 238 break;
239 case 't': /* timeout period */ 239 case 't': /* timeout period */
240 timeout_interval = atoi (optarg); 240 timeout_interval = atoi(optarg);
241 break; 241 break;
242 case 'v': /* verbose mode */ 242 case 'v': /* verbose mode */
243 verbose++; 243 verbose++;
244 break; 244 break;
245 case '4': /* IPv4 only */ 245 case '4': /* IPv4 only */
246 address_family = AF_INET; 246 address_family = AF_INET;
247 break; 247 break;
248 case '6': /* IPv6 only */ 248 case '6': /* IPv6 only */
249#ifdef USE_IPV6
250 address_family = AF_INET6; 249 address_family = AF_INET6;
251#else
252 usage (_("IPv6 support not available\n"));
253#endif
254 break; 250 break;
255 case 'H': /* hostname */ 251 case 'H': /* hostname */ {
256 ptr=optarg; 252 char *ptr = optarg;
257 while (1) { 253 while (true) {
258 n_addresses++; 254 result.config.n_addresses++;
259 if (n_addresses > max_addr) { 255 if (result.config.n_addresses > max_addr) {
260 max_addr *= 2; 256 max_addr *= 2;
261 addresses = realloc (addresses, sizeof(char*) * max_addr); 257 result.config.addresses =
262 if (addresses == NULL) 258 realloc(result.config.addresses, sizeof(char *) * max_addr);
263 die (STATE_UNKNOWN, _("Could not realloc() addresses\n")); 259 if (result.config.addresses == NULL) {
260 die(STATE_UNKNOWN, _("Could not realloc() addresses\n"));
261 }
264 } 262 }
265 addresses[n_addresses-1] = ptr; 263 result.config.addresses[result.config.n_addresses - 1] = ptr;
266 if ((ptr = index (ptr, ','))) { 264 if ((ptr = index(ptr, ','))) {
267 strcpy (ptr, ""); 265 strcpy(ptr, "");
268 ptr += sizeof(char); 266 ptr += sizeof(char);
269 } else { 267 } else {
270 break; 268 break;
271 } 269 }
272 } 270 }
271 } break;
272 case 'p': /* number of packets to send */
273 if (is_intnonneg(optarg)) {
274 result.config.max_packets = atoi(optarg);
275 } else {
276 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg);
277 }
273 break; 278 break;
274 case 'p': /* number of packets to send */ 279 case 'n': /* no HTML */
275 if (is_intnonneg (optarg)) 280 result.config.display_html = false;
276 max_packets = atoi (optarg);
277 else
278 usage2 (_("<max_packets> (%s) must be a non-negative number\n"), optarg);
279 break;
280 case 'n': /* no HTML */
281 display_html = false;
282 break; 281 break;
283 case 'L': /* show HTML */ 282 case 'L': /* show HTML */
284 display_html = true; 283 result.config.display_html = true;
285 break; 284 break;
286 case 'c': 285 case 'c':
287 get_threshold (optarg, &crta, &cpl); 286 get_threshold(optarg, &result.config.crta, &result.config.cpl);
288 break; 287 break;
289 case 'w': 288 case 'w':
290 get_threshold (optarg, &wrta, &wpl); 289 get_threshold(optarg, &result.config.wrta, &result.config.wpl);
291 break; 290 break;
292 } 291 }
293 } 292 }
294 293
295 c = optind; 294 int arg_counter = optind;
296 if (c == argc) 295 if (arg_counter == argc) {
297 return validate_arguments (); 296 return validate_arguments(result);
297 }
298 298
299 if (addresses[0] == NULL) { 299 if (result.config.addresses[0] == NULL) {
300 if (!is_host (argv[c])) { 300 if (!is_host(argv[arg_counter])) {
301 usage2 (_("Invalid hostname/address"), argv[c]); 301 usage2(_("Invalid hostname/address"), argv[arg_counter]);
302 } else { 302 } else {
303 addresses[0] = argv[c++]; 303 result.config.addresses[0] = argv[arg_counter++];
304 n_addresses++; 304 result.config.n_addresses++;
305 if (c == argc) 305 if (arg_counter == argc) {
306 return validate_arguments (); 306 return validate_arguments(result);
307 }
307 } 308 }
308 } 309 }
309 310
310 if (wpl == UNKNOWN_PACKET_LOSS) { 311 if (result.config.wpl == UNKNOWN_PACKET_LOSS) {
311 if (!is_intpercent (argv[c])) { 312 if (!is_intpercent(argv[arg_counter])) {
312 printf (_("<wpl> (%s) must be an integer percentage\n"), argv[c]); 313 printf(_("<wpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
313 return ERROR; 314 result.errorcode = ERROR;
314 } else { 315 return result;
315 wpl = atoi (argv[c++]); 316 }
316 if (c == argc) 317 result.config.wpl = atoi(argv[arg_counter++]);
317 return validate_arguments (); 318 if (arg_counter == argc) {
319 return validate_arguments(result);
318 } 320 }
319 } 321 }
320 322
321 if (cpl == UNKNOWN_PACKET_LOSS) { 323 if (result.config.cpl == UNKNOWN_PACKET_LOSS) {
322 if (!is_intpercent (argv[c])) { 324 if (!is_intpercent(argv[arg_counter])) {
323 printf (_("<cpl> (%s) must be an integer percentage\n"), argv[c]); 325 printf(_("<cpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
324 return ERROR; 326 result.errorcode = ERROR;
325 } else { 327 return result;
326 cpl = atoi (argv[c++]); 328 }
327 if (c == argc) 329 result.config.cpl = atoi(argv[arg_counter++]);
328 return validate_arguments (); 330 if (arg_counter == argc) {
331 return validate_arguments(result);
329 } 332 }
330 } 333 }
331 334
332 if (wrta < 0.0) { 335 if (result.config.wrta < 0.0) {
333 if (is_negative (argv[c])) { 336 if (is_negative(argv[arg_counter])) {
334 printf (_("<wrta> (%s) must be a non-negative number\n"), argv[c]); 337 printf(_("<wrta> (%s) must be a non-negative number\n"), argv[arg_counter]);
335 return ERROR; 338 result.errorcode = ERROR;
336 } else { 339 return result;
337 wrta = atof (argv[c++]); 340 }
338 if (c == argc) 341 result.config.wrta = atof(argv[arg_counter++]);
339 return validate_arguments (); 342 if (arg_counter == argc) {
343 return validate_arguments(result);
340 } 344 }
341 } 345 }
342 346
343 if (crta < 0.0) { 347 if (result.config.crta < 0.0) {
344 if (is_negative (argv[c])) { 348 if (is_negative(argv[arg_counter])) {
345 printf (_("<crta> (%s) must be a non-negative number\n"), argv[c]); 349 printf(_("<crta> (%s) must be a non-negative number\n"), argv[arg_counter]);
346 return ERROR; 350 result.errorcode = ERROR;
347 } else { 351 return result;
348 crta = atof (argv[c++]); 352 }
349 if (c == argc) 353 result.config.crta = atof(argv[arg_counter++]);
350 return validate_arguments (); 354 if (arg_counter == argc) {
355 return validate_arguments(result);
351 } 356 }
352 } 357 }
353 358
354 if (max_packets == -1) { 359 if (result.config.max_packets == -1) {
355 if (is_intnonneg (argv[c])) { 360 if (is_intnonneg(argv[arg_counter])) {
356 max_packets = atoi (argv[c++]); 361 result.config.max_packets = atoi(argv[arg_counter++]);
357 } else { 362 } else {
358 printf (_("<max_packets> (%s) must be a non-negative number\n"), argv[c]); 363 printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[arg_counter]);
359 return ERROR; 364 result.errorcode = ERROR;
365 return result;
360 } 366 }
361 } 367 }
362 368
363 return validate_arguments (); 369 return validate_arguments(result);
364} 370}
365 371
366 372int get_threshold(char *arg, double *trta, int *tpl) {
367 373 if (is_intnonneg(arg) && sscanf(arg, "%lf", trta) == 1) {
368int
369get_threshold (char *arg, float *trta, int *tpl)
370{
371 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
372 return OK; 374 return OK;
373 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2) 375 }
376
377 if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%lf%*[:,]%d%%", trta, tpl) == 2) {
374 return OK; 378 return OK;
375 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 379 }
380
381 if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) {
376 return OK; 382 return OK;
383 }
377 384
378 usage2 (_("%s: Warning threshold must be integer or percentage!\n\n"), arg); 385 usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg);
379 return STATE_UNKNOWN; 386 return STATE_UNKNOWN;
380} 387}
381 388
382 389check_ping_config_wrapper validate_arguments(check_ping_config_wrapper config_wrapper) {
383 390 if (config_wrapper.config.wrta < 0.0) {
384int 391 printf(_("<wrta> was not set\n"));
385validate_arguments () 392 config_wrapper.errorcode = ERROR;
386{ 393 return config_wrapper;
387 float max_seconds;
388 int i;
389
390 if (wrta < 0.0) {
391 printf (_("<wrta> was not set\n"));
392 return ERROR;
393 } 394 }
394 else if (crta < 0.0) { 395
395 printf (_("<crta> was not set\n")); 396 if (config_wrapper.config.crta < 0.0) {
396 return ERROR; 397 printf(_("<crta> was not set\n"));
398 config_wrapper.errorcode = ERROR;
399 return config_wrapper;
397 } 400 }
398 else if (wpl == UNKNOWN_PACKET_LOSS) { 401
399 printf (_("<wpl> was not set\n")); 402 if (config_wrapper.config.wpl == UNKNOWN_PACKET_LOSS) {
400 return ERROR; 403 printf(_("<wpl> was not set\n"));
404 config_wrapper.errorcode = ERROR;
405 return config_wrapper;
401 } 406 }
402 else if (cpl == UNKNOWN_PACKET_LOSS) { 407
403 printf (_("<cpl> was not set\n")); 408 if (config_wrapper.config.cpl == UNKNOWN_PACKET_LOSS) {
404 return ERROR; 409 printf(_("<cpl> was not set\n"));
410 config_wrapper.errorcode = ERROR;
411 return config_wrapper;
405 } 412 }
406 else if (wrta > crta) { 413
407 printf (_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta); 414 if (config_wrapper.config.wrta > config_wrapper.config.crta) {
408 return ERROR; 415 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), config_wrapper.config.wrta,
416 config_wrapper.config.crta);
417 config_wrapper.errorcode = ERROR;
418 return config_wrapper;
409 } 419 }
410 else if (wpl > cpl) { 420
411 printf (_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl); 421 if (config_wrapper.config.wpl > config_wrapper.config.cpl) {
412 return ERROR; 422 printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), config_wrapper.config.wpl,
423 config_wrapper.config.cpl);
424 config_wrapper.errorcode = ERROR;
425 return config_wrapper;
413 } 426 }
414 427
415 if (max_packets == -1) 428 if (config_wrapper.config.max_packets == -1) {
416 max_packets = DEFAULT_MAX_PACKETS; 429 config_wrapper.config.max_packets = DEFAULT_MAX_PACKETS;
430 }
417 431
418 max_seconds = crta / 1000.0 * max_packets + max_packets; 432 double max_seconds = (config_wrapper.config.crta / 1000.0 * config_wrapper.config.max_packets) +
419 if (max_seconds > timeout_interval) 433 config_wrapper.config.max_packets;
420 timeout_interval = (int)max_seconds; 434 if (max_seconds > timeout_interval) {
435 timeout_interval = (unsigned int)max_seconds;
436 }
421 437
422 for (i=0; i<n_addresses; i++) { 438 for (size_t i = 0; i < config_wrapper.config.n_addresses; i++) {
423 if (!is_host(addresses[i])) 439 if (!is_host(config_wrapper.config.addresses[i])) {
424 usage2 (_("Invalid hostname/address"), addresses[i]); 440 usage2(_("Invalid hostname/address"), config_wrapper.config.addresses[i]);
441 }
425 } 442 }
426 443
427 if (n_addresses == 0) { 444 if (config_wrapper.config.n_addresses == 0) {
428 usage (_("You must specify a server address or host name")); 445 usage(_("You must specify a server address or host name"));
429 } 446 }
430 447
431 return OK; 448 return config_wrapper;
432} 449}
433 450
451ping_result run_ping(const char *cmd, const char *addr, double crta) {
452 if ((child_process = spopen(cmd)) == NULL) {
453 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
454 }
434 455
456 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
457 if (child_stderr == NULL) {
458 printf(_("Cannot open stderr for %s\n"), cmd);
459 }
435 460
436int
437run_ping (const char *cmd, const char *addr)
438{
439 char buf[MAX_INPUT_BUFFER]; 461 char buf[MAX_INPUT_BUFFER];
440 int result = STATE_UNKNOWN; 462 ping_result result = {
441 int match; 463 .state = STATE_UNKNOWN,
442 464 .packet_loss = UNKNOWN_PACKET_LOSS,
443 if ((child_process = spopen (cmd)) == NULL) 465 .round_trip_average = UNKNOWN_TRIP_TIME,
444 die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 466 };
445
446 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
447 if (child_stderr == NULL)
448 printf (_("Cannot open stderr for %s\n"), cmd);
449
450 while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) {
451 467
452 if (verbose >= 3) 468 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) {
469 if (verbose >= 3) {
453 printf("Output: %s", buf); 470 printf("Output: %s", buf);
471 }
454 472
455 result = max_state (result, error_scan (buf, addr)); 473 result.state = max_state(result.state, error_scan(buf, addr));
456 474
457 /* get the percent loss statistics */ 475 /* get the percent loss statistics */
458 match = 0; 476 int match = 0;
459 if((sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n",&pl,&match) && match) || 477 if ((sscanf(
460 (sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss%n",&pl,&match) && match) || 478 buf,
461 (sscanf(buf,"%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n",&pl,&match) && match) || 479 "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n",
462 (sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% packet loss%n",&pl,&match) && match) || 480 &result.packet_loss, &match) == 1 &&
463 (sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% loss, time%n",&pl,&match) && match) || 481 match) ||
464 (sscanf(buf,"%*d packets transmitted, %*d received, %d%% loss, time%n",&pl,&match) && match) || 482 (sscanf(buf,
465 (sscanf(buf,"%*d packets transmitted, %*d received, %d%% packet loss, time%n",&pl,&match) && match) || 483 "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet "
466 (sscanf(buf,"%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n",&pl,&match) && match) || 484 "loss%n",
467 (sscanf(buf,"%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n",&pl,&match) && match) || 485 &result.packet_loss, &match) == 1 &&
468 (sscanf(buf,"%*[^(](%d%% %*[^)])%n",&pl,&match) && match) 486 match) ||
469 ) 487 (sscanf(buf,
488 "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n",
489 &result.packet_loss, &match) == 1 &&
490 match) ||
491 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n",
492 &result.packet_loss, &match) == 1 &&
493 match) ||
494 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n",
495 &result.packet_loss, &match) == 1 &&
496 match) ||
497 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n",
498 &result.packet_loss, &match) == 1 &&
499 match) ||
500 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n",
501 &result.packet_loss, &match) == 1 &&
502 match) == 1 ||
503 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n",
504 &result.packet_loss, &match) == 1 &&
505 match) ||
506 (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n",
507 &result.packet_loss, &match) == 1 &&
508 match) ||
509 (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &result.packet_loss, &match) == 1 && match)) {
470 continue; 510 continue;
511 }
471 512
472 /* get the round trip average */ 513 /* get the round trip average */
473 else 514 if ((sscanf(buf, "round-trip min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average,
474 if((sscanf(buf,"round-trip min/avg/max = %*f/%f/%*f%n",&rta,&match) && match) || 515 &match) == 1 &&
475 (sscanf(buf,"round-trip min/avg/max/mdev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 516 match) ||
476 (sscanf(buf,"round-trip min/avg/max/sdev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 517 (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%lf/%*f/%*f%n",
477 (sscanf(buf,"round-trip min/avg/max/stddev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 518 &result.round_trip_average, &match) == 1 &&
478 (sscanf(buf,"round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 519 match) ||
479 (sscanf(buf,"round-trip (ms) min/avg/max = %*f/%f/%*f%n",&rta,&match) && match) || 520 (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%lf/%*f/%*f%n",
480 (sscanf(buf,"round-trip (ms) min/avg/max/stddev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 521 &result.round_trip_average, &match) == 1 &&
481 (sscanf(buf,"rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms%n",&rta,&match) && match) || 522 match) ||
482 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %fms%n", &rta, &match) && match) 523 (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%lf/%*f/%*f%n",
483 ) 524 &result.round_trip_average, &match) == 1 &&
525 match) ||
526 (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%lf/%*f/%*f%n",
527 &result.round_trip_average, &match) == 1 &&
528 match) ||
529 (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average,
530 &match) == 1 &&
531 match) ||
532 (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%lf/%*f/%*f%n",
533 &result.round_trip_average, &match) == 1 &&
534 match) ||
535 (sscanf(buf, "rtt min/avg/max/mdev = %*f/%lf/%*f/%*f ms%n", &result.round_trip_average,
536 &match) == 1 &&
537 match) ||
538 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %lfms%n",
539 &result.round_trip_average, &match) == 1 &&
540 match)) {
484 continue; 541 continue;
542 }
485 } 543 }
486 544
487 /* this is needed because there is no rta if all packets are lost */ 545 /* this is needed because there is no rta if all packets are lost */
488 if (pl == 100) 546 if (result.packet_loss == 100) {
489 rta = crta; 547 result.round_trip_average = crta;
548 }
490 549
491 /* check stderr, setting at least WARNING if there is output here */ 550 /* check stderr, setting at least WARNING if there is output here */
492 /* Add warning into warn_text */ 551 /* Add warning into warn_text */
493 while (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr)) { 552 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
494 if ( 553 if (!strstr(buf, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") &&
495 ! strstr(buf,"WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") 554 !strstr(buf, "Warning: time of day goes back")
496 && ! strstr(buf,"Warning: time of day goes back")
497 555
498 ) { 556 ) {
499 if (verbose >= 3) { 557 if (verbose >= 3) {
500 printf("Got stderr: %s", buf); 558 printf("Got stderr: %s", buf);
501 } 559 }
502 if ((result=error_scan(buf, addr)) == STATE_OK) { 560 if ((result.state = error_scan(buf, addr)) == STATE_OK) {
503 result = STATE_WARNING; 561 result.state = STATE_WARNING;
504 if (warn_text == NULL) { 562 if (warn_text == NULL) {
505 warn_text = strdup(_("System call sent warnings to stderr ")); 563 warn_text = strdup(_("System call sent warnings to stderr "));
506 } else { 564 } else {
507 xasprintf(&warn_text, "%s %s", warn_text, _("System call sent warnings to stderr ")); 565 xasprintf(&warn_text, "%s %s", warn_text,
566 _("System call sent warnings to stderr "));
508 } 567 }
509 } 568 }
510 } 569 }
511 } 570 }
512 571
513 (void) fclose (child_stderr); 572 (void)fclose(child_stderr);
514 573
574 spclose(child_process);
515 575
516 spclose (child_process); 576 if (warn_text == NULL) {
517
518 if (warn_text == NULL)
519 warn_text = strdup(""); 577 warn_text = strdup("");
578 }
520 579
521 return result; 580 return result;
522} 581}
523 582
583mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) {
584 if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") ||
585 strstr(buf, "No route")) {
586 die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr);
587 } else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) {
588 die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr);
589 } else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) {
590 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr);
591 } else if (strstr(buf, "Destination Protocol Unreachable")) {
592 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr);
593 } else if (strstr(buf, "Destination Net Prohibited")) {
594 die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr);
595 } else if (strstr(buf, "Destination Host Prohibited")) {
596 die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr);
597 } else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) {
598 die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr);
599 } else if (strstr(buf, "unknown host")) {
600 die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr);
601 } else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) {
602 die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr);
603 } else if (strstr(buf, "Destination unreachable: ")) {
604 die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr);
605 }
524 606
607 if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) {
608 if (warn_text == NULL) {
609 warn_text = strdup(_(WARN_DUPLICATES));
610 } else if (!strstr(warn_text, _(WARN_DUPLICATES)) &&
611 xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) {
612 die(STATE_UNKNOWN, _("Unable to realloc warn_text\n"));
613 }
614 return STATE_WARNING;
615 }
525 616
526int 617 return STATE_OK;
527error_scan (char buf[MAX_INPUT_BUFFER], const char *addr)
528{
529 if (strstr (buf, "Network is unreachable") ||
530 strstr (buf, "Destination Net Unreachable") ||
531 strstr (buf, "No route")
532 )
533 die (STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr);
534 else if (strstr (buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable"))
535 die (STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr);
536 else if (strstr (buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable"))
537 die (STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr);
538 else if (strstr (buf, "Destination Protocol Unreachable"))
539 die (STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr);
540 else if (strstr (buf, "Destination Net Prohibited"))
541 die (STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr);
542 else if (strstr (buf, "Destination Host Prohibited"))
543 die (STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr);
544 else if (strstr (buf, "Packet filtered") || strstr(buf, "Administratively prohibited"))
545 die (STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr);
546 else if (strstr (buf, "unknown host" ))
547 die (STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr);
548 else if (strstr (buf, "Time to live exceeded") || strstr(buf, "Time exceeded"))
549 die (STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr);
550 else if (strstr (buf, "Destination unreachable: "))
551 die (STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr);
552
553 if (strstr (buf, "(DUP!)") || strstr (buf, "DUPLICATES FOUND")) {
554 if (warn_text == NULL)
555 warn_text = strdup (_(WARN_DUPLICATES));
556 else if (! strstr (warn_text, _(WARN_DUPLICATES)) &&
557 xasprintf (&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1)
558 die (STATE_UNKNOWN, _("Unable to realloc warn_text\n"));
559 return (STATE_WARNING);
560 }
561
562 return (STATE_OK);
563} 618}
564 619
620void print_help(void) {
621 print_revision(progname, NP_VERSION);
565 622
623 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
624 printf(COPYRIGHT, copyright, email);
566 625
567void 626 printf(_("Use ping to check connection statistics for a remote host."));
568print_help (void)
569{
570 print_revision (progname, NP_VERSION);
571
572 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
573 printf (COPYRIGHT, copyright, email);
574
575 printf (_("Use ping to check connection statistics for a remote host."));
576 627
577 printf ("\n\n"); 628 printf("\n\n");
578 629
579 print_usage (); 630 print_usage();
580 631
581 printf (UT_HELP_VRSN); 632 printf(UT_HELP_VRSN);
582 printf (UT_EXTRA_OPTS); 633 printf(UT_EXTRA_OPTS);
583 634
584 printf (UT_IPv46); 635 printf(UT_IPv46);
585 636
586 printf (" %s\n", "-H, --hostname=HOST"); 637 printf(" %s\n", "-H, --hostname=HOST");
587 printf (" %s\n", _("host to ping")); 638 printf(" %s\n", _("host to ping"));
588 printf (" %s\n", "-w, --warning=THRESHOLD"); 639 printf(" %s\n", "-w, --warning=THRESHOLD");
589 printf (" %s\n", _("warning threshold pair")); 640 printf(" %s\n", _("warning threshold pair"));
590 printf (" %s\n", "-c, --critical=THRESHOLD"); 641 printf(" %s\n", "-c, --critical=THRESHOLD");
591 printf (" %s\n", _("critical threshold pair")); 642 printf(" %s\n", _("critical threshold pair"));
592 printf (" %s\n", "-p, --packets=INTEGER"); 643 printf(" %s\n", "-p, --packets=INTEGER");
593 printf (" %s ", _("number of ICMP ECHO packets to send")); 644 printf(" %s ", _("number of ICMP ECHO packets to send"));
594 printf (_("(Default: %d)\n"), DEFAULT_MAX_PACKETS); 645 printf(_("(Default: %d)\n"), DEFAULT_MAX_PACKETS);
595 printf (" %s\n", "-L, --link"); 646 printf(" %s\n", "-L, --link");
596 printf (" %s\n", _("show HTML in the plugin output (obsoleted by urlize)")); 647 printf(" %s\n", _("show HTML in the plugin output (obsoleted by urlize)"));
597 648
598 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 649 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
599 650
600 printf ("\n"); 651 printf("\n");
601 printf ("%s\n", _("THRESHOLD is <rta>,<pl>% where <rta> is the round trip average travel")); 652 printf("%s\n", _("THRESHOLD is <rta>,<pl>% where <rta> is the round trip average travel"));
602 printf ("%s\n", _("time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the")); 653 printf("%s\n", _("time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the"));
603 printf ("%s\n", _("percentage of packet loss to trigger an alarm state.")); 654 printf("%s\n", _("percentage of packet loss to trigger an alarm state."));
604 655
605 printf ("\n"); 656 printf("\n");
606 printf ("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss")); 657 printf("%s\n",
607 printf ("%s\n", _("(percentage) and round trip average (milliseconds). It can produce HTML output")); 658 _("This plugin uses the ping command to probe the specified host for packet loss"));
608 printf ("%s\n", _("linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in")); 659 printf("%s\n",
609 printf ("%s\n", _("the contrib area of the downloads section at http://www.nagios.org/")); 660 _("(percentage) and round trip average (milliseconds). It can produce HTML output."));
610 661
611 printf (UT_SUPPORT); 662 printf(UT_SUPPORT);
612} 663}
613 664
614void 665void print_usage(void) {
615print_usage (void) 666 printf("%s\n", _("Usage:"));
616{ 667 printf("%s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n", progname);
617 printf ("%s\n", _("Usage:")); 668 printf(" [-p packets] [-t timeout] [-4|-6]\n");
618 printf ("%s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n", progname);
619 printf (" [-p packets] [-t timeout] [-4|-6]\n");
620} 669}
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 1fcbd981..174dcd97 100644
--- a/plugins/check_procs.c
+++ b/plugins/check_procs.c
@@ -1,357 +1,339 @@
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-2008 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-2008"; 39const char *copyright = "2000-2024";
40const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
41 41
42#include "common.h" 42#include "common.h"
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
54int process_arguments (int, char **); 56typedef struct {
55int validate_arguments (void); 57 int errorcode;
56int convert_to_seconds (char *); 58 check_procs_config config;
57void 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*/);
60char *warning_range = NULL; 62
61char *critical_range = NULL; 63static int convert_to_seconds(char * /*etime*/, enum metric /*metric*/);
62thresholds *procs_thresholds = NULL; 64static void print_help(void);
63 65void print_usage(void);
64int 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 */ 83
82char *metric_name; 84static int verbose = 0;
83enum metric { 85
84 METRIC_PROCS, 86static int stat_exe(const pid_t pid, struct stat *buf) {
85 METRIC_VSZ,
86 METRIC_RSS,
87 METRIC_CPU,
88 METRIC_ELAPSED
89};
90enum metric metric = METRIC_PROCS;
91
92int verbose = 0;
93int uid;
94pid_t ppid;
95int vsz;
96int rss;
97float pcpu;
98char *statopts;
99char *prog;
100char *exclude_progs;
101char **exclude_progs_arr = NULL;
102char exclude_progs_counter = 0;
103char *args;
104char *input_filename = NULL;
105regex_t re_args;
106char *fmt;
107char *fails;
108char tmp[MAX_INPUT_BUFFER];
109int kthread_filter = 0;
110int usepid = 0; /* whether to test for pid or /proc/pid/exe */
111
112FILE *ps_input = NULL;
113
114static int
115stat_exe (const pid_t pid, struct stat *buf) {
116 char *path; 87 char *path;
117 int ret;
118 xasprintf(&path, "/proc/%d/exe", pid); 88 xasprintf(&path, "/proc/%d/exe", pid);
119 ret = stat(path, buf); 89 int ret = stat(path, buf);
120 free(path); 90 free(path);
121 return ret; 91 return ret;
122} 92}
123 93
124 94int main(int argc, char **argv) {
125int 95 setlocale(LC_ALL, "");
126main (int argc, char **argv)
127{
128 char *input_buffer;
129 char *input_line;
130 char *procprog;
131
132 pid_t mypid = 0;
133 pid_t myppid = 0;
134 struct stat statbuf;
135 dev_t mydev = 0;
136 ino_t myino = 0;
137 int procuid = 0;
138 pid_t procpid = 0;
139 pid_t procppid = 0;
140 pid_t kthread_ppid = 0;
141 int procvsz = 0;
142 int procrss = 0;
143 int procseconds = 0;
144 float procpcpu = 0;
145 char procstat[8];
146 char procetime[MAX_INPUT_BUFFER] = { '\0' };
147 char *procargs;
148
149 const char *zombie = "Z";
150
151 int resultsum = 0; /* bitmask of the filter criteria met by a process */
152 int found = 0; /* counter for number of lines returned in `ps` output */
153 int procs = 0; /* counter for number of processes meeting filter criteria */
154 int pos; /* number of spaces before 'args' in `ps` output */
155 int cols; /* number of columns in ps output */
156 int expected_cols = PS_COLS - 1;
157 int warn = 0; /* number of processes in warn state */
158 int crit = 0; /* number of processes in crit state */
159 int i = 0;
160 int result = STATE_UNKNOWN;
161 int ret = 0;
162 output chld_out, chld_err;
163
164 setlocale (LC_ALL, "");
165 bindtextdomain (PACKAGE, LOCALEDIR);
166 textdomain (PACKAGE);
167 setlocale(LC_NUMERIC, "POSIX"); 96 setlocale(LC_NUMERIC, "POSIX");
168 97 bindtextdomain(PACKAGE, LOCALEDIR);
169 input_buffer = malloc (MAX_INPUT_BUFFER); 98 textdomain(PACKAGE);
170 procprog = malloc (MAX_INPUT_BUFFER);
171
172 xasprintf (&metric_name, "PROCS");
173 metric = METRIC_PROCS;
174 99
175 /* Parse extra opts if any */ 100 /* Parse extra opts if any */
176 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 }
177 107
178 if (process_arguments (argc, argv) == ERROR) 108 check_procs_config config = tmp_config.config;
179 usage4 (_("Could not parse arguments"));
180 109
181 /* find ourself */ 110 /* find ourself */
182 mypid = getpid(); 111 pid_t mypid = getpid();
183 myppid = getppid(); 112 pid_t myppid = getppid();
184 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) {
185 /* usepid might have been set by -T */ 117 /* usepid might have been set by -T */
186 usepid = 1; 118 config.usepid = true;
187 } else { 119 } else {
188 usepid = 0; 120 config.usepid = false;
189 mydev = statbuf.st_dev; 121 mydev = statbuf.st_dev;
190 myino = statbuf.st_ino; 122 myino = statbuf.st_ino;
191 } 123 }
192 124
193 /* Set signal handling and alarm timeout */ 125 /* Set signal handling and alarm timeout */
194 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 126 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
195 die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); 127 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
196 } 128 }
197 (void) alarm ((unsigned) timeout_interval); 129 (void)alarm(timeout_interval);
198 130
199 if (verbose >= 2) 131 if (verbose >= 2) {
200 printf (_("CMD: %s\n"), PS_COMMAND); 132 printf(_("CMD: %s\n"), PS_COMMAND);
133 }
201 134
202 if (input_filename == NULL) { 135 output chld_out;
203 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);
204 if (chld_err.lines > 0) { 140 if (chld_err.lines > 0) {
205 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]);
206 exit(STATE_WARNING); 142 exit(STATE_WARNING);
207 } 143 }
208 } else { 144 } else {
209 result = cmd_file_read( input_filename, &chld_out, 0); 145 result = cmd_file_read(config.input_filename, &chld_out, 0);
210 } 146 }
211 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
212 /* flush first line: j starts at 1 */ 168 /* flush first line: j starts at 1 */
213 for (size_t j = 1; j < chld_out.lines; j++) { 169 for (size_t j = 1; j < chld_out.lines; j++) {
214 input_line = chld_out.line[j]; 170 char *input_line = chld_out.line[j];
215 171
216 if (verbose >= 3) 172 if (verbose >= 3) {
217 printf ("%s", input_line); 173 printf("%s", input_line);
174 }
218 175
219 strcpy (procprog, ""); 176 strcpy(procprog, "");
220 xasprintf (&procargs, "%s", ""); 177 char *procargs;
178 xasprintf(&procargs, "%s", "");
221 179
222 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);
223 182
224 /* Zombie processes do not give a procprog command */ 183 /* Zombie processes do not give a procprog command */
225 if ( cols < expected_cols && strstr(procstat, zombie) ) { 184 const char *zombie = "Z";
185 if (cols < expected_cols && strstr(procstat, zombie)) {
226 cols = expected_cols; 186 cols = expected_cols;
227 } 187 }
228 if ( cols >= expected_cols ) { 188 if (cols >= expected_cols) {
229 resultsum = 0; 189 resultsum = 0;
230 xasprintf (&procargs, "%s", input_line + pos); 190 xasprintf(&procargs, "%s", input_line + pos);
231 strip (procargs); 191 strip(procargs);
232 192
233 /* Some ps return full pathname for command. This removes path */ 193 /* Some ps return full pathname for command. This removes path */
234 strcpy(procprog, base_name(procprog)); 194 strcpy(procprog, base_name(procprog));
235 195
236 /* we need to convert the elapsed time to seconds */ 196 /* we need to convert the elapsed time to seconds */
237 procseconds = convert_to_seconds(procetime); 197 procseconds = convert_to_seconds(procetime, config.metric);
238 198
239 if (verbose >= 3) 199 if (verbose >= 3) {
240 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 "
241 procs, procuid, procvsz, procrss, 201 "prog=%s args=%s\n",
242 procpid, procppid, procpcpu, procstat, 202 procs, procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat,
243 procetime, procprog, procargs); 203 procetime, procprog, procargs);
204 }
244 205
245 /* Ignore self */ 206 /* Ignore self */
246 if ((usepid && mypid == procpid) || 207 int ret = 0;
247 ( ((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || 208 if ((config.usepid && mypid == procpid) ||
248 (ret == -1 && errno == ENOENT)) 209 (((!config.usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) &&
249 ) { 210 statbuf.st_dev == mydev && statbuf.st_ino == myino)) ||
250 if (verbose >= 3) 211 (ret == -1 && errno == ENOENT))) {
251 printf("not considering - is myself or gone\n"); 212 if (verbose >= 3) {
213 printf("not considering - is myself or gone\n");
214 }
252 continue; 215 continue;
253 } 216 }
254 /* Ignore parent*/ 217 /* Ignore parent*/
255 else if (myppid == procpid) { 218 if (myppid == procpid) {
256 if (verbose >= 3) 219 if (verbose >= 3) {
257 printf("not considering - is parent\n"); 220 printf("not considering - is parent\n");
221 }
258 continue; 222 continue;
259 } 223 }
260 224
261 /* Ignore our own children */ 225 /* Ignore our own children */
262 if (procppid == mypid) { 226 if (procppid == mypid) {
263 if (verbose >= 3) 227 if (verbose >= 3) {
264 printf("not considering - is our child\n"); 228 printf("not considering - is our child\n");
229 }
265 continue; 230 continue;
266 } 231 }
267 232
268 /* Ignore excluded processes by name */ 233 /* Ignore excluded processes by name */
269 if(options & EXCLUDE_PROGS) { 234 if (config.options & EXCLUDE_PROGS) {
270 int found = 0; 235 bool found = false;
271 int i = 0; 236 for (int i = 0; i < (config.exclude_progs_counter); i++) {
272 237 if (!strcmp(procprog, config.exclude_progs_arr[i])) {
273 for(i=0; i < (exclude_progs_counter); i++) { 238 found = true;
274 if(!strcmp(procprog, exclude_progs_arr[i])) { 239 }
275 found = 1; 240 }
276 } 241 if (!found) {
277 } 242 resultsum |= EXCLUDE_PROGS;
278 if(found == 0) { 243 } else {
279 resultsum |= EXCLUDE_PROGS; 244 if (verbose >= 3) {
280 } else 245 printf("excluding - by ignorelist\n");
281 { 246 }
282 if(verbose >= 3) 247 }
283 printf("excluding - by ignorelist\n");
284 }
285 } 248 }
286 249
287 /* filter kernel threads (children of KTHREAD_PARENT)*/ 250 /* filter kernel threads (children of KTHREAD_PARENT)*/
288 /* TODO adapt for other OSes than GNU/Linux 251 /* TODO adapt for other OSes than GNU/Linux
289 sorry for not doing that, but I've no other OSes to test :-( */ 252 sorry for not doing that, but I've no other OSes to test :-( */
290 if (kthread_filter == 1) { 253 if (config.kthread_filter) {
291 /* get pid KTHREAD_PARENT */ 254 /* get pid KTHREAD_PARENT */
292 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT) ) 255 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) {
293 kthread_ppid = procpid; 256 kthread_ppid = procpid;
257 }
294 258
295 if (kthread_ppid == procppid) { 259 if (kthread_ppid == procppid) {
296 if (verbose >= 2) 260 if (verbose >= 2) {
297 printf ("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs); 261 printf("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid,
262 procppid, procprog, procargs);
263 }
298 continue; 264 continue;
299 } 265 }
300 } 266 }
301 267
302 if ((options & STAT) && (strstr (procstat, statopts))) 268 if ((config.options & STAT) && (strstr(procstat, config.statopts))) {
303 resultsum |= STAT; 269 resultsum |= STAT;
304 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL)) 270 }
271 if ((config.options & ARGS) && procargs && (strstr(procargs, config.args) != NULL)) {
305 resultsum |= ARGS; 272 resultsum |= ARGS;
306 if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t) 0, NULL, 0) == 0)) 273 }
274 if ((config.options & EREG_ARGS) && procargs &&
275 (regexec(&config.re_args, procargs, (size_t)0, NULL, 0) == 0)) {
307 resultsum |= EREG_ARGS; 276 resultsum |= EREG_ARGS;
308 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0)) 277 }
278 if ((config.options & PROG) && procprog && (strcmp(config.prog, procprog) == 0)) {
309 resultsum |= PROG; 279 resultsum |= PROG;
310 if ((options & PPID) && (procppid == ppid)) 280 }
281 if ((config.options & PPID) && (procppid == config.ppid)) {
311 resultsum |= PPID; 282 resultsum |= PPID;
312 if ((options & USER) && (procuid == uid)) 283 }
284 if ((config.options & USER) && (procuid == config.uid)) {
313 resultsum |= USER; 285 resultsum |= USER;
314 if ((options & VSZ) && (procvsz >= vsz)) 286 }
287 if ((config.options & VSZ) && (procvsz >= config.vsz)) {
315 resultsum |= VSZ; 288 resultsum |= VSZ;
316 if ((options & RSS) && (procrss >= rss)) 289 }
290 if ((config.options & RSS) && (procrss >= config.rss)) {
317 resultsum |= RSS; 291 resultsum |= RSS;
318 if ((options & PCPU) && (procpcpu >= pcpu)) 292 }
293 if ((config.options & PCPU) && (procpcpu >= config.pcpu)) {
319 resultsum |= PCPU; 294 resultsum |= PCPU;
295 }
320 296
321 found++; 297 found++;
322 298
323 /* Next line if filters not matched */ 299 /* Next line if filters not matched */
324 if (!(options == resultsum || options == ALL)) 300 if (!(config.options == resultsum || config.options == ALL)) {
325 continue; 301 continue;
302 }
326 303
327 procs++; 304 procs++;
328 if (verbose >= 2) { 305 if (verbose >= 2) {
329 printf ("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 306 printf("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s "
330 procuid, procvsz, procrss, 307 "prog=%s args=%s\n",
331 procpid, procppid, procpcpu, procstat, 308 procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat, procetime,
332 procetime, procprog, procargs); 309 procprog, procargs);
333 } 310 }
334 311
335 if (metric == METRIC_VSZ) 312 mp_state_enum temporary_result = STATE_OK;
336 i = get_status ((double)procvsz, procs_thresholds); 313 if (config.metric == METRIC_VSZ) {
337 else if (metric == METRIC_RSS) 314 temporary_result = get_status((double)procvsz, config.procs_thresholds);
338 i = get_status ((double)procrss, procs_thresholds); 315 } else if (config.metric == METRIC_RSS) {
316 temporary_result = get_status((double)procrss, config.procs_thresholds);
317 }
339 /* TODO? float thresholds for --metric=CPU */ 318 /* TODO? float thresholds for --metric=CPU */
340 else if (metric == METRIC_CPU) 319 else if (config.metric == METRIC_CPU) {
341 i = get_status (procpcpu, procs_thresholds); 320 temporary_result = get_status(procpcpu, config.procs_thresholds);
342 else if (metric == METRIC_ELAPSED) 321 } else if (config.metric == METRIC_ELAPSED) {
343 i = get_status ((double)procseconds, procs_thresholds); 322 temporary_result = get_status((double)procseconds, config.procs_thresholds);
323 }
344 324
345 if (metric != METRIC_PROCS) { 325 if (config.metric != METRIC_PROCS) {
346 if (i == STATE_WARNING) { 326 if (temporary_result == STATE_WARNING) {
347 warn++; 327 warn++;
348 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 328 xasprintf(&config.fails, "%s%s%s", config.fails,
349 result = max_state (result, i); 329 (strcmp(config.fails, "") ? ", " : ""), procprog);
330 result = max_state(result, temporary_result);
350 } 331 }
351 if (i == STATE_CRITICAL) { 332 if (temporary_result == STATE_CRITICAL) {
352 crit++; 333 crit++;
353 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 334 xasprintf(&config.fails, "%s%s%s", config.fails,
354 result = max_state (result, i); 335 (strcmp(config.fails, "") ? ", " : ""), procprog);
336 result = max_state(result, temporary_result);
355 } 337 }
356 } 338 }
357 } 339 }
@@ -361,339 +343,366 @@ main (int argc, char **argv)
361 } 343 }
362 } 344 }
363 345
364 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ 346 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
365 printf (_("Unable to read output\n")); 347 printf(_("Unable to read output\n"));
366 return STATE_UNKNOWN; 348 return STATE_UNKNOWN;
367 } 349 }
368 350
369 if ( result == STATE_UNKNOWN ) 351 if (result == STATE_UNKNOWN) {
370 result = STATE_OK; 352 result = STATE_OK;
353 }
371 354
372 /* Needed if procs found, but none match filter */ 355 /* Needed if procs found, but none match filter */
373 if ( metric == METRIC_PROCS ) { 356 if (config.metric == METRIC_PROCS) {
374 result = max_state (result, get_status ((double)procs, procs_thresholds) ); 357 result = max_state(result, get_status((double)procs, config.procs_thresholds));
375 } 358 }
376 359
377 if ( result == STATE_OK ) { 360 if (result == STATE_OK) {
378 printf ("%s %s: ", metric_name, _("OK")); 361 printf("%s %s: ", config.metric_name, _("OK"));
379 } else if (result == STATE_WARNING) { 362 } else if (result == STATE_WARNING) {
380 printf ("%s %s: ", metric_name, _("WARNING")); 363 printf("%s %s: ", config.metric_name, _("WARNING"));
381 if ( metric != METRIC_PROCS ) { 364 if (config.metric != METRIC_PROCS) {
382 printf (_("%d warn out of "), warn); 365 printf(_("%d warn out of "), warn);
383 } 366 }
384 } else if (result == STATE_CRITICAL) { 367 } else if (result == STATE_CRITICAL) {
385 printf ("%s %s: ", metric_name, _("CRITICAL")); 368 printf("%s %s: ", config.metric_name, _("CRITICAL"));
386 if (metric != METRIC_PROCS) { 369 if (config.metric != METRIC_PROCS) {
387 printf (_("%d crit, %d warn out of "), crit, warn); 370 printf(_("%d crit, %d warn out of "), crit, warn);
388 } 371 }
389 } 372 }
390 printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs); 373 printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs);
391 374
392 if (strcmp(fmt,"") != 0) { 375 if (strcmp(config.fmt, "") != 0) {
393 printf (_(" with %s"), fmt); 376 printf(_(" with %s"), config.fmt);
394 } 377 }
395 378
396 if ( verbose >= 1 && strcmp(fails,"") ) 379 if (verbose >= 1 && strcmp(config.fails, "")) {
397 printf (" [%s]", fails); 380 printf(" [%s]", config.fails);
381 }
398 382
399 if (metric == METRIC_PROCS) 383 if (config.metric == METRIC_PROCS) {
400 printf (" | procs=%d;%s;%s;0;", procs, 384 printf(" | procs=%d;%s;%s;0;", procs, config.warning_range ? config.warning_range : "",
401 warning_range ? warning_range : "", 385 config.critical_range ? config.critical_range : "");
402 critical_range ? critical_range : ""); 386 } else {
403 else 387 printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit);
404 printf (" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); 388 }
405 389
406 printf ("\n"); 390 printf("\n");
407 return result; 391 exit(result);
408} 392}
409 393
410
411
412/* process command-line arguments */ 394/* process command-line arguments */
413int 395check_procs_config_wrapper process_arguments(int argc, char **argv) {
414process_arguments (int argc, char **argv) 396 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
415{ 397 {"critical", required_argument, 0, 'c'},
416 int c = 1; 398 {"metric", required_argument, 0, 'm'},
417 char *user; 399 {"timeout", required_argument, 0, 't'},
418 struct passwd *pw; 400 {"status", required_argument, 0, 's'},
419 int option = 0; 401 {"ppid", required_argument, 0, 'p'},
420 int err; 402 {"user", required_argument, 0, 'u'},
421 int cflags = REG_NOSUB | REG_EXTENDED; 403 {"command", required_argument, 0, 'C'},
422 char errbuf[MAX_INPUT_BUFFER]; 404 {"vsz", required_argument, 0, 'z'},
423 char *temp_string; 405 {"rss", required_argument, 0, 'r'},
424 int i=0; 406 {"pcpu", required_argument, 0, 'P'},
425 static struct option longopts[] = { 407 {"elapsed", required_argument, 0, 'e'},
426 {"warning", required_argument, 0, 'w'}, 408 {"argument-array", required_argument, 0, 'a'},
427 {"critical", required_argument, 0, 'c'}, 409 {"help", no_argument, 0, 'h'},
428 {"metric", required_argument, 0, 'm'}, 410 {"version", no_argument, 0, 'V'},
429 {"timeout", required_argument, 0, 't'}, 411 {"verbose", no_argument, 0, 'v'},
430 {"status", required_argument, 0, 's'}, 412 {"ereg-argument-array", required_argument, 0, CHAR_MAX + 1},
431 {"ppid", required_argument, 0, 'p'}, 413 {"input-file", required_argument, 0, CHAR_MAX + 2},
432 {"user", required_argument, 0, 'u'}, 414 {"no-kthreads", required_argument, 0, 'k'},
433 {"command", required_argument, 0, 'C'}, 415 {"traditional-filter", no_argument, 0, 'T'},
434 {"vsz", required_argument, 0, 'z'}, 416 {"exclude-process", required_argument, 0, 'X'},
435 {"rss", required_argument, 0, 'r'}, 417 {0, 0, 0, 0}};
436 {"pcpu", required_argument, 0, 'P'}, 418
437 {"elapsed", required_argument, 0, 'e'}, 419 for (int index = 1; index < argc; index++) {
438 {"argument-array", required_argument, 0, 'a'}, 420 if (strcmp("-to", argv[index]) == 0) {
439 {"help", no_argument, 0, 'h'}, 421 strcpy(argv[index], "-t");
440 {"version", no_argument, 0, 'V'}, 422 }
441 {"verbose", no_argument, 0, 'v'}, 423 }
442 {"ereg-argument-array", required_argument, 0, CHAR_MAX+1},
443 {"input-file", required_argument, 0, CHAR_MAX+2},
444 {"no-kthreads", required_argument, 0, 'k'},
445 {"traditional-filter", no_argument, 0, 'T'},
446 {"exclude-process", required_argument, 0, 'X'},
447 {0, 0, 0, 0}
448 };
449 424
450 for (c = 1; c < argc; c++) 425 check_procs_config_wrapper result = {
451 if (strcmp ("-to", argv[c]) == 0) 426 .errorcode = OK,
452 strcpy (argv[c], "-t"); 427 .config = check_procs_config_init(),
428 };
453 429
454 while (1) { 430 while (true) {
455 c = getopt_long (argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", 431 int option = 0;
456 longopts, &option); 432 int option_index =
433 getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:TX:", longopts, &option);
457 434
458 if (c == -1 || c == EOF) 435 if (CHECK_EOF(option_index)) {
459 break; 436 break;
437 }
460 438
461 switch (c) { 439 switch (option_index) {
462 case '?': /* help */ 440 case '?': /* help */
463 usage5 (); 441 usage5();
464 case 'h': /* help */ 442 case 'h': /* help */
465 print_help (); 443 print_help();
466 exit (STATE_UNKNOWN); 444 exit(STATE_UNKNOWN);
467 case 'V': /* version */ 445 case 'V': /* version */
468 print_revision (progname, NP_VERSION); 446 print_revision(progname, NP_VERSION);
469 exit (STATE_UNKNOWN); 447 exit(STATE_UNKNOWN);
470 case 't': /* timeout period */ 448 case 't': /* timeout period */
471 if (!is_integer (optarg)) 449 if (!is_integer(optarg)) {
472 usage2 (_("Timeout interval must be a positive integer"), optarg); 450 usage2(_("Timeout interval must be a positive integer"), optarg);
473 else 451 } else {
474 timeout_interval = atoi (optarg); 452 timeout_interval = atoi(optarg);
453 }
475 break; 454 break;
476 case 'c': /* critical threshold */ 455 case 'c': /* critical threshold */
477 critical_range = optarg; 456 result.config.critical_range = optarg;
478 break; 457 break;
479 case 'w': /* warning threshold */ 458 case 'w': /* warning threshold */
480 warning_range = optarg; 459 result.config.warning_range = optarg;
481 break; 460 break;
482 case 'p': /* process id */ 461 case 'p': { /* process id */
483 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) { 462 static char tmp[MAX_INPUT_BUFFER];
484 xasprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid); 463 if (sscanf(optarg, "%d%[^0-9]", &result.config.ppid, tmp) == 1) {
485 options |= PPID; 464 xasprintf(&result.config.fmt, "%s%sPPID = %d",
465 (result.config.fmt ? result.config.fmt : ""),
466 (result.config.options ? ", " : ""), result.config.ppid);
467 result.config.options |= PPID;
486 break; 468 break;
487 } 469 }
488 usage4 (_("Parent Process ID must be an integer!")); 470 usage4(_("Parent Process ID must be an integer!"));
489 case 's': /* status */ 471 }
490 if (statopts) 472 case 's': /* status */
473 if (result.config.statopts) {
491 break; 474 break;
492 else 475 } else {
493 statopts = optarg; 476 result.config.statopts = optarg;
494 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 477 }
495 options |= STAT; 478 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"),
479 (result.config.fmt ? result.config.fmt : ""),
480 (result.config.options ? ", " : ""), result.config.statopts);
481 result.config.options |= STAT;
496 break; 482 break;
497 case 'u': /* user or user id */ 483 case 'u': /* user or user id */ {
498 if (is_integer (optarg)) { 484 struct passwd *pw;
499 uid = atoi (optarg); 485 if (is_integer(optarg)) {
500 pw = getpwuid ((uid_t) uid); 486 result.config.uid = atoi(optarg);
487 pw = getpwuid(result.config.uid);
501 /* check to be sure user exists */ 488 /* check to be sure user exists */
502 if (pw == NULL) 489 if (pw == NULL) {
503 usage2 (_("UID was not found"), optarg); 490 usage2(_("UID was not found"), optarg);
504 } 491 }
505 else { 492 } else {
506 pw = getpwnam (optarg); 493 pw = getpwnam(optarg);
507 /* check to be sure user exists */ 494 /* check to be sure user exists */
508 if (pw == NULL) 495 if (pw == NULL) {
509 usage2 (_("User name was not found"), optarg); 496 usage2(_("User name was not found"), optarg);
497 }
510 /* then get uid */ 498 /* then get uid */
511 uid = pw->pw_uid; 499 result.config.uid = pw->pw_uid;
512 } 500 }
513 user = pw->pw_name; 501
514 xasprintf (&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), 502 char *user = pw->pw_name;
515 uid, user); 503 xasprintf(&result.config.fmt, "%s%sUID = %d (%s)",
516 options |= USER; 504 (result.config.fmt ? result.config.fmt : ""),
517 break; 505 (result.config.options ? ", " : ""), result.config.uid, user);
518 case 'C': /* command */ 506 result.config.options |= USER;
507 } break;
508 case 'C': /* command */
519 /* TODO: allow this to be passed in with --metric */ 509 /* TODO: allow this to be passed in with --metric */
520 if (prog) 510 if (result.config.prog) {
521 break; 511 break;
522 else 512 } else {
523 prog = optarg; 513 result.config.prog = optarg;
524 xasprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 514 }
525 prog); 515 xasprintf(&result.config.fmt, _("%s%scommand name '%s'"),
526 options |= PROG; 516 (result.config.fmt ? result.config.fmt : ""),
517 (result.config.options ? ", " : ""), result.config.prog);
518 result.config.options |= PROG;
527 break; 519 break;
528 case 'X': 520 case 'X':
529 if(exclude_progs) 521 if (result.config.exclude_progs) {
530 break; 522 break;
531 else 523 } else {
532 exclude_progs = optarg; 524 result.config.exclude_progs = optarg;
533 xasprintf (&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 525 }
534 exclude_progs); 526 xasprintf(&result.config.fmt, _("%s%sexclude progs '%s'"),
535 char *p = strtok(exclude_progs, ","); 527 (result.config.fmt ? result.config.fmt : ""),
536 528 (result.config.options ? ", " : ""), result.config.exclude_progs);
537 while(p){ 529 char *tmp_pointer = strtok(result.config.exclude_progs, ",");
538 exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char*) * ++exclude_progs_counter); 530
539 exclude_progs_arr[exclude_progs_counter-1] = p; 531 while (tmp_pointer) {
540 p = strtok(NULL, ","); 532 result.config.exclude_progs_arr =
533 realloc(result.config.exclude_progs_arr,
534 sizeof(char *) * ++result.config.exclude_progs_counter);
535 result.config.exclude_progs_arr[result.config.exclude_progs_counter - 1] =
536 tmp_pointer;
537 tmp_pointer = strtok(NULL, ",");
541 } 538 }
542 539
543 options |= EXCLUDE_PROGS; 540 result.config.options |= EXCLUDE_PROGS;
544 break; 541 break;
545 case 'a': /* args (full path name with args) */ 542 case 'a': /* args (full path name with args) */
546 /* TODO: allow this to be passed in with --metric */ 543 /* TODO: allow this to be passed in with --metric */
547 if (args) 544 if (result.config.args) {
548 break; 545 break;
549 else 546 } else {
550 args = optarg; 547 result.config.args = optarg;
551 xasprintf (&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); 548 }
552 options |= ARGS; 549 xasprintf(&result.config.fmt, "%s%sargs '%s'",
550 (result.config.fmt ? result.config.fmt : ""),
551 (result.config.options ? ", " : ""), result.config.args);
552 result.config.options |= ARGS;
553 break; 553 break;
554 case CHAR_MAX+1: 554 case CHAR_MAX + 1: {
555 err = regcomp(&re_args, optarg, cflags); 555 int cflags = REG_NOSUB | REG_EXTENDED;
556 int err = regcomp(&result.config.re_args, optarg, cflags);
556 if (err != 0) { 557 if (err != 0) {
557 regerror (err, &re_args, errbuf, MAX_INPUT_BUFFER); 558 char errbuf[MAX_INPUT_BUFFER];
558 die (STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 559 regerror(err, &result.config.re_args, errbuf, MAX_INPUT_BUFFER);
560 die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"),
561 _("Could not compile regular expression"), errbuf);
559 } 562 }
560 /* Strip off any | within the regex optarg */ 563 /* Strip off any | within the regex optarg */
561 temp_string = strdup(optarg); 564 char *temp_string = strdup(optarg);
562 while(temp_string[i]!='\0'){ 565 int index = 0;
563 if(temp_string[i]=='|') 566 while (temp_string[index] != '\0') {
564 temp_string[i]=','; 567 if (temp_string[index] == '|') {
565 i++; 568 temp_string[index] = ',';
566 } 569 }
567 xasprintf (&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); 570 index++;
568 options |= EREG_ARGS; 571 }
569 break; 572 xasprintf(&result.config.fmt, "%s%sregex args '%s'",
570 case 'r': /* RSS */ 573 (result.config.fmt ? result.config.fmt : ""),
571 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) { 574 (result.config.options ? ", " : ""), temp_string);
572 xasprintf (&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); 575 result.config.options |= EREG_ARGS;
573 options |= RSS; 576 } break;
577 case 'r': { /* RSS */
578 static char tmp[MAX_INPUT_BUFFER];
579 if (sscanf(optarg, "%d%[^0-9]", &result.config.rss, tmp) == 1) {
580 xasprintf(&result.config.fmt, "%s%sRSS >= %d",
581 (result.config.fmt ? result.config.fmt : ""),
582 (result.config.options ? ", " : ""), result.config.rss);
583 result.config.options |= RSS;
574 break; 584 break;
575 } 585 }
576 usage4 (_("RSS must be an integer!")); 586 usage4(_("RSS must be an integer!"));
577 case 'z': /* VSZ */ 587 }
578 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) { 588 case 'z': { /* VSZ */
579 xasprintf (&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); 589 static char tmp[MAX_INPUT_BUFFER];
580 options |= VSZ; 590 if (sscanf(optarg, "%d%[^0-9]", &result.config.vsz, tmp) == 1) {
591 xasprintf(&result.config.fmt, "%s%sVSZ >= %d",
592 (result.config.fmt ? result.config.fmt : ""),
593 (result.config.options ? ", " : ""), result.config.vsz);
594 result.config.options |= VSZ;
581 break; 595 break;
582 } 596 }
583 usage4 (_("VSZ must be an integer!")); 597 usage4(_("VSZ must be an integer!"));
584 case 'P': /* PCPU */ 598 }
599 case 'P': { /* PCPU */
585 /* TODO: -P 1.5.5 is accepted */ 600 /* TODO: -P 1.5.5 is accepted */
586 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { 601 static char tmp[MAX_INPUT_BUFFER];
587 xasprintf (&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); 602 if (sscanf(optarg, "%f%[^0-9.]", &result.config.pcpu, tmp) == 1) {
588 options |= PCPU; 603 xasprintf(&result.config.fmt, "%s%sPCPU >= %.2f",
604 (result.config.fmt ? result.config.fmt : ""),
605 (result.config.options ? ", " : ""), result.config.pcpu);
606 result.config.options |= PCPU;
589 break; 607 break;
590 } 608 }
591 usage4 (_("PCPU must be a float!")); 609 usage4(_("PCPU must be a float!"));
610 }
592 case 'm': 611 case 'm':
593 xasprintf (&metric_name, "%s", optarg); 612 xasprintf(&result.config.metric_name, "%s", optarg);
594 if ( strcmp(optarg, "PROCS") == 0) { 613 if (strcmp(optarg, "PROCS") == 0) {
595 metric = METRIC_PROCS; 614 result.config.metric = METRIC_PROCS;
596 break; 615 break;
597 } 616 }
598 else if ( strcmp(optarg, "VSZ") == 0) { 617 if (strcmp(optarg, "VSZ") == 0) {
599 metric = METRIC_VSZ; 618 result.config.metric = METRIC_VSZ;
600 break; 619 break;
601 } 620 }
602 else if ( strcmp(optarg, "RSS") == 0 ) { 621 if (strcmp(optarg, "RSS") == 0) {
603 metric = METRIC_RSS; 622 result.config.metric = METRIC_RSS;
604 break; 623 break;
605 } 624 }
606 else if ( strcmp(optarg, "CPU") == 0 ) { 625 if (strcmp(optarg, "CPU") == 0) {
607 metric = METRIC_CPU; 626 result.config.metric = METRIC_CPU;
608 break; 627 break;
609 } 628 }
610 else if ( strcmp(optarg, "ELAPSED") == 0) { 629 if (strcmp(optarg, "ELAPSED") == 0) {
611 metric = METRIC_ELAPSED; 630 result.config.metric = METRIC_ELAPSED;
612 break; 631 break;
613 } 632 }
614 633
615 usage4 (_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); 634 usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!"));
616 case 'k': /* linux kernel thread filter */ 635 case 'k': /* linux kernel thread filter */
617 kthread_filter = 1; 636 result.config.kthread_filter = true;
618 break; 637 break;
619 case 'v': /* command */ 638 case 'v': /* command */
620 verbose++; 639 verbose++;
621 break; 640 break;
622 case 'T': 641 case 'T':
623 usepid = 1; 642 result.config.usepid = true;
624 break; 643 break;
625 case CHAR_MAX+2: 644 case CHAR_MAX + 2:
626 input_filename = optarg; 645 result.config.input_filename = optarg;
627 break; 646 break;
628 } 647 }
629 } 648 }
630 649
631 c = optind; 650 int index = optind;
632 if ((! warning_range) && argv[c]) 651 if ((!result.config.warning_range) && argv[index]) {
633 warning_range = argv[c++]; 652 result.config.warning_range = argv[index++];
634 if ((! critical_range) && argv[c]) 653 }
635 critical_range = argv[c++]; 654 if ((!result.config.critical_range) && argv[index]) {
636 if (statopts == NULL && argv[c]) { 655 result.config.critical_range = argv[index++];
637 xasprintf (&statopts, "%s", argv[c++]); 656 }
638 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 657 if (result.config.statopts == NULL && argv[index]) {
639 options |= STAT; 658 xasprintf(&result.config.statopts, "%s", argv[index++]);
659 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"),
660 (result.config.fmt ? result.config.fmt : ""), (result.config.options ? ", " : ""),
661 result.config.statopts);
662 result.config.options |= STAT;
640 } 663 }
641 664
642 /* this will abort in case of invalid ranges */ 665 /* this will abort in case of invalid ranges */
643 set_thresholds (&procs_thresholds, warning_range, critical_range); 666 set_thresholds(&result.config.procs_thresholds, result.config.warning_range,
667 result.config.critical_range);
644 668
645 return validate_arguments (); 669 return validate_arguments(result);
646} 670}
647 671
672check_procs_config_wrapper validate_arguments(check_procs_config_wrapper config_wrapper) {
673 if (config_wrapper.config.options == 0) {
674 config_wrapper.config.options = ALL;
675 }
648 676
677 if (config_wrapper.config.statopts == NULL) {
678 config_wrapper.config.statopts = strdup("");
679 }
649 680
650int 681 if (config_wrapper.config.prog == NULL) {
651validate_arguments () 682 config_wrapper.config.prog = strdup("");
652{ 683 }
653 if (options == 0)
654 options = ALL;
655
656 if (statopts==NULL)
657 statopts = strdup("");
658
659 if (prog==NULL)
660 prog = strdup("");
661 684
662 if (args==NULL) 685 if (config_wrapper.config.args == NULL) {
663 args = strdup(""); 686 config_wrapper.config.args = strdup("");
687 }
664 688
665 if (fmt==NULL) 689 if (config_wrapper.config.fmt == NULL) {
666 fmt = strdup(""); 690 config_wrapper.config.fmt = strdup("");
691 }
667 692
668 if (fails==NULL) 693 if (config_wrapper.config.fails == NULL) {
669 fails = strdup(""); 694 config_wrapper.config.fails = strdup("");
695 }
670 696
671 return options; 697 // return options;
698 return config_wrapper;
672} 699}
673 700
674
675/* convert the elapsed time to seconds */ 701/* convert the elapsed time to seconds */
676int 702int convert_to_seconds(char *etime, enum metric metric) {
677convert_to_seconds(char *etime) { 703 int hyphcnt = 0;
678 704 int coloncnt = 0;
679 char *ptr; 705 for (char *ptr = etime; *ptr != '\0'; ptr++) {
680 int total;
681
682 int hyphcnt;
683 int coloncnt;
684 int days;
685 int hours;
686 int minutes;
687 int seconds;
688
689 hyphcnt = 0;
690 coloncnt = 0;
691 days = 0;
692 hours = 0;
693 minutes = 0;
694 seconds = 0;
695
696 for (ptr = etime; *ptr != '\0'; ptr++) {
697 706
698 if (*ptr == '-') { 707 if (*ptr == '-') {
699 hyphcnt++; 708 hyphcnt++;
@@ -705,9 +714,12 @@ convert_to_seconds(char *etime) {
705 } 714 }
706 } 715 }
707 716
717 int days = 0;
718 int hours = 0;
719 int minutes = 0;
720 int seconds = 0;
708 if (hyphcnt > 0) { 721 if (hyphcnt > 0) {
709 sscanf(etime, "%d-%d:%d:%d", 722 sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds);
710 &days, &hours, &minutes, &seconds);
711 /* linux 2.6.5/2.6.6 reporting some processes with infinite 723 /* linux 2.6.5/2.6.6 reporting some processes with infinite
712 * elapsed times for some reason */ 724 * elapsed times for some reason */
713 if (days == 49710) { 725 if (days == 49710) {
@@ -715,135 +727,129 @@ convert_to_seconds(char *etime) {
715 } 727 }
716 } else { 728 } else {
717 if (coloncnt == 2) { 729 if (coloncnt == 2) {
718 sscanf(etime, "%d:%d:%d", 730 sscanf(etime, "%d:%d:%d", &hours, &minutes, &seconds);
719 &hours, &minutes, &seconds);
720 } else if (coloncnt == 1) { 731 } else if (coloncnt == 1) {
721 sscanf(etime, "%d:%d", 732 sscanf(etime, "%d:%d", &minutes, &seconds);
722 &minutes, &seconds);
723 } 733 }
724 } 734 }
725 735
726 total = (days * 86400) + 736 int total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds;
727 (hours * 3600) +
728 (minutes * 60) +
729 seconds;
730 737
731 if (verbose >= 3 && metric == METRIC_ELAPSED) { 738 if (verbose >= 3 && metric == METRIC_ELAPSED) {
732 printf("seconds: %d\n", total); 739 printf("seconds: %d\n", total);
733 } 740 }
734 return total; 741 return total;
735} 742}
736 743
737 744void print_help(void) {
738void 745 print_revision(progname, NP_VERSION);
739print_help (void) 746
740{ 747 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
741 print_revision (progname, NP_VERSION); 748 printf(COPYRIGHT, copyright, email);
742 749
743 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 750 printf("%s\n",
744 printf (COPYRIGHT, copyright, email); 751 _("Checks all processes and generates WARNING or CRITICAL states if the specified"));
745 752 printf("%s\n",
746 printf ("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified")); 753 _("metric is outside the required threshold ranges. The metric defaults to number"));
747 printf ("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); 754 printf("%s\n",
748 printf ("%s\n", _("of processes. Search filters can be applied to limit the processes to check.")); 755 _("of processes. Search filters can be applied to limit the processes to check."));
749 756
750 printf ("\n\n"); 757 printf("\n\n");
751 758
752 printf ("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); 759 printf("%s\n",
753 printf ("%s\n", _("are excluded from any checks to prevent false positives.")); 760 _("The parent process, check_procs itself and any child process of check_procs (ps)"));
754 761 printf("%s\n", _("are excluded from any checks to prevent false positives."));
755 printf ("\n\n"); 762
756 763 printf("\n\n");
757 print_usage (); 764
758 765 print_usage();
759 printf (UT_HELP_VRSN); 766
760 printf (UT_EXTRA_OPTS); 767 printf(UT_HELP_VRSN);
761 printf (" %s\n", "-w, --warning=RANGE"); 768 printf(UT_EXTRA_OPTS);
762 printf (" %s\n", _("Generate warning state if metric is outside this range")); 769 printf(" %s\n", "-w, --warning=RANGE");
763 printf (" %s\n", "-c, --critical=RANGE"); 770 printf(" %s\n", _("Generate warning state if metric is outside this range"));
764 printf (" %s\n", _("Generate critical state if metric is outside this range")); 771 printf(" %s\n", "-c, --critical=RANGE");
765 printf (" %s\n", "-m, --metric=TYPE"); 772 printf(" %s\n", _("Generate critical state if metric is outside this range"));
766 printf (" %s\n", _("Check thresholds against metric. Valid types:")); 773 printf(" %s\n", "-m, --metric=TYPE");
767 printf (" %s\n", _("PROCS - number of processes (default)")); 774 printf(" %s\n", _("Check thresholds against metric. Valid types:"));
768 printf (" %s\n", _("VSZ - virtual memory size")); 775 printf(" %s\n", _("PROCS - number of processes (default)"));
769 printf (" %s\n", _("RSS - resident set memory size")); 776 printf(" %s\n", _("VSZ - virtual memory size"));
770 printf (" %s\n", _("CPU - percentage CPU")); 777 printf(" %s\n", _("RSS - resident set memory size"));
778 printf(" %s\n", _("CPU - percentage CPU"));
771/* only linux etime is support currently */ 779/* only linux etime is support currently */
772#if defined( __linux__ ) 780#if defined(__linux__)
773 printf (" %s\n", _("ELAPSED - time elapsed in seconds")); 781 printf(" %s\n", _("ELAPSED - time elapsed in seconds"));
774#endif /* defined(__linux__) */ 782#endif /* defined(__linux__) */
775 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 783 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
776 784
777 printf (" %s\n", "-v, --verbose"); 785 printf(" %s\n", "-v, --verbose");
778 printf (" %s\n", _("Extra information. Up to 3 verbosity levels")); 786 printf(" %s\n", _("Extra information. Up to 3 verbosity levels"));
779 787
780 printf (" %s\n", "-T, --traditional"); 788 printf(" %s\n", "-T, --traditional");
781 printf (" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); 789 printf(" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe"));
782 790
783 printf ("\n"); 791 printf("\n");
784 printf ("%s\n", "Filters:"); 792 printf("%s\n", "Filters:");
785 printf (" %s\n", "-s, --state=STATUSFLAGS"); 793 printf(" %s\n", "-s, --state=STATUSFLAGS");
786 printf (" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); 794 printf(" %s\n", _("Only scan for processes that have, in the output of `ps`, one or"));
787 printf (" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); 795 printf(" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,"));
788 printf (" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); 796 printf(" %s\n", _("RSZDT, plus others based on the output of your 'ps' command)."));
789 printf (" %s\n", "-p, --ppid=PPID"); 797 printf(" %s\n", "-p, --ppid=PPID");
790 printf (" %s\n", _("Only scan for children of the parent process ID indicated.")); 798 printf(" %s\n", _("Only scan for children of the parent process ID indicated."));
791 printf (" %s\n", "-z, --vsz=VSZ"); 799 printf(" %s\n", "-z, --vsz=VSZ");
792 printf (" %s\n", _("Only scan for processes with VSZ higher than indicated.")); 800 printf(" %s\n", _("Only scan for processes with VSZ higher than indicated."));
793 printf (" %s\n", "-r, --rss=RSS"); 801 printf(" %s\n", "-r, --rss=RSS");
794 printf (" %s\n", _("Only scan for processes with RSS higher than indicated.")); 802 printf(" %s\n", _("Only scan for processes with RSS higher than indicated."));
795 printf (" %s\n", "-P, --pcpu=PCPU"); 803 printf(" %s\n", "-P, --pcpu=PCPU");
796 printf (" %s\n", _("Only scan for processes with PCPU higher than indicated.")); 804 printf(" %s\n", _("Only scan for processes with PCPU higher than indicated."));
797 printf (" %s\n", "-u, --user=USER"); 805 printf(" %s\n", "-u, --user=USER");
798 printf (" %s\n", _("Only scan for processes with user name or ID indicated.")); 806 printf(" %s\n", _("Only scan for processes with user name or ID indicated."));
799 printf (" %s\n", "-a, --argument-array=STRING"); 807 printf(" %s\n", "-a, --argument-array=STRING");
800 printf (" %s\n", _("Only scan for processes with args that contain STRING.")); 808 printf(" %s\n", _("Only scan for processes with args that contain STRING."));
801 printf (" %s\n", "--ereg-argument-array=STRING"); 809 printf(" %s\n", "--ereg-argument-array=STRING");
802 printf (" %s\n", _("Only scan for processes with args that contain the regex STRING.")); 810 printf(" %s\n", _("Only scan for processes with args that contain the regex STRING."));
803 printf (" %s\n", "-C, --command=COMMAND"); 811 printf(" %s\n", "-C, --command=COMMAND");
804 printf (" %s\n", _("Only scan for exact matches of COMMAND (without path).")); 812 printf(" %s\n", _("Only scan for exact matches of COMMAND (without path)."));
805 printf (" %s\n", "-X, --exclude-process"); 813 printf(" %s\n", "-X, --exclude-process");
806 printf (" %s\n", _("Exclude processes which match this comma separated list")); 814 printf(" %s\n", _("Exclude processes which match this comma separated list"));
807 printf (" %s\n", "-k, --no-kthreads"); 815 printf(" %s\n", "-k, --no-kthreads");
808 printf (" %s\n", _("Only scan for non kernel threads (works on Linux only).")); 816 printf(" %s\n", _("Only scan for non kernel threads (works on Linux only)."));
809 817
810 printf(_("\n\ 818 printf(_("\n\
811RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\ 819RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
812specified 'max:min', a warning status will be generated if the\n\ 820specified 'max:min', a warning status will be generated if the\n\
813count is inside the specified range\n\n")); 821count is inside the specified range\n\n"));
814 822
815 printf(_("\ 823 printf(_("\
816This plugin checks the number of currently running processes and\n\ 824This plugin checks the number of currently running processes and\n\
817generates WARNING or CRITICAL states if the process count is outside\n\ 825generates WARNING or CRITICAL states if the process count is outside\n\
818the specified threshold ranges. The process count can be filtered by\n\ 826the specified threshold ranges. The process count can be filtered by\n\
819process owner, parent process PID, current state (e.g., 'Z'), or may\n\ 827process owner, parent process PID, current state (e.g., 'Z'), or may\n\
820be the total number of running processes\n\n")); 828be the total number of running processes\n\n"));
821 829
822 printf ("%s\n", _("Examples:")); 830 printf("%s\n", _("Examples:"));
823 printf (" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); 831 printf(" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry");
824 printf (" %s\n", _("Warning if not two processes with command name portsentry.")); 832 printf(" %s\n", _("Warning if not two processes with command name portsentry."));
825 printf (" %s\n\n", _("Critical if < 2 or > 1024 processes")); 833 printf(" %s\n\n", _("Critical if < 2 or > 1024 processes"));
826 printf (" %s\n", "check_procs -c 1: -C sshd"); 834 printf(" %s\n", "check_procs -c 1: -C sshd");
827 printf (" %s\n", _("Critical if not at least 1 process with command sshd")); 835 printf(" %s\n", _("Critical if not at least 1 process with command sshd"));
828 printf (" %s\n", "check_procs -w 1024 -c 1: -C sshd"); 836 printf(" %s\n", "check_procs -w 1024 -c 1: -C sshd");
829 printf (" %s\n", _("Warning if > 1024 processes with command name sshd.")); 837 printf(" %s\n", _("Warning if > 1024 processes with command name sshd."));
830 printf (" %s\n\n", _("Critical if < 1 processes with command name sshd.")); 838 printf(" %s\n\n", _("Critical if < 1 processes with command name sshd."));
831 printf (" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); 839 printf(" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root");
832 printf (" %s\n", _("Warning alert if > 10 processes with command arguments containing")); 840 printf(" %s\n", _("Warning alert if > 10 processes with command arguments containing"));
833 printf (" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); 841 printf(" %s\n\n", _("'/usr/local/bin/perl' and owned by root"));
834 printf (" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); 842 printf(" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ");
835 printf (" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); 843 printf(" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K"));
836 printf (" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); 844 printf(" %s\n", "check_procs -w 10 -c 20 --metric=CPU");
837 printf (" %s\n", _("Alert if CPU of any processes over 10\% or 20\%")); 845 printf(" %s\n", _("Alert if CPU of any processes over 10%% or 20%%"));
838 846
839 printf (UT_SUPPORT); 847 printf(UT_SUPPORT);
840} 848}
841 849
842void 850void print_usage(void) {
843print_usage (void) 851 printf("%s\n", _("Usage:"));
844{ 852 printf("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname);
845 printf ("%s\n", _("Usage:")); 853 printf(" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
846 printf ("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname); 854 printf(" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
847 printf (" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
848 printf (" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
849} 855}
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 6b32710a..03153926 100644
--- a/plugins/check_radius.c
+++ b/plugins/check_radius.c
@@ -1,99 +1,96 @@
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-2008 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
31#include "output.h"
31const char *progname = "check_radius"; 32const char *progname = "check_radius";
32const char *copyright = "2000-2008"; 33const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
34 35
35#include "common.h" 36#include "common.h"
36#include "utils.h" 37#include "utils.h"
37#include "netutils.h" 38#include "netutils.h"
39#include "states.h"
40#include "check_radius.d/config.h"
38 41
39#if defined(HAVE_LIBRADCLI) 42#if defined(HAVE_LIBRADCLI)
40#include <radcli/radcli.h> 43# include <radcli/radcli.h>
41#elif defined(HAVE_LIBFREERADIUS_CLIENT) 44#elif defined(HAVE_LIBFREERADIUS_CLIENT)
42#include <freeradius-client.h> 45# include <freeradius-client.h>
43#elif defined(HAVE_LIBRADIUSCLIENT_NG) 46#elif defined(HAVE_LIBRADIUSCLIENT_NG)
44#include <radiusclient-ng.h> 47# include <radiusclient-ng.h>
45#else 48#else
46#include <radiusclient.h> 49# include <radiusclient.h>
47#endif 50#endif
48 51
49int process_arguments (int, char **); 52typedef struct {
50void print_help (void); 53 int errorcode;
51void print_usage (void); 54 check_radius_config config;
52 55} check_radius_config_wrapper;
53#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI) 56static check_radius_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
54#define my_rc_conf_str(a) rc_conf_str(rch,a) 57static void print_help(void);
55#if defined(HAVE_LIBRADCLI) 58void print_usage(void);
56#define my_rc_send_server(a,b) rc_send_server(rch,a,b,AUTH) 59
57#else 60#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
58#define my_rc_send_server(a,b) rc_send_server(rch,a,b) 61 defined(HAVE_LIBRADCLI)
59#endif 62# define my_rc_conf_str(a) rc_conf_str(rch, a)
60#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI) 63# if 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) 64# define my_rc_send_server(a, b) rc_send_server(rch, a, b, AUTH)
65# elif defined(HAVE_LIBFREERADIUS_CLIENT)
66# define my_rc_send_server(a, b) rc_send_server(rch, a, b, 0)
67# else
68# define my_rc_send_server(a, b) rc_send_server(rch, a, b)
69# endif
70# if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI)
71# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, (a)->secret, e, f)
72# else
73# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, e, f)
74# endif
75# define my_rc_avpair_add(a, b, c, d) rc_avpair_add(rch, a, b, c, -1, d)
76# define my_rc_read_dictionary(a) rc_read_dictionary(rch, a)
62#else 77#else
63#define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(rch,a,b,c,d,e,f) 78# define my_rc_conf_str(a) rc_conf_str(a)
64#endif 79# define my_rc_send_server(a, b) rc_send_server(a, b)
65#define my_rc_avpair_add(a,b,c,d) rc_avpair_add(rch,a,b,c,-1,d) 80# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(a, b, c, d, e, f)
66#define my_rc_read_dictionary(a) rc_read_dictionary(rch, a) 81# define my_rc_avpair_add(a, b, c, d) rc_avpair_add(a, b, c, d)
67#else 82# define my_rc_read_dictionary(a) rc_read_dictionary(a)
68#define my_rc_conf_str(a) rc_conf_str(a)
69#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)
71#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)
73#endif 83#endif
74 84
75/* REJECT_RC is only defined in some version of radiusclient. It has 85/* 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 */ 86 * been reported from radiusclient-ng 0.5.6 on FreeBSD 7.2-RELEASE */
77#ifndef REJECT_RC 87#ifndef REJECT_RC
78#define REJECT_RC BADRESP_RC 88# define REJECT_RC BADRESP_RC
79#endif 89#endif
80 90
81int my_rc_read_config(char *); 91static int my_rc_read_config(char * /*a*/, rc_handle ** /*rch*/);
82
83#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
84rc_handle *rch = NULL;
85#endif
86 92
87char *server = NULL; 93static bool verbose = false;
88char *username = NULL;
89char *password = NULL;
90char *nasid = NULL;
91char *nasipaddress = NULL;
92char *expect = NULL;
93char *config_file = NULL;
94unsigned short port = PW_AUTH_UDP_PORT;
95int retries = 1;
96bool verbose = false;
97 94
98/****************************************************************************** 95/******************************************************************************
99 96
@@ -148,149 +145,229 @@ Please note that all tags must be lowercase to use the DocBook XML DTD.
148-@@ 145-@@
149******************************************************************************/ 146******************************************************************************/
150 147
148int main(int argc, char **argv) {
149 setlocale(LC_ALL, "");
150 bindtextdomain(PACKAGE, LOCALEDIR);
151 textdomain(PACKAGE);
151 152
153 /* Parse extra opts if any */
154 argv = np_extra_opts(&argc, argv, progname);
155
156 check_radius_config_wrapper tmp_config = process_arguments(argc, argv);
157
158 if (tmp_config.errorcode == ERROR) {
159 usage4(_("Could not parse arguments"));
160 }
161
162 check_radius_config config = tmp_config.config;
163
164 if (config.output_format_is_set) {
165 mp_set_format(config.output_format);
166 }
167
168#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
169 defined(HAVE_LIBRADCLI)
170 rc_handle *rch = NULL;
171#endif
172
173 mp_check overall = mp_check_init();
174 mp_subcheck sc_read_config = mp_subcheck_init();
175
176 char *str = strdup("dictionary");
177 if ((config.config_file && my_rc_read_config(config.config_file, &rch)) ||
178 my_rc_read_dictionary(my_rc_conf_str(str))) {
179 sc_read_config = mp_set_subcheck_state(sc_read_config, STATE_UNKNOWN);
180 xasprintf(&sc_read_config.output, "failed to read config file");
181 mp_add_subcheck_to_check(&overall, sc_read_config);
182 mp_exit(overall);
183 }
184
185 sc_read_config = mp_set_subcheck_state(sc_read_config, STATE_OK);
186 xasprintf(&sc_read_config.output, "read config file successfully");
187 mp_add_subcheck_to_check(&overall, sc_read_config);
188
189 uint32_t service = PW_AUTHENTICATE_ONLY;
190
191 mp_subcheck sc_configuring = mp_subcheck_init();
192 SEND_DATA data;
193 memset(&data, 0, sizeof(data));
194 if (!(my_rc_avpair_add(&data.send_pairs, PW_SERVICE_TYPE, &service, 0) &&
195 my_rc_avpair_add(&data.send_pairs, PW_USER_NAME, config.username, 0) &&
196 my_rc_avpair_add(&data.send_pairs, PW_USER_PASSWORD, config.password, 0))) {
197 xasprintf(&sc_configuring.output, "Failed to the radius options: Out of Memory?");
198 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
199 mp_add_subcheck_to_check(&overall, sc_configuring);
200 mp_exit(overall);
201 }
202
203 if (config.nas_id != NULL) {
204 if (!(my_rc_avpair_add(&data.send_pairs, PW_NAS_IDENTIFIER, config.nas_id, 0))) {
205 xasprintf(&sc_configuring.output,
206 "Failed to the radius options: invalid NAS identifier?");
207 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
208 mp_add_subcheck_to_check(&overall, sc_configuring);
209 mp_exit(overall);
210 }
211 }
152 212
153int
154main (int argc, char **argv)
155{
156 struct sockaddr_storage ss;
157 char name[HOST_NAME_MAX]; 213 char name[HOST_NAME_MAX];
214 if (config.nas_ip_address == NULL) {
215 if (gethostname(name, sizeof(name)) != 0) {
216 xasprintf(&sc_configuring.output, "gethostname() failed");
217 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
218 mp_add_subcheck_to_check(&overall, sc_configuring);
219 mp_exit(overall);
220 }
221 config.nas_ip_address = name;
222 }
223
224 struct sockaddr_storage radius_server_socket;
225 if (!dns_lookup(config.nas_ip_address, &radius_server_socket, AF_UNSPEC)) {
226 xasprintf(&sc_configuring.output, "invalid NAS IP address. Lookup failed");
227 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
228 mp_add_subcheck_to_check(&overall, sc_configuring);
229 mp_exit(overall);
230 }
231
232 uint32_t client_id = ntohl(((struct sockaddr_in *)&radius_server_socket)->sin_addr.s_addr);
233 if (my_rc_avpair_add(&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL) {
234 xasprintf(&sc_configuring.output, "invalid NAS IP address. Setting option failed");
235 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
236 mp_add_subcheck_to_check(&overall, sc_configuring);
237 mp_exit(overall);
238 }
239
240 my_rc_buildreq(&data, PW_ACCESS_REQUEST, config.server, config.port, (int)timeout_interval,
241 config.retries);
242
158#ifdef RC_BUFFER_LEN 243#ifdef RC_BUFFER_LEN
159 char msg[RC_BUFFER_LEN]; 244 char msg[RC_BUFFER_LEN];
160#else 245#else
161 char msg[BUFFER_LEN]; 246 char msg[BUFFER_LEN];
162#endif 247#endif
163 SEND_DATA data;
164 int result = STATE_UNKNOWN;
165 uint32_t client_id, service;
166 char *str;
167 248
168 setlocale (LC_ALL, ""); 249 int result = my_rc_send_server(&data, msg);
169 bindtextdomain (PACKAGE, LOCALEDIR); 250 rc_avpair_free(data.send_pairs);
170 textdomain (PACKAGE); 251 if (data.receive_pairs) {
252 rc_avpair_free(data.receive_pairs);
253 }
171 254
172 /* Parse extra opts if any */ 255 mp_subcheck sc_eval = mp_subcheck_init();
173 argv=np_extra_opts (&argc, argv, progname);
174 256
175 if (process_arguments (argc, argv) == ERROR) 257 if (result == TIMEOUT_RC) {
176 usage4 (_("Could not parse arguments")); 258 xasprintf(&sc_eval.output, "timeout");
259 sc_eval = mp_set_subcheck_state(sc_eval, STATE_CRITICAL);
260 mp_add_subcheck_to_check(&overall, sc_eval);
261 mp_exit(overall);
262 }
177 263
178 str = strdup ("dictionary"); 264 if (result == ERROR_RC) {
179 if ((config_file && my_rc_read_config (config_file)) || 265 xasprintf(&sc_eval.output, "auth error");
180 my_rc_read_dictionary (my_rc_conf_str (str))) 266 sc_eval = mp_set_subcheck_state(sc_eval, STATE_CRITICAL);
181 die (STATE_UNKNOWN, _("Config file error\n")); 267 mp_add_subcheck_to_check(&overall, sc_eval);
268 mp_exit(overall);
269 }
182 270
183 service = PW_AUTHENTICATE_ONLY; 271 if (result == REJECT_RC) {
272 xasprintf(&sc_eval.output, "auth failed");
273 sc_eval = mp_set_subcheck_state(sc_eval, STATE_WARNING);
274 mp_add_subcheck_to_check(&overall, sc_eval);
275 mp_exit(overall);
276 }
184 277
185 memset (&data, 0, sizeof(data)); 278 if (result == BADRESP_RC) {
186 if (!(my_rc_avpair_add (&data.send_pairs, PW_SERVICE_TYPE, &service, 0) && 279 xasprintf(&sc_eval.output, "bad response");
187 my_rc_avpair_add (&data.send_pairs, PW_USER_NAME, username, 0) && 280 sc_eval = mp_set_subcheck_state(sc_eval, STATE_WARNING);
188 my_rc_avpair_add (&data.send_pairs, PW_USER_PASSWORD, password, 0) 281 mp_add_subcheck_to_check(&overall, sc_eval);
189 )) 282 mp_exit(overall);
190 die (STATE_UNKNOWN, _("Out of Memory?\n")); 283 }
191 284
192 if (nasid != NULL) { 285 if (config.expect && !strstr(msg, config.expect)) {
193 if (!(my_rc_avpair_add (&data.send_pairs, PW_NAS_IDENTIFIER, nasid, 0))) 286 xasprintf(&sc_eval.output, "%s", msg);
194 die (STATE_UNKNOWN, _("Invalid NAS-Identifier\n")); 287 sc_eval = mp_set_subcheck_state(sc_eval, STATE_WARNING);
288 mp_add_subcheck_to_check(&overall, sc_eval);
289 mp_exit(overall);
195 } 290 }
196 291
197 if (nasipaddress == NULL) { 292 if (result == OK_RC) {
198 if (gethostname (name, sizeof(name)) != 0) 293 xasprintf(&sc_eval.output, "auth OK");
199 die (STATE_UNKNOWN, _("gethostname() failed!\n")); 294 sc_eval = mp_set_subcheck_state(sc_eval, STATE_OK);
200 nasipaddress = name; 295 mp_add_subcheck_to_check(&overall, sc_eval);
296 mp_exit(overall);
201 } 297 }
202 if (!dns_lookup (nasipaddress, &ss, AF_INET)) /* TODO: Support IPv6. */
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);
229 die (STATE_UNKNOWN, "%s\n", msg);
230}
231 298
299 xasprintf(&sc_eval.output, "unexpected result code: %d", result);
300 sc_eval = mp_set_subcheck_state(sc_eval, STATE_UNKNOWN);
301 mp_add_subcheck_to_check(&overall, sc_eval);
232 302
303 mp_exit(overall);
304}
233 305
234/* process command-line arguments */ 306/* process command-line arguments */
235int 307check_radius_config_wrapper process_arguments(int argc, char **argv) {
236process_arguments (int argc, char **argv) 308 enum {
237{ 309 output_format_index
238 int c;
239
240 int option = 0;
241 static struct option longopts[] = {
242 {"hostname", required_argument, 0, 'H'},
243 {"port", required_argument, 0, 'P'},
244 {"username", required_argument, 0, 'u'},
245 {"password", required_argument, 0, 'p'},
246 {"nas-id", required_argument, 0, 'n'},
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 }; 310 };
257 311
258 while (1) { 312 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
259 c = getopt_long (argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, 313 {"port", required_argument, 0, 'P'},
260 &option); 314 {"username", required_argument, 0, 'u'},
315 {"password", required_argument, 0, 'p'},
316 {"nas-id", required_argument, 0, 'n'},
317 {"nas-ip-address", required_argument, 0, 'N'},
318 {"filename", required_argument, 0, 'F'},
319 {"expect", required_argument, 0, 'e'},
320 {"retries", required_argument, 0, 'r'},
321 {"timeout", required_argument, 0, 't'},
322 {"verbose", no_argument, 0, 'v'},
323 {"version", no_argument, 0, 'V'},
324 {"help", no_argument, 0, 'h'},
325 {"output-format", required_argument, 0, output_format_index},
326 {0, 0, 0, 0}};
327
328 check_radius_config_wrapper result = {
329 .errorcode = OK,
330 .config = check_radius_config_init(),
331 };
332
333 while (true) {
334 int option = 0;
335 int option_index = getopt_long(argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, &option);
261 336
262 if (c == -1 || c == EOF || c == 1) 337 if (CHECK_EOF(option_index) || option_index == 1) {
263 break; 338 break;
339 }
264 340
265 switch (c) { 341 switch (option_index) {
266 case '?': /* print short usage statement if args not parsable */ 342 case '?': /* print short usage statement if args not parsable */
267 usage5 (); 343 usage5();
268 case 'h': /* help */ 344 case 'h': /* help */
269 print_help (); 345 print_help();
270 exit (STATE_UNKNOWN); 346 exit(STATE_UNKNOWN);
271 case 'V': /* version */ 347 case 'V': /* version */
272 print_revision (progname, NP_VERSION); 348 print_revision(progname, NP_VERSION);
273 exit (STATE_UNKNOWN); 349 exit(STATE_UNKNOWN);
274 case 'v': /* verbose mode */ 350 case 'v': /* verbose mode */
275 verbose = true; 351 verbose = true;
276 break; 352 break;
277 case 'H': /* hostname */ 353 case 'H': /* hostname */
278 if (!is_host (optarg)) { 354 if (!is_host(optarg)) {
279 usage2 (_("Invalid hostname/address"), optarg); 355 usage2(_("Invalid hostname/address"), optarg);
280 } 356 }
281 server = optarg; 357 result.config.server = optarg;
282 break; 358 break;
283 case 'P': /* port */ 359 case 'P': /* port */
284 if (is_intnonneg (optarg)) 360 if (is_intnonneg(optarg)) {
285 port = (unsigned short)atoi (optarg); 361 result.config.port = (unsigned short)atoi(optarg);
286 else 362 } else {
287 usage4 (_("Port must be a positive integer")); 363 usage4(_("Port must be a positive integer"));
364 }
288 break; 365 break;
289 case 'u': /* username */ 366 case 'u': /* username */
290 username = optarg; 367 result.config.username = optarg;
291 break; 368 break;
292 case 'p': /* password */ 369 case 'p': /* password */
293 password = strdup(optarg); 370 result.config.password = strdup(optarg);
294 371
295 /* Delete the password from process list */ 372 /* Delete the password from process list */
296 while (*optarg != '\0') { 373 while (*optarg != '\0') {
@@ -298,119 +375,131 @@ process_arguments (int argc, char **argv)
298 optarg++; 375 optarg++;
299 } 376 }
300 break; 377 break;
301 case 'n': /* nas id */ 378 case 'n': /* nas id */
302 nasid = optarg; 379 result.config.nas_id = optarg;
303 break; 380 break;
304 case 'N': /* nas ip address */ 381 case 'N': /* nas ip address */
305 nasipaddress = optarg; 382 result.config.nas_ip_address = optarg;
306 break; 383 break;
307 case 'F': /* configuration file */ 384 case 'F': /* configuration file */
308 config_file = optarg; 385 result.config.config_file = optarg;
309 break; 386 break;
310 case 'e': /* expect */ 387 case 'e': /* expect */
311 expect = optarg; 388 result.config.expect = optarg;
312 break; 389 break;
313 case 'r': /* retries */ 390 case 'r': /* retries */
314 if (is_intpos (optarg)) 391 if (is_intpos(optarg)) {
315 retries = atoi (optarg); 392 result.config.retries = atoi(optarg);
316 else 393 } else {
317 usage4 (_("Number of retries must be a positive integer")); 394 usage4(_("Number of retries must be a positive integer"));
395 }
396 break;
397 case 't': /* timeout */
398 if (is_intpos(optarg)) {
399 timeout_interval = (unsigned)atoi(optarg);
400 } else {
401 usage2(_("Timeout interval must be a positive integer"), optarg);
402 }
318 break; 403 break;
319 case 't': /* timeout */ 404 case output_format_index: {
320 if (is_intpos (optarg)) 405 parsed_output_format parser = mp_parse_output_format(optarg);
321 timeout_interval = (unsigned)atoi (optarg); 406 if (!parser.parsing_success) {
322 else 407 // TODO List all available formats here, maybe add anothoer usage function
323 usage2 (_("Timeout interval must be a positive integer"), optarg); 408 printf("Invalid output format: %s\n", optarg);
409 exit(STATE_UNKNOWN);
410 }
411
412 result.config.output_format_is_set = true;
413 result.config.output_format = parser.output_format;
324 break; 414 break;
325 } 415 }
416 }
326 } 417 }
327 418
328 if (server == NULL) 419 if (result.config.server == NULL) {
329 usage4 (_("Hostname was not supplied")); 420 usage4(_("Hostname was not supplied"));
330 if (username == NULL) 421 }
331 usage4 (_("User not specified")); 422 if (result.config.username == NULL) {
332 if (password == NULL) 423 usage4(_("User not specified"));
333 usage4 (_("Password not specified")); 424 }
334 if (config_file == NULL) 425 if (result.config.password == NULL) {
335 usage4 (_("Configuration file not specified")); 426 usage4(_("Password not specified"));
427 }
428 if (result.config.config_file == NULL) {
429 usage4(_("Configuration file not specified"));
430 }
336 431
337 return OK; 432 return result;
338} 433}
339 434
340 435void print_help(void) {
341
342void
343print_help (void)
344{
345 char *myport; 436 char *myport;
346 xasprintf (&myport, "%d", PW_AUTH_UDP_PORT); 437 xasprintf(&myport, "%d", PW_AUTH_UDP_PORT);
347 438
348 print_revision (progname, NP_VERSION); 439 print_revision(progname, NP_VERSION);
349 440
350 printf ("Copyright (c) 1999 Robert August Vincent II\n"); 441 printf("Copyright (c) 1999 Robert August Vincent II\n");
351 printf (COPYRIGHT, copyright, email); 442 printf(COPYRIGHT, copyright, email);
352 443
353 printf("%s\n", _("Tests to see if a RADIUS server is accepting connections.")); 444 printf("%s\n", _("Tests to see if a RADIUS server is accepting connections."));
354 445
355 printf ("\n\n"); 446 printf("\n\n");
356 447
357 print_usage (); 448 print_usage();
358 449
359 printf (UT_HELP_VRSN); 450 printf(UT_HELP_VRSN);
360 printf (UT_EXTRA_OPTS); 451 printf(UT_EXTRA_OPTS);
361 452
362 printf (UT_HOST_PORT, 'P', myport); 453 printf(UT_HOST_PORT, 'P', myport);
363 454
364 printf (" %s\n", "-u, --username=STRING"); 455 printf(" %s\n", "-u, --username=STRING");
365 printf (" %s\n", _("The user to authenticate")); 456 printf(" %s\n", _("The user to authenticate"));
366 printf (" %s\n", "-p, --password=STRING"); 457 printf(" %s\n", "-p, --password=STRING");
367 printf (" %s\n", _("Password for authentication (SECURITY RISK)")); 458 printf(" %s\n", _("Password for authentication (SECURITY RISK)"));
368 printf (" %s\n", "-n, --nas-id=STRING"); 459 printf(" %s\n", "-n, --nas-id=STRING");
369 printf (" %s\n", _("NAS identifier")); 460 printf(" %s\n", _("NAS identifier"));
370 printf (" %s\n", "-N, --nas-ip-address=STRING"); 461 printf(" %s\n", "-N, --nas-ip-address=STRING");
371 printf (" %s\n", _("NAS IP Address")); 462 printf(" %s\n", _("NAS IP Address"));
372 printf (" %s\n", "-F, --filename=STRING"); 463 printf(" %s\n", "-F, --filename=STRING");
373 printf (" %s\n", _("Configuration file")); 464 printf(" %s\n", _("Configuration file"));
374 printf (" %s\n", "-e, --expect=STRING"); 465 printf(" %s\n", "-e, --expect=STRING");
375 printf (" %s\n", _("Response string to expect from the server")); 466 printf(" %s\n", _("Response string to expect from the server"));
376 printf (" %s\n", "-r, --retries=INTEGER"); 467 printf(" %s\n", "-r, --retries=INTEGER");
377 printf (" %s\n", _("Number of times to retry a failed connection")); 468 printf(" %s\n", _("Number of times to retry a failed connection"));
378 469 printf(UT_OUTPUT_FORMAT);
379 printf (UT_CONN_TIMEOUT, timeout_interval); 470
380 471 printf(UT_CONN_TIMEOUT, timeout_interval);
381 printf ("\n"); 472
382 printf ("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections.")); 473 printf("\n");
383 printf ("%s\n", _("The server to test must be specified in the invocation, as well as a user")); 474 printf("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections."));
384 printf ("%s\n", _("name and password. A configuration file must be present. The format of")); 475 printf("%s\n", _("The server to test must be specified in the invocation, as well as a user"));
385 printf ("%s\n", _("the configuration file is described in the radiusclient library sources.")); 476 printf("%s\n", _("name and password. A configuration file must be present. The format of"));
386 printf ("%s\n", _("The password option presents a substantial security issue because the")); 477 printf("%s\n", _("the configuration file is described in the radiusclient library sources."));
387 printf ("%s\n", _("password can possibly be determined by careful watching of the command line")); 478 printf("%s\n", _("The password option presents a substantial security issue because the"));
388 printf ("%s\n", _("in a process listing. This risk is exacerbated because the plugin will")); 479 printf("%s\n",
389 printf ("%s\n", _("typically be executed at regular predictable intervals. Please be sure that")); 480 _("password can possibly be determined by careful watching of the command line"));
390 printf ("%s\n", _("the password used does not allow access to sensitive system resources.")); 481 printf("%s\n", _("in a process listing. This risk is exacerbated because the plugin will"));
391 482 printf("%s\n",
392 printf (UT_SUPPORT); 483 _("typically be executed at regular predictable intervals. Please be sure that"));
484 printf("%s\n", _("the password used does not allow access to sensitive system resources."));
485
486 printf(UT_SUPPORT);
393} 487}
394 488
395 489void print_usage(void) {
396 490 printf("%s\n", _("Usage:"));
397void 491 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\ 492 [-P port] [-t timeout] [-r retries] [-e expect]\n\
403 [-n nas-id] [-N nas-ip-addr]\n", progname); 493 [-n nas-id] [-N nas-ip-addr]\n",
494 progname);
404} 495}
405 496
406 497int my_rc_read_config(char *config_file_name, rc_handle **rch) {
407 498#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
408int my_rc_read_config(char * a) 499 defined(HAVE_LIBRADCLI)
409{ 500 *rch = rc_read_config(config_file_name);
410#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
411 rch = rc_read_config(a);
412 return (rch == NULL) ? 1 : 0; 501 return (rch == NULL) ? 1 : 0;
413#else 502#else
414 return rc_read_config(a); 503 return rc_read_config(config_file_name);
415#endif 504#endif
416} 505}
diff --git a/plugins/check_radius.d/config.h b/plugins/check_radius.d/config.h
new file mode 100644
index 00000000..656bf98e
--- /dev/null
+++ b/plugins/check_radius.d/config.h
@@ -0,0 +1,48 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include <stddef.h>
6#if defined(HAVE_LIBRADCLI)
7# include <radcli/radcli.h>
8#elif defined(HAVE_LIBFREERADIUS_CLIENT)
9# include <freeradius-client.h>
10#elif defined(HAVE_LIBRADIUSCLIENT_NG)
11# include <radiusclient-ng.h>
12#else
13# include <radiusclient.h>
14#endif
15
16typedef struct {
17 char *server;
18 char *username;
19 char *password;
20 char *config_file;
21 char *nas_id;
22 char *nas_ip_address;
23 int retries;
24 unsigned short port;
25
26 char *expect;
27
28 bool output_format_is_set;
29 mp_output_format output_format;
30} check_radius_config;
31
32check_radius_config check_radius_config_init() {
33 check_radius_config tmp = {
34 .server = NULL,
35 .username = NULL,
36 .password = NULL,
37 .config_file = NULL,
38 .nas_id = NULL,
39 .nas_ip_address = NULL,
40 .retries = 1,
41 .port = PW_AUTH_UDP_PORT,
42
43 .expect = NULL,
44
45 .output_format_is_set = false,
46 };
47 return tmp;
48}
diff --git a/plugins/check_real.c b/plugins/check_real.c
index 15e035b6..b415578f 100644
--- a/plugins/check_real.c
+++ b/plugins/check_real.c
@@ -1,454 +1,525 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_real plugin 3 * Monitoring check_real plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 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_real plugin 10 * This file contains the check_real plugin
11* 11 *
12* This plugin tests the REAL service on the specified host. 12 * This plugin tests the REAL service on the specified host.
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_real"; 31#include "output.h"
32const char *copyright = "2000-2007"; 32#include "perfdata.h"
33const char *email = "devel@monitoring-plugins.org"; 33#include "states.h"
34 34#include <stdio.h>
35#include "common.h" 35#include "common.h"
36#include "netutils.h" 36#include "netutils.h"
37#include "thresholds.h"
37#include "utils.h" 38#include "utils.h"
39#include "check_real.d/config.h"
38 40
39enum { 41const char *progname = "check_real";
40 PORT = 554 42const char *copyright = "2000-2024";
41}; 43const char *email = "devel@monitoring-plugins.org";
42 44
43#define EXPECT "RTSP/1." 45#define URL ""
44#define URL ""
45 46
46int process_arguments (int, char **); 47typedef struct {
47int validate_arguments (void); 48 int errorcode;
48void print_help (void); 49 check_real_config config;
49void print_usage (void); 50} check_real_config_wrapper;
51static check_real_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50 52
51int server_port = PORT; 53static void print_help(void);
52char *server_address; 54void print_usage(void);
53char *host_name;
54char *server_url = NULL;
55char *server_expect;
56int warning_time = 0;
57bool check_warning_time = false;
58int critical_time = 0;
59bool check_critical_time = false;
60bool verbose = false;
61 55
56static bool verbose = false;
62 57
58int main(int argc, char **argv) {
59 setlocale(LC_ALL, "");
60 bindtextdomain(PACKAGE, LOCALEDIR);
61 textdomain(PACKAGE);
63 62
64int 63 /* Parse extra opts if any */
65main (int argc, char **argv) 64 argv = np_extra_opts(&argc, argv, progname);
66{
67 int sd;
68 int result = STATE_UNKNOWN;
69 char buffer[MAX_INPUT_BUFFER];
70 char *status_line = NULL;
71 65
72 setlocale (LC_ALL, ""); 66 check_real_config_wrapper tmp_config = process_arguments(argc, argv);
73 bindtextdomain (PACKAGE, LOCALEDIR); 67 if (tmp_config.errorcode == ERROR) {
74 textdomain (PACKAGE); 68 usage4(_("Could not parse arguments"));
69 }
75 70
76 /* Parse extra opts if any */ 71 const check_real_config config = tmp_config.config;
77 argv=np_extra_opts (&argc, argv, progname);
78 72
79 if (process_arguments (argc, argv) == ERROR) 73 if (config.output_format_is_set) {
80 usage4 (_("Could not parse arguments")); 74 mp_set_format(config.output_format);
75 }
81 76
82 /* initialize alarm signal handling */ 77 /* initialize alarm signal handling */
83 signal (SIGALRM, socket_timeout_alarm_handler); 78 signal(SIGALRM, socket_timeout_alarm_handler);
84 79
85 /* set socket timeout */ 80 /* set socket timeout */
86 alarm (socket_timeout); 81 alarm(socket_timeout);
87 time (&start_time); 82 time_t start_time;
83 time(&start_time);
84
85 mp_check overall = mp_check_init();
86 mp_subcheck sc_connect = mp_subcheck_init();
88 87
89 /* try to connect to the host at the given port number */ 88 /* try to connect to the host at the given port number */
90 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) 89 int socket;
91 die (STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), 90 if (my_tcp_connect(config.server_address, config.server_port, &socket) != STATE_OK) {
92 server_address, server_port); 91 xasprintf(&sc_connect.output, _("unable to connect to %s on port %d"),
92 config.server_address, config.server_port);
93 sc_connect = mp_set_subcheck_state(sc_connect, STATE_CRITICAL);
94 mp_add_subcheck_to_check(&overall, sc_connect);
95 mp_exit(overall);
96 }
97
98 xasprintf(&sc_connect.output, _("connected to %s on port %d"), config.server_address,
99 config.server_port);
100 sc_connect = mp_set_subcheck_state(sc_connect, STATE_OK);
101 mp_add_subcheck_to_check(&overall, sc_connect);
93 102
94 /* Part I - Server Check */ 103 /* Part I - Server Check */
104 mp_subcheck sc_send = mp_subcheck_init();
95 105
96 /* send the OPTIONS request */ 106 /* send the OPTIONS request */
97 sprintf (buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", host_name, server_port); 107 char buffer[MAX_INPUT_BUFFER];
98 result = send (sd, buffer, strlen (buffer), 0); 108 sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", config.host_name, config.server_port);
109 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0);
110 if (sent_bytes == -1) {
111 xasprintf(&sc_send.output, _("Sending options to %s failed"), config.host_name);
112 sc_send = mp_set_subcheck_state(sc_send, STATE_CRITICAL);
113 mp_add_subcheck_to_check(&overall, sc_send);
114 mp_exit(overall);
115 }
99 116
100 /* send the header sync */ 117 /* send the header sync */
101 sprintf (buffer, "CSeq: 1\r\n"); 118 sprintf(buffer, "CSeq: 1\r\n");
102 result = send (sd, buffer, strlen (buffer), 0); 119 sent_bytes = send(socket, buffer, strlen(buffer), 0);
120 if (sent_bytes == -1) {
121 xasprintf(&sc_send.output, _("Sending header sync to %s failed"), config.host_name);
122 sc_send = mp_set_subcheck_state(sc_send, STATE_CRITICAL);
123 mp_add_subcheck_to_check(&overall, sc_send);
124 mp_exit(overall);
125 }
103 126
104 /* send a newline so the server knows we're done with the request */ 127 /* send a newline so the server knows we're done with the request */
105 sprintf (buffer, "\r\n"); 128 sprintf(buffer, "\r\n");
106 result = send (sd, buffer, strlen (buffer), 0); 129 sent_bytes = send(socket, buffer, strlen(buffer), 0);
130 if (sent_bytes == -1) {
131 xasprintf(&sc_send.output, _("Sending newline to %s failed"), config.host_name);
132 sc_send = mp_set_subcheck_state(sc_send, STATE_CRITICAL);
133 mp_add_subcheck_to_check(&overall, sc_send);
134 mp_exit(overall);
135 }
107 136
108 /* watch for the REAL connection string */ 137 /* watch for the REAL connection string */
109 result = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0); 138 ssize_t received_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
110 139
111 /* return a CRITICAL status if we couldn't read any data */ 140 /* return a CRITICAL status if we couldn't read any data */
112 if (result == -1) 141 if (received_bytes == -1) {
113 die (STATE_CRITICAL, _("No data received from %s\n"), host_name); 142 xasprintf(&sc_send.output, _("No data received from %s"), config.host_name);
114 143 sc_send = mp_set_subcheck_state(sc_send, STATE_CRITICAL);
115 /* make sure we find the response we are looking for */ 144 mp_add_subcheck_to_check(&overall, sc_send);
116 if (!strstr (buffer, server_expect)) { 145 mp_exit(overall);
117 if (server_port == PORT)
118 printf ("%s\n", _("Invalid REAL response received from host"));
119 else
120 printf (_("Invalid REAL response received from host on port %d\n"),
121 server_port);
122 } 146 }
123 else { 147
124 /* else we got the REAL string, so check the return code */ 148 time_t end_time;
125 149 {
126 time (&end_time); 150 mp_subcheck sc_options_request = mp_subcheck_init();
127 151 mp_state_enum options_result = STATE_OK;
128 result = STATE_OK; 152 /* make sure we find the response we are looking for */
129 153 if (!strstr(buffer, config.server_expect)) {
130 status_line = (char *) strtok (buffer, "\n"); 154 if (config.server_port == PORT) {
131 155 xasprintf(&sc_options_request.output, "invalid REAL response received from host");
132 if (strstr (status_line, "200")) 156 } else {
133 result = STATE_OK; 157 xasprintf(&sc_options_request.output,
134 158 "invalid REAL response received from host on port %d",
135 /* client errors result in a warning state */ 159 config.server_port);
136 else if (strstr (status_line, "400")) 160 }
137 result = STATE_WARNING; 161 } else {
138 else if (strstr (status_line, "401")) 162 /* else we got the REAL string, so check the return code */
139 result = STATE_WARNING; 163 time(&end_time);
140 else if (strstr (status_line, "402")) 164
141 result = STATE_WARNING; 165 options_result = STATE_OK;
142 else if (strstr (status_line, "403")) 166
143 result = STATE_WARNING; 167 char *status_line = strtok(buffer, "\n");
144 else if (strstr (status_line, "404")) 168 xasprintf(&sc_options_request.output, "status line: %s", status_line);
145 result = STATE_WARNING; 169
146 170 if (strstr(status_line, "200")) {
147 /* server errors result in a critical state */ 171 options_result = STATE_OK;
148 else if (strstr (status_line, "500")) 172 }
149 result = STATE_CRITICAL; 173 /* client errors options_result in a warning state */
150 else if (strstr (status_line, "501")) 174 else if (strstr(status_line, "400")) {
151 result = STATE_CRITICAL; 175 options_result = STATE_WARNING;
152 else if (strstr (status_line, "502")) 176 } else if (strstr(status_line, "401")) {
153 result = STATE_CRITICAL; 177 options_result = STATE_WARNING;
154 else if (strstr (status_line, "503")) 178 } else if (strstr(status_line, "402")) {
155 result = STATE_CRITICAL; 179 options_result = STATE_WARNING;
156 180 } else if (strstr(status_line, "403")) {
157 else 181 options_result = STATE_WARNING;
158 result = STATE_UNKNOWN; 182 } else if (strstr(status_line, "404")) {
183 options_result = STATE_WARNING;
184 } else if (strstr(status_line, "500")) {
185 /* server errors options_result in a critical state */
186 options_result = STATE_CRITICAL;
187 } else if (strstr(status_line, "501")) {
188 options_result = STATE_CRITICAL;
189 } else if (strstr(status_line, "502")) {
190 options_result = STATE_CRITICAL;
191 } else if (strstr(status_line, "503")) {
192 options_result = STATE_CRITICAL;
193 } else {
194 options_result = STATE_UNKNOWN;
195 }
196 }
197
198 sc_options_request = mp_set_subcheck_state(sc_options_request, options_result);
199 mp_add_subcheck_to_check(&overall, sc_options_request);
200
201 if (options_result != STATE_OK) {
202 // exit here if Setting options already failed
203 mp_exit(overall);
204 }
159 } 205 }
160 206
161 /* Part II - Check stream exists and is ok */ 207 /* Part II - Check stream exists and is ok */
162 if ((result == STATE_OK )&& (server_url != NULL) ) { 208 if (config.server_url != NULL) {
163
164 /* Part I - Server Check */ 209 /* Part I - Server Check */
210 mp_subcheck sc_describe = mp_subcheck_init();
165 211
166 /* send the DESCRIBE request */ 212 /* send the DESCRIBE request */
167 sprintf (buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", host_name, 213 sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", config.host_name,
168 server_port, server_url); 214 config.server_port, config.server_url);
169 result = send (sd, buffer, strlen (buffer), 0); 215
216 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0);
217 if (sent_bytes == -1) {
218 sc_describe = mp_set_subcheck_state(sc_describe, STATE_CRITICAL);
219 xasprintf(&sc_describe.output, "sending DESCRIBE request to %s failed",
220 config.host_name);
221 mp_add_subcheck_to_check(&overall, sc_describe);
222 mp_exit(overall);
223 }
170 224
171 /* send the header sync */ 225 /* send the header sync */
172 sprintf (buffer, "CSeq: 2\r\n"); 226 sprintf(buffer, "CSeq: 2\r\n");
173 result = send (sd, buffer, strlen (buffer), 0); 227 sent_bytes = send(socket, buffer, strlen(buffer), 0);
228 if (sent_bytes == -1) {
229 sc_describe = mp_set_subcheck_state(sc_describe, STATE_CRITICAL);
230 xasprintf(&sc_describe.output, "sending DESCRIBE request to %s failed",
231 config.host_name);
232 mp_add_subcheck_to_check(&overall, sc_describe);
233 mp_exit(overall);
234 }
174 235
175 /* send a newline so the server knows we're done with the request */ 236 /* send a newline so the server knows we're done with the request */
176 sprintf (buffer, "\r\n"); 237 sprintf(buffer, "\r\n");
177 result = send (sd, buffer, strlen (buffer), 0); 238 sent_bytes = send(socket, buffer, strlen(buffer), 0);
239 if (sent_bytes == -1) {
240 sc_describe = mp_set_subcheck_state(sc_describe, STATE_CRITICAL);
241 xasprintf(&sc_describe.output, "sending DESCRIBE request to %s failed",
242 config.host_name);
243 mp_add_subcheck_to_check(&overall, sc_describe);
244 mp_exit(overall);
245 }
178 246
179 /* watch for the REAL connection string */ 247 /* watch for the REAL connection string */
180 result = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0); 248 ssize_t recv_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
181 buffer[result] = '\0'; /* null terminate received buffer */ 249 if (recv_bytes == -1) {
182 250 /* return a CRITICAL status if we couldn't read any data */
183 /* return a CRITICAL status if we couldn't read any data */ 251 sc_describe = mp_set_subcheck_state(sc_describe, STATE_CRITICAL);
184 if (result == -1) { 252 xasprintf(&sc_describe.output, "No data received from host on DESCRIBE request");
185 printf (_("No data received from host\n")); 253 mp_add_subcheck_to_check(&overall, sc_describe);
186 result = STATE_CRITICAL; 254 mp_exit(overall);
187 } 255 } else {
188 else { 256 buffer[recv_bytes] = '\0'; /* null terminate received buffer */
189 /* make sure we find the response we are looking for */ 257 /* make sure we find the response we are looking for */
190 if (!strstr (buffer, server_expect)) { 258 if (!strstr(buffer, config.server_expect)) {
191 if (server_port == PORT) 259 if (config.server_port == PORT) {
192 printf ("%s\n", _("Invalid REAL response received from host")); 260 xasprintf(&sc_describe.output, "invalid REAL response received from host");
193 else 261 } else {
194 printf (_("Invalid REAL response received from host on port %d\n"), 262 xasprintf(&sc_describe.output,
195 server_port); 263 "invalid REAL response received from host on port %d",
196 } 264 config.server_port);
197 else { 265 }
198 266
267 sc_describe = mp_set_subcheck_state(sc_describe, STATE_UNKNOWN);
268 mp_add_subcheck_to_check(&overall, sc_describe);
269 mp_exit(overall);
270 } else {
199 /* else we got the REAL string, so check the return code */ 271 /* else we got the REAL string, so check the return code */
200 272
201 time (&end_time); 273 time(&end_time);
202 274
203 result = STATE_OK; 275 char *status_line = strtok(buffer, "\n");
204 276 xasprintf(&sc_describe.output, "status line: %s", status_line);
205 status_line = (char *) strtok (buffer, "\n"); 277
206 278 mp_state_enum describe_result;
207 if (strstr (status_line, "200")) 279 if (strstr(status_line, "200")) {
208 result = STATE_OK; 280 describe_result = STATE_OK;
209 281 }
210 /* client errors result in a warning state */ 282 /* client errors describe_result in a warning state */
211 else if (strstr (status_line, "400")) 283 else if (strstr(status_line, "400")) {
212 result = STATE_WARNING; 284 describe_result = STATE_WARNING;
213 else if (strstr (status_line, "401")) 285 } else if (strstr(status_line, "401")) {
214 result = STATE_WARNING; 286 describe_result = STATE_WARNING;
215 else if (strstr (status_line, "402")) 287 } else if (strstr(status_line, "402")) {
216 result = STATE_WARNING; 288 describe_result = STATE_WARNING;
217 else if (strstr (status_line, "403")) 289 } else if (strstr(status_line, "403")) {
218 result = STATE_WARNING; 290 describe_result = STATE_WARNING;
219 else if (strstr (status_line, "404")) 291 } else if (strstr(status_line, "404")) {
220 result = STATE_WARNING; 292 describe_result = STATE_WARNING;
221 293 }
222 /* server errors result in a critical state */ 294 /* server errors describe_result in a critical state */
223 else if (strstr (status_line, "500")) 295 else if (strstr(status_line, "500")) {
224 result = STATE_CRITICAL; 296 describe_result = STATE_CRITICAL;
225 else if (strstr (status_line, "501")) 297 } else if (strstr(status_line, "501")) {
226 result = STATE_CRITICAL; 298 describe_result = STATE_CRITICAL;
227 else if (strstr (status_line, "502")) 299 } else if (strstr(status_line, "502")) {
228 result = STATE_CRITICAL; 300 describe_result = STATE_CRITICAL;
229 else if (strstr (status_line, "503")) 301 } else if (strstr(status_line, "503")) {
230 result = STATE_CRITICAL; 302 describe_result = STATE_CRITICAL;
231 303 } else {
232 else 304 describe_result = STATE_UNKNOWN;
233 result = STATE_UNKNOWN; 305 }
306
307 sc_describe = mp_set_subcheck_state(sc_describe, describe_result);
308 mp_add_subcheck_to_check(&overall, sc_describe);
234 } 309 }
235 } 310 }
236 } 311 }
237 312
238 /* Return results */ 313 /* Return results */
239 if (result == STATE_OK) { 314 mp_subcheck sc_timing = mp_subcheck_init();
240 315 xasprintf(&sc_timing.output, "response time: %lds", end_time - start_time);
241 if (check_critical_time 316 sc_timing = mp_set_subcheck_default_state(sc_timing, STATE_OK);
242 && (end_time - start_time) > critical_time) result = STATE_CRITICAL; 317
243 else if (check_warning_time 318 mp_perfdata pd_response_time = perfdata_init();
244 && (end_time - start_time) > warning_time) result = 319 pd_response_time = mp_set_pd_value(pd_response_time, (end_time - start_time));
245 STATE_WARNING; 320 pd_response_time.label = "response_time";
246 321 pd_response_time.uom = "s";
247 /* Put some HTML in here to create a dynamic link */ 322 pd_response_time = mp_pd_set_thresholds(pd_response_time, config.time_thresholds);
248 printf (_("REAL %s - %d second response time\n"), 323 mp_add_perfdata_to_subcheck(&sc_connect, pd_response_time);
249 state_text (result), 324 sc_timing = mp_set_subcheck_state(sc_timing, mp_get_pd_status(pd_response_time));
250 (int) (end_time - start_time)); 325
251 } 326 mp_add_subcheck_to_check(&overall, sc_timing);
252 else
253 printf ("%s\n", status_line);
254 327
255 /* close the connection */ 328 /* close the connection */
256 close (sd); 329 close(socket);
257 330
258 /* reset the alarm */ 331 /* reset the alarm */
259 alarm (0); 332 alarm(0);
260 333
261 return result; 334 mp_exit(overall);
262} 335}
263 336
264
265
266/* process command-line arguments */ 337/* process command-line arguments */
267int 338check_real_config_wrapper process_arguments(int argc, char **argv) {
268process_arguments (int argc, char **argv) 339 enum {
269{ 340 output_format_index = CHAR_MAX + 1,
270 int c; 341 };
271 342
272 int option = 0; 343 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
273 static struct option longopts[] = { 344 {"IPaddress", required_argument, 0, 'I'},
274 {"hostname", required_argument, 0, 'H'}, 345 {"expect", required_argument, 0, 'e'},
275 {"IPaddress", required_argument, 0, 'I'}, 346 {"url", required_argument, 0, 'u'},
276 {"expect", required_argument, 0, 'e'}, 347 {"port", required_argument, 0, 'p'},
277 {"url", required_argument, 0, 'u'}, 348 {"critical", required_argument, 0, 'c'},
278 {"port", required_argument, 0, 'p'}, 349 {"warning", required_argument, 0, 'w'},
279 {"critical", required_argument, 0, 'c'}, 350 {"timeout", required_argument, 0, 't'},
280 {"warning", required_argument, 0, 'w'}, 351 {"verbose", no_argument, 0, 'v'},
281 {"timeout", required_argument, 0, 't'}, 352 {"version", no_argument, 0, 'V'},
282 {"verbose", no_argument, 0, 'v'}, 353 {"help", no_argument, 0, 'h'},
283 {"version", no_argument, 0, 'V'}, 354 {"output-format", required_argument, 0, output_format_index},
284 {"help", no_argument, 0, 'h'}, 355 {0, 0, 0, 0}};
285 {0, 0, 0, 0} 356
357 check_real_config_wrapper result = {
358 .errorcode = OK,
359 .config = check_real_config_init(),
286 }; 360 };
287 361
288 if (argc < 2) 362 if (argc < 2) {
289 return ERROR; 363 result.errorcode = ERROR;
364 return result;
365 }
290 366
291 for (c = 1; c < argc; c++) { 367 for (int i = 1; i < argc; i++) {
292 if (strcmp ("-to", argv[c]) == 0) 368 if (strcmp("-to", argv[i]) == 0) {
293 strcpy (argv[c], "-t"); 369 strcpy(argv[i], "-t");
294 else if (strcmp ("-wt", argv[c]) == 0) 370 } else if (strcmp("-wt", argv[i]) == 0) {
295 strcpy (argv[c], "-w"); 371 strcpy(argv[i], "-w");
296 else if (strcmp ("-ct", argv[c]) == 0) 372 } else if (strcmp("-ct", argv[i]) == 0) {
297 strcpy (argv[c], "-c"); 373 strcpy(argv[i], "-c");
374 }
298 } 375 }
299 376
300 while (1) { 377 while (true) {
301 c = getopt_long (argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, 378 int option = 0;
302 &option); 379 int option_char = getopt_long(argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, &option);
303 380
304 if (c == -1 || c == EOF) 381 if (option_char == -1 || option_char == EOF) {
305 break; 382 break;
383 }
306 384
307 switch (c) { 385 switch (option_char) {
308 case 'I': /* hostname */ 386 case 'I': /* hostname */
309 case 'H': /* hostname */ 387 case 'H': /* hostname */
310 if (server_address) 388 if (result.config.server_address) {
311 break; 389 break;
312 else if (is_host (optarg)) 390 } else if (is_host(optarg)) {
313 server_address = optarg; 391 result.config.server_address = optarg;
314 else 392 } else {
315 usage2 (_("Invalid hostname/address"), optarg); 393 usage2(_("Invalid hostname/address"), optarg);
316 break; 394 }
317 case 'e': /* string to expect in response header */
318 server_expect = optarg;
319 break; 395 break;
320 case 'u': /* server URL */ 396 case 'e': /* string to expect in response header */
321 server_url = optarg; 397 result.config.server_expect = optarg;
322 break; 398 break;
323 case 'p': /* port */ 399 case 'u': /* server URL */
324 if (is_intpos (optarg)) { 400 result.config.server_url = optarg;
325 server_port = atoi (optarg);
326 }
327 else {
328 usage4 (_("Port must be a positive integer"));
329 }
330 break; 401 break;
331 case 'w': /* warning time threshold */ 402 case 'p': /* port */
332 if (is_intnonneg (optarg)) { 403 if (is_intpos(optarg)) {
333 warning_time = atoi (optarg); 404 result.config.server_port = atoi(optarg);
334 check_warning_time = true; 405 } else {
335 } 406 usage4(_("Port must be a positive integer"));
336 else {
337 usage4 (_("Warning time must be a positive integer"));
338 } 407 }
339 break; 408 break;
340 case 'c': /* critical time threshold */ 409 case 'w': /* warning time threshold */
341 if (is_intnonneg (optarg)) { 410 {
342 critical_time = atoi (optarg); 411 mp_range_parsed critical_range = mp_parse_range_string(optarg);
343 check_critical_time = true; 412 if (critical_range.error != MP_PARSING_SUCCESS) {
413 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg);
344 } 414 }
345 else { 415 result.config.time_thresholds =
346 usage4 (_("Critical time must be a positive integer")); 416 mp_thresholds_set_warn(result.config.time_thresholds, critical_range.range);
417 } break;
418 case 'c': /* critical time threshold */
419 {
420 mp_range_parsed critical_range = mp_parse_range_string(optarg);
421 if (critical_range.error != MP_PARSING_SUCCESS) {
422 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg);
347 } 423 }
348 break; 424 result.config.time_thresholds =
349 case 'v': /* verbose */ 425 mp_thresholds_set_crit(result.config.time_thresholds, critical_range.range);
426 } break;
427 case 'v': /* verbose */
350 verbose = true; 428 verbose = true;
351 break; 429 break;
352 case 't': /* timeout */ 430 case 't': /* timeout */
353 if (is_intnonneg (optarg)) { 431 if (is_intnonneg(optarg)) {
354 socket_timeout = atoi (optarg); 432 socket_timeout = atoi(optarg);
433 } else {
434 usage4(_("Timeout interval must be a positive integer"));
355 } 435 }
356 else { 436 break;
357 usage4 (_("Timeout interval must be a positive integer")); 437 case 'V': /* version */
438 print_revision(progname, NP_VERSION);
439 exit(STATE_UNKNOWN);
440 case 'h': /* help */
441 print_help();
442 exit(STATE_UNKNOWN);
443 case output_format_index: {
444 parsed_output_format parser = mp_parse_output_format(optarg);
445 if (!parser.parsing_success) {
446 // TODO List all available formats here, maybe add anothoer usage function
447 printf("Invalid output format: %s\n", optarg);
448 exit(STATE_UNKNOWN);
358 } 449 }
450
451 result.config.output_format_is_set = true;
452 result.config.output_format = parser.output_format;
359 break; 453 break;
360 case 'V': /* version */ 454 }
361 print_revision (progname, NP_VERSION); 455 case '?': /* usage */
362 exit (STATE_UNKNOWN); 456 usage5();
363 case 'h': /* help */
364 print_help ();
365 exit (STATE_UNKNOWN);
366 case '?': /* usage */
367 usage5 ();
368 } 457 }
369 } 458 }
370 459
371 c = optind; 460 int option_char = optind;
372 if (server_address==NULL && argc>c) { 461 if (result.config.server_address == NULL && argc > option_char) {
373 if (is_host (argv[c])) { 462 if (is_host(argv[option_char])) {
374 server_address = argv[c++]; 463 result.config.server_address = argv[option_char++];
375 } 464 } else {
376 else { 465 usage2(_("Invalid hostname/address"), argv[option_char]);
377 usage2 (_("Invalid hostname/address"), argv[c]);
378 } 466 }
379 } 467 }
380 468
381 if (server_address==NULL) 469 if (result.config.server_address == NULL) {
382 usage4 (_("You must provide a server to check")); 470 usage4(_("You must provide a server to check"));
383 471 }
384 if (host_name==NULL)
385 host_name = strdup (server_address);
386
387 if (server_expect == NULL)
388 server_expect = strdup(EXPECT);
389
390 return validate_arguments ();
391}
392
393 472
473 if (result.config.host_name == NULL) {
474 result.config.host_name = strdup(result.config.server_address);
475 }
394 476
395int 477 return result;
396validate_arguments (void)
397{
398 return OK;
399} 478}
400 479
401 480void print_help(void) {
402
403void
404print_help (void)
405{
406 char *myport; 481 char *myport;
407 xasprintf (&myport, "%d", PORT); 482 xasprintf(&myport, "%d", PORT);
408 483
409 print_revision (progname, NP_VERSION); 484 print_revision(progname, NP_VERSION);
410 485
411 printf ("Copyright (c) 1999 Pedro Leite <leite@cic.ua.pt>\n"); 486 printf("Copyright (c) 1999 Pedro Leite <leite@cic.ua.pt>\n");
412 printf (COPYRIGHT, copyright, email); 487 printf(COPYRIGHT, copyright, email);
413 488
414 printf ("%s\n", _("This plugin tests the REAL service on the specified host.")); 489 printf("%s\n", _("This plugin tests the REAL service on the specified host."));
415 490
416 printf ("\n\n"); 491 printf("\n\n");
417 492
418 print_usage (); 493 print_usage();
419 494
420 printf (UT_HELP_VRSN); 495 printf(UT_HELP_VRSN);
421 printf (UT_EXTRA_OPTS); 496 printf(UT_EXTRA_OPTS);
422 497
423 printf (UT_HOST_PORT, 'p', myport); 498 printf(UT_HOST_PORT, 'p', myport);
424 499
425 printf (" %s\n", "-u, --url=STRING"); 500 printf(" %s\n", "-u, --url=STRING");
426 printf (" %s\n", _("Connect to this url")); 501 printf(" %s\n", _("Connect to this url"));
427 printf (" %s\n", "-e, --expect=STRING"); 502 printf(" %s\n", "-e, --expect=STRING");
428 printf (_("String to expect in first line of server response (default: %s)\n"), 503 printf(_("String to expect in first line of server response (default: %s)\n"), default_expect);
429 EXPECT);
430 504
431 printf (UT_WARN_CRIT); 505 printf(UT_WARN_CRIT);
432 506
433 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 507 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
434 508
435 printf (UT_VERBOSE); 509 printf(UT_VERBOSE);
436 510
437 printf ("\n"); 511 printf("\n");
438 printf ("%s\n", _("This plugin will attempt to open an RTSP connection with the host.")); 512 printf("%s\n", _("This plugin will attempt to open an RTSP connection with the host."));
439 printf ("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); 513 printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return"));
440 printf ("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful connects,")); 514 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful connects,"));
441 printf ("%s\n", _("but incorrect response messages from the host result in STATE_WARNING return")); 515 printf("%s\n",
442 printf ("%s\n", _("values.")); 516 _("but incorrect response messages from the host result in STATE_WARNING return"));
517 printf("%s\n", _("values."));
443 518
444 printf (UT_SUPPORT); 519 printf(UT_SUPPORT);
445} 520}
446 521
447 522void print_usage(void) {
448 523 printf("%s\n", _("Usage:"));
449void 524 printf("%s -H host [-e expect] [-p port] [-w warn] [-c crit] [-t timeout] [-v]\n", progname);
450print_usage (void)
451{
452 printf ("%s\n", _("Usage:"));
453 printf ("%s -H host [-e expect] [-p port] [-w warn] [-c crit] [-t timeout] [-v]\n", progname);
454} 525}
diff --git a/plugins/check_real.d/config.h b/plugins/check_real.d/config.h
new file mode 100644
index 00000000..15b70b98
--- /dev/null
+++ b/plugins/check_real.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
6#include <stddef.h>
7
8enum {
9 PORT = 554
10};
11
12const char *default_expect = "RTSP/1.";
13
14typedef struct {
15 char *server_address;
16 char *host_name;
17 int server_port;
18 char *server_url;
19
20 const char *server_expect;
21
22 mp_thresholds time_thresholds;
23
24 bool output_format_is_set;
25 mp_output_format output_format;
26} check_real_config;
27
28check_real_config check_real_config_init() {
29 check_real_config tmp = {
30 .server_address = NULL,
31 .host_name = NULL,
32 .server_port = PORT,
33 .server_url = NULL,
34
35 .server_expect = default_expect,
36
37 .time_thresholds = mp_thresholds_init(),
38
39 .output_format_is_set = false,
40 };
41 return tmp;
42}
diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c
index 986c3e18..19e2a58f 100644
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
@@ -1,265 +1,313 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_smtp plugin 3 * Monitoring check_smtp plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2023 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_smtp plugin 10 * This file contains the check_smtp plugin
11* 11 *
12* This plugin will attempt to open an SMTP connection with the host. 12 * This plugin will attempt to open an SMTP connection with the host.
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
31const char *progname = "check_smtp";
32const char *copyright = "2000-2007";
33const char *email = "devel@monitoring-plugins.org";
34 30
35#include "common.h" 31#include "common.h"
36#include "netutils.h" 32#include "netutils.h"
33#include "output.h"
34#include "perfdata.h"
35#include "thresholds.h"
37#include "utils.h" 36#include "utils.h"
38#include "base64.h" 37#include "base64.h"
38#include "regex.h"
39 39
40#include <ctype.h> 40#include <ctype.h>
41#include <string.h>
42#include "check_smtp.d/config.h"
43#include "../lib/states.h"
41 44
42#ifdef HAVE_SSL 45const char *progname = "check_smtp";
43bool check_cert = false; 46const char *copyright = "2000-2024";
44int days_till_exp_warn, days_till_exp_crit; 47const char *email = "devel@monitoring-plugins.org";
45# define my_recv(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
46# define my_send(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
47#else /* ifndef HAVE_SSL */
48# define my_recv(buf, len) read(sd, buf, len)
49# define my_send(buf, len) send(sd, buf, len, 0)
50#endif
51 48
52enum { 49#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
53 SMTP_PORT = 25, 50#define SMTP_HELO "HELO "
54 SMTPS_PORT = 465 51#define SMTP_EHLO "EHLO "
55}; 52#define SMTP_LHLO "LHLO "
56#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" 53#define SMTP_QUIT "QUIT\r\n"
57#define SMTP_EXPECT "220" 54#define SMTP_STARTTLS "STARTTLS\r\n"
58#define SMTP_HELO "HELO "
59#define SMTP_EHLO "EHLO "
60#define SMTP_LHLO "LHLO "
61#define SMTP_QUIT "QUIT\r\n"
62#define SMTP_STARTTLS "STARTTLS\r\n"
63#define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n" 55#define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n"
64 56
65#define EHLO_SUPPORTS_STARTTLS 1 57#define EHLO_SUPPORTS_STARTTLS 1
66 58
67int process_arguments (int, char **); 59typedef struct {
68int validate_arguments (void); 60 int errorcode;
69void print_help (void); 61 check_smtp_config config;
70void print_usage (void); 62} check_smtp_config_wrapper;
71void smtp_quit(void); 63static check_smtp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
72int recvline(char *, size_t);
73int recvlines(char *, size_t);
74int my_close(void);
75 64
76#include "regex.h" 65int my_recv(check_smtp_config config, void *buf, int num, int socket_descriptor,
77char regex_expect[MAX_INPUT_BUFFER] = ""; 66 bool ssl_established) {
78regex_t preg; 67#ifdef HAVE_SSL
79regmatch_t pmatch[10]; 68 if ((config.use_starttls || config.use_ssl) && ssl_established) {
80char timestamp[20] = ""; 69 return np_net_ssl_read(buf, num);
81char errbuf[MAX_INPUT_BUFFER]; 70 }
82int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 71 return (int)read(socket_descriptor, buf, (size_t)num);
83int eflags = 0; 72#else /* ifndef HAVE_SSL */
84int errcode, excode; 73 return read(socket_descriptor, buf, len)
85 74#endif
86int server_port = SMTP_PORT; 75}
87int server_port_option = 0;
88char *server_address = NULL;
89char *server_expect = NULL;
90char *mail_command = NULL;
91char *from_arg = NULL;
92int send_mail_from=0;
93int ncommands=0;
94int command_size=0;
95int nresponses=0;
96int response_size=0;
97char **commands = NULL;
98char **responses = NULL;
99char *authtype = NULL;
100char *authuser = NULL;
101char *authpass = NULL;
102double warning_time = 0;
103bool check_warning_time = false;
104double critical_time = 0;
105bool check_critical_time = false;
106int verbose = 0;
107bool use_ssl = false;
108bool use_starttls = false;
109bool use_sni = false;
110bool use_proxy_prefix = false;
111bool use_ehlo = false;
112bool use_lhlo = false;
113bool ssl_established = false;
114char *localhostname = NULL;
115int sd;
116char buffer[MAX_INPUT_BUFFER];
117enum {
118 TCP_PROTOCOL = 1,
119 UDP_PROTOCOL = 2,
120};
121bool ignore_send_quit_failure = false;
122
123
124int
125main (int argc, char **argv)
126{
127 bool supports_tls = false;
128 int n = 0;
129 double elapsed_time;
130 long microsec;
131 int result = STATE_UNKNOWN;
132 char *cmd_str = NULL;
133 char *helocmd = NULL;
134 char *error_msg = "";
135 char *server_response = NULL;
136 struct timeval tv;
137 76
138 /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ 77int my_send(check_smtp_config config, void *buf, int num, int socket_descriptor,
139 (void) signal (SIGPIPE, SIG_IGN); 78 bool ssl_established) {
79#ifdef HAVE_SSL
80 if ((config.use_starttls || config.use_ssl) && ssl_established) {
81
82 return np_net_ssl_write(buf, num);
83 }
84 return (int)send(socket_descriptor, buf, (size_t)num, 0);
85#else /* ifndef HAVE_SSL */
86 return send(socket_descriptor, buf, len, 0);
87#endif
88}
140 89
141 setlocale (LC_ALL, ""); 90static void print_help(void);
142 bindtextdomain (PACKAGE, LOCALEDIR); 91void print_usage(void);
143 textdomain (PACKAGE); 92static char *smtp_quit(check_smtp_config /*config*/, char /*buffer*/[MAX_INPUT_BUFFER],
93 int /*socket_descriptor*/, bool /*ssl_established*/);
94static int recvline(char * /*buf*/, size_t /*bufsize*/, check_smtp_config /*config*/,
95 int /*socket_descriptor*/, bool /*ssl_established*/);
96static int recvlines(check_smtp_config /*config*/, char * /*buf*/, size_t /*bufsize*/,
97 int /*socket_descriptor*/, bool /*ssl_established*/);
98static int my_close(int /*socket_descriptor*/);
99
100static int verbose = 0;
101
102int main(int argc, char **argv) {
103#ifdef __OpenBSD__
104 /* - rpath is required to read --extra-opts (given up later)
105 * - inet is required for sockets
106 * - unix is required for Unix domain sockets
107 * - dns is required for name lookups */
108 pledge("stdio rpath inet unix dns", NULL);
109#endif // __OpenBSD__
110
111 setlocale(LC_ALL, "");
112 bindtextdomain(PACKAGE, LOCALEDIR);
113 textdomain(PACKAGE);
144 114
145 /* Parse extra opts if any */ 115 /* Parse extra opts if any */
146 argv=np_extra_opts (&argc, argv, progname); 116 argv = np_extra_opts(&argc, argv, progname);
117
118 check_smtp_config_wrapper tmp_config = process_arguments(argc, argv);
119
120 if (tmp_config.errorcode == ERROR) {
121 usage4(_("Could not parse arguments"));
122 }
123
124#ifdef __OpenBSD__
125 pledge("stdio inet unix dns", NULL);
126#endif // __OpenBSD__
147 127
148 if (process_arguments (argc, argv) == ERROR) 128 const check_smtp_config config = tmp_config.config;
149 usage4 (_("Could not parse arguments")); 129
130 if (config.output_format_is_set) {
131 mp_set_format(config.output_format);
132 }
150 133
151 /* If localhostname not set on command line, use gethostname to set */ 134 /* If localhostname not set on command line, use gethostname to set */
152 if(! localhostname){ 135 char *localhostname = config.localhostname;
153 localhostname = malloc (HOST_MAX_BYTES); 136 if (!localhostname) {
154 if(!localhostname){ 137 localhostname = malloc(HOST_MAX_BYTES);
138 if (!localhostname) {
155 printf(_("malloc() failed!\n")); 139 printf(_("malloc() failed!\n"));
156 return STATE_CRITICAL; 140 exit(STATE_CRITICAL);
157 } 141 }
158 if(gethostname(localhostname, HOST_MAX_BYTES)){ 142 if (gethostname(localhostname, HOST_MAX_BYTES)) {
159 printf(_("gethostname() failed!\n")); 143 printf(_("gethostname() failed!\n"));
160 return STATE_CRITICAL; 144 exit(STATE_CRITICAL);
161 } 145 }
162 } 146 }
163 if(use_lhlo)
164 xasprintf (&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n");
165 else if(use_ehlo)
166 xasprintf (&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n");
167 else
168 xasprintf (&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n");
169 147
170 if (verbose) 148 char *helocmd = NULL;
149 if (config.use_lhlo) {
150 xasprintf(&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n");
151 } else if (config.use_ehlo) {
152 xasprintf(&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n");
153 } else {
154 xasprintf(&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n");
155 }
156
157 if (verbose) {
171 printf("HELOCMD: %s", helocmd); 158 printf("HELOCMD: %s", helocmd);
159 }
172 160
161 char *mail_command = strdup("MAIL ");
162 char *cmd_str = NULL;
173 /* initialize the MAIL command with optional FROM command */ 163 /* initialize the MAIL command with optional FROM command */
174 xasprintf (&cmd_str, "%sFROM:<%s>%s", mail_command, from_arg, "\r\n"); 164 xasprintf(&cmd_str, "%sFROM:<%s>%s", mail_command, config.from_arg, "\r\n");
165
166 if (verbose && config.send_mail_from) {
167 printf("FROM CMD: %s", cmd_str);
168 }
175 169
176 if (verbose && send_mail_from) 170 /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */
177 printf ("FROM CMD: %s", cmd_str); 171 (void)signal(SIGPIPE, SIG_IGN);
178 172
179 /* initialize alarm signal handling */ 173 /* initialize alarm signal handling */
180 (void) signal (SIGALRM, socket_timeout_alarm_handler); 174 (void)signal(SIGALRM, socket_timeout_alarm_handler);
181 175
182 /* set socket timeout */ 176 /* set socket timeout */
183 (void) alarm (socket_timeout); 177 (void)alarm(socket_timeout);
184 178
179 struct timeval start_time;
185 /* start timer */ 180 /* start timer */
186 gettimeofday (&tv, NULL); 181 gettimeofday(&start_time, NULL);
182
183 int socket_descriptor = 0;
187 184
188 /* try to connect to the host at the given port number */ 185 /* try to connect to the host at the given port number */
189 result = my_tcp_connect (server_address, server_port, &sd); 186 mp_state_enum tcp_result =
190 187 my_tcp_connect(config.server_address, config.server_port, &socket_descriptor);
191 if (result == STATE_OK) { /* we connected */ 188
192 /* If requested, send PROXY header */ 189 mp_check overall = mp_check_init();
193 if (use_proxy_prefix) { 190 mp_subcheck sc_tcp_connect = mp_subcheck_init();
194 if (verbose) 191 char buffer[MAX_INPUT_BUFFER];
195 printf ("Sending header %s\n", PROXY_PREFIX); 192 bool ssl_established = false;
196 my_send(PROXY_PREFIX, strlen(PROXY_PREFIX)); 193
194 if (tcp_result != STATE_OK) {
195 // Connect failed
196 sc_tcp_connect = mp_set_subcheck_state(sc_tcp_connect, STATE_CRITICAL);
197 xasprintf(&sc_tcp_connect.output, "TCP connect to '%s' failed", config.server_address);
198 mp_add_subcheck_to_check(&overall, sc_tcp_connect);
199 mp_exit(overall);
200 }
201
202 /* we connected */
203 /* If requested, send PROXY header */
204 if (config.use_proxy_prefix) {
205 if (verbose) {
206 printf("Sending header %s\n", PROXY_PREFIX);
197 } 207 }
208 my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
209 }
198 210
199#ifdef HAVE_SSL 211#ifdef HAVE_SSL
200 if (use_ssl) { 212 if (config.use_ssl) {
201 result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); 213 int tls_result = np_net_ssl_init_with_hostname(
202 if (result != STATE_OK) { 214 socket_descriptor, (config.use_sni ? config.server_address : NULL));
203 printf (_("CRITICAL - Cannot create SSL context.\n")); 215
204 close(sd); 216 mp_subcheck sc_tls_connection = mp_subcheck_init();
205 np_net_ssl_cleanup(); 217
206 return STATE_CRITICAL; 218 if (tls_result != STATE_OK) {
207 } else { 219 close(socket_descriptor);
208 ssl_established = 1; 220 np_net_ssl_cleanup();
209 } 221
222 sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_CRITICAL);
223 xasprintf(&sc_tls_connection.output, "cannot create TLS context");
224 mp_add_subcheck_to_check(&overall, sc_tls_connection);
225 mp_exit(overall);
210 } 226 }
227
228 sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_OK);
229 xasprintf(&sc_tls_connection.output, "TLS context established");
230 mp_add_subcheck_to_check(&overall, sc_tls_connection);
231 ssl_established = true;
232 }
211#endif 233#endif
212 234
213 /* watch for the SMTP connection string and */ 235 /* watch for the SMTP connection string and */
214 /* return a WARNING status if we couldn't read any data */ 236 /* return a WARNING status if we couldn't read any data */
215 if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { 237 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
216 printf (_("recv() failed\n")); 238 mp_subcheck sc_read_data = mp_subcheck_init();
217 return STATE_WARNING; 239 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
240 xasprintf(&sc_read_data.output, "recv() failed");
241 mp_add_subcheck_to_check(&overall, sc_read_data);
242 mp_exit(overall);
243 }
244
245 char *server_response = NULL;
246 /* save connect return (220 hostname ..) for later use */
247 xasprintf(&server_response, "%s", buffer);
248
249 /* send the HELO/EHLO command */
250 my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
251
252 /* allow for response to helo command to reach us */
253 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
254 mp_subcheck sc_read_data = mp_subcheck_init();
255 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
256 xasprintf(&sc_read_data.output, "recv() failed");
257 mp_add_subcheck_to_check(&overall, sc_read_data);
258 mp_exit(overall);
259 }
260
261 bool supports_tls = false;
262 if (config.use_ehlo || config.use_lhlo) {
263 if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
264 supports_tls = true;
218 } 265 }
266 }
219 267
220 /* save connect return (220 hostname ..) for later use */ 268 if (config.use_starttls && !supports_tls) {
221 xasprintf(&server_response, "%s", buffer); 269 smtp_quit(config, buffer, socket_descriptor, ssl_established);
222 270
223 /* send the HELO/EHLO command */ 271 mp_subcheck sc_read_data = mp_subcheck_init();
224 my_send(helocmd, strlen(helocmd)); 272 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
273 xasprintf(&sc_read_data.output, "StartTLS not supported by server");
274 mp_add_subcheck_to_check(&overall, sc_read_data);
275 mp_exit(overall);
276 }
225 277
226 /* allow for response to helo command to reach us */ 278#ifdef HAVE_SSL
227 if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { 279 if (config.use_starttls) {
228 printf (_("recv() failed\n")); 280 /* send the STARTTLS command */
229 return STATE_WARNING; 281 send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
230 } else if(use_ehlo || use_lhlo){ 282
231 if(strstr(buffer, "250 STARTTLS") != NULL || 283 mp_subcheck sc_starttls_init = mp_subcheck_init();
232 strstr(buffer, "250-STARTTLS") != NULL){ 284 recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
233 supports_tls=true; 285 ssl_established); /* wait for it */
234 } 286 if (!strstr(buffer, SMTP_EXPECT)) {
287 smtp_quit(config, buffer, socket_descriptor, ssl_established);
288
289 xasprintf(&sc_starttls_init.output, "StartTLS not supported by server");
290 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_UNKNOWN);
291 mp_add_subcheck_to_check(&overall, sc_starttls_init);
292 mp_exit(overall);
235 } 293 }
236 294
237 if(use_starttls && ! supports_tls){ 295 mp_state_enum starttls_result = np_net_ssl_init_with_hostname(
238 printf(_("WARNING - TLS not supported by server\n")); 296 socket_descriptor, (config.use_sni ? config.server_address : NULL));
239 smtp_quit(); 297 if (starttls_result != STATE_OK) {
240 return STATE_WARNING; 298 close(socket_descriptor);
299 np_net_ssl_cleanup();
300
301 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_CRITICAL);
302 xasprintf(&sc_starttls_init.output, "failed to create StartTLS context");
303 mp_add_subcheck_to_check(&overall, sc_starttls_init);
304 mp_exit(overall);
241 } 305 }
306 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_OK);
307 xasprintf(&sc_starttls_init.output, "created StartTLS context");
308 mp_add_subcheck_to_check(&overall, sc_starttls_init);
242 309
243#ifdef HAVE_SSL 310 ssl_established = true;
244 if(use_starttls) {
245 /* send the STARTTLS command */
246 send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
247
248 recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */
249 if (!strstr (buffer, SMTP_EXPECT)) {
250 printf (_("Server does not support STARTTLS\n"));
251 smtp_quit();
252 return STATE_UNKNOWN;
253 }
254 result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL));
255 if(result != STATE_OK) {
256 printf (_("CRITICAL - Cannot create SSL context.\n"));
257 close(sd);
258 np_net_ssl_cleanup();
259 return STATE_CRITICAL;
260 } else {
261 ssl_established = 1;
262 }
263 311
264 /* 312 /*
265 * Resend the EHLO command. 313 * Resend the EHLO command.
@@ -272,218 +320,287 @@ main (int argc, char **argv)
272 * reason, some MTAs will not allow an AUTH LOGIN command before 320 * reason, some MTAs will not allow an AUTH LOGIN command before
273 * we resent EHLO via TLS. 321 * we resent EHLO via TLS.
274 */ 322 */
275 if (my_send(helocmd, strlen(helocmd)) <= 0) { 323 if (my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established) <=
276 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS.")); 324 0) {
277 my_close(); 325 my_close(socket_descriptor);
278 return STATE_UNKNOWN; 326
327 mp_subcheck sc_ehlo = mp_subcheck_init();
328 sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
329 xasprintf(&sc_ehlo.output, "cannot send EHLO command via StartTLS");
330 mp_add_subcheck_to_check(&overall, sc_ehlo);
331 mp_exit(overall);
279 } 332 }
280 if (verbose) 333
334 if (verbose) {
281 printf(_("sent %s"), helocmd); 335 printf(_("sent %s"), helocmd);
282 if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
283 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS."));
284 my_close();
285 return STATE_UNKNOWN;
286 } 336 }
337
338 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
339 my_close(socket_descriptor);
340
341 mp_subcheck sc_ehlo = mp_subcheck_init();
342 sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
343 xasprintf(&sc_ehlo.output, "cannot read EHLO response via StartTLS");
344 mp_add_subcheck_to_check(&overall, sc_ehlo);
345 mp_exit(overall);
346 }
347
287 if (verbose) { 348 if (verbose) {
288 printf("%s", buffer); 349 printf("%s", buffer);
289 } 350 }
351 }
352
353# ifdef MOPL_USE_OPENSSL
354 if (ssl_established) {
355 net_ssl_check_cert_result cert_check_result =
356 np_net_ssl_check_cert2(config.days_till_exp_warn, config.days_till_exp_crit);
357
358 mp_subcheck sc_cert_check = mp_subcheck_init();
359
360 switch (cert_check_result.errors) {
361 case ALL_OK: {
362
363 if (cert_check_result.result_state != STATE_OK &&
364 config.ignore_certificate_expiration) {
365 xasprintf(&sc_cert_check.output,
366 "Remaining certificate lifetime: %d days. Expiration will be ignored",
367 (int)(cert_check_result.remaining_seconds / 86400));
368 sc_cert_check = mp_set_subcheck_state(sc_cert_check, STATE_OK);
369 } else {
370 xasprintf(&sc_cert_check.output, "Remaining certificate lifetime: %d days",
371 (int)(cert_check_result.remaining_seconds / 86400));
372 sc_cert_check =
373 mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
374 }
375 } break;
376 case NO_SERVER_CERTIFICATE_PRESENT: {
377 xasprintf(&sc_cert_check.output, "no server certificate present");
378 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
379 } break;
380 case UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT: {
381 xasprintf(&sc_cert_check.output, "can not retrieve certificate subject");
382 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
383 } break;
384 case WRONG_TIME_FORMAT_IN_CERTIFICATE: {
385 xasprintf(&sc_cert_check.output, "wrong time format in certificate");
386 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
387 } break;
388 };
389
390 mp_add_subcheck_to_check(&overall, sc_cert_check);
391 }
392# endif /* MOPL_USE_OPENSSL */
290 393
291# ifdef USE_OPENSSL
292 if ( check_cert ) {
293 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
294 smtp_quit();
295 my_close();
296 return result;
297 }
298# endif /* USE_OPENSSL */
299 }
300#endif 394#endif
301 395
302 if (verbose) 396 if (verbose) {
303 printf ("%s", buffer); 397 printf("%s", buffer);
304 398 }
305 /* save buffer for later use */ 399
306 xasprintf(&server_response, "%s%s", server_response, buffer); 400 /* save buffer for later use */
307 /* strip the buffer of carriage returns */ 401 xasprintf(&server_response, "%s%s", server_response, buffer);
308 strip (server_response); 402 /* strip the buffer of carriage returns */
309 403 strip(server_response);
310 /* make sure we find the droids we are looking for */ 404
311 if (!strstr (server_response, server_expect)) { 405 /* make sure we find the droids we are looking for */
312 if (server_port == SMTP_PORT) 406 mp_subcheck sc_expect_response = mp_subcheck_init();
313 printf (_("Invalid SMTP response received from host: %s\n"), server_response); 407
314 else 408 if (!strstr(server_response, config.server_expect)) {
315 printf (_("Invalid SMTP response received from host on port %d: %s\n"), 409 sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_WARNING);
316 server_port, server_response); 410 if (config.server_port == SMTP_PORT) {
317 return STATE_WARNING; 411 xasprintf(&sc_expect_response.output, _("invalid SMTP response received from host: %s"),
412 server_response);
413 } else {
414 xasprintf(&sc_expect_response.output,
415 _("invalid SMTP response received from host on port %d: %s"),
416 config.server_port, server_response);
318 } 417 }
418 exit(STATE_WARNING);
419 } else {
420 xasprintf(&sc_expect_response.output, "received valid SMTP response '%s' from host: '%s'",
421 config.server_expect, server_response);
422 sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_OK);
423 }
424
425 mp_add_subcheck_to_check(&overall, sc_expect_response);
426
427 if (config.send_mail_from) {
428 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
429 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
430 verbose) {
431 printf("%s", buffer);
432 }
433 }
434
435 size_t counter = 0;
436 while (counter < config.ncommands) {
437 xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
438 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
439 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
440 verbose) {
441 printf("%s", buffer);
442 }
443
444 strip(buffer);
445
446 if (counter < config.nresponses) {
447 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
448 regex_t preg;
449 int errcode = regcomp(&preg, config.responses[counter], cflags);
450 char errbuf[MAX_INPUT_BUFFER];
451 if (errcode != 0) {
452 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
453 printf(_("Could Not Compile Regular Expression"));
454 exit(STATE_UNKNOWN);
455 }
319 456
320 if (send_mail_from) { 457 regmatch_t pmatch[10];
321 my_send(cmd_str, strlen(cmd_str)); 458 int eflags = 0;
322 if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) 459 int excode = regexec(&preg, buffer, 10, pmatch, eflags);
323 printf("%s", buffer); 460 mp_subcheck sc_expected_responses = mp_subcheck_init();
461 if (excode == 0) {
462 xasprintf(&sc_expected_responses.output, "valid response '%s' to command '%s'",
463 buffer, config.commands[counter]);
464 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_OK);
465 } else if (excode == REG_NOMATCH) {
466 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_WARNING);
467 xasprintf(&sc_expected_responses.output, "invalid response '%s' to command '%s'",
468 buffer, config.commands[counter]);
469 } else {
470 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
471 xasprintf(&sc_expected_responses.output, "regexec execute error: %s", errbuf);
472 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_UNKNOWN);
473 }
324 } 474 }
475 counter++;
476 }
325 477
326 n = 0; 478 if (config.authtype != NULL) {
327 while (n < ncommands) { 479 mp_subcheck sc_auth = mp_subcheck_init();
328 xasprintf (&cmd_str, "%s%s", commands[n], "\r\n"); 480
329 my_send(cmd_str, strlen(cmd_str)); 481 if (strcmp(config.authtype, "LOGIN") == 0) {
330 if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) 482 char *abuf;
331 printf("%s", buffer); 483 int ret;
332 strip (buffer); 484 do {
333 if (n < nresponses) { 485 /* send AUTH LOGIN */
334 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 486 my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
335 errcode = regcomp (&preg, responses[n], cflags); 487 ssl_established);
336 if (errcode != 0) { 488
337 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 489 if (verbose) {
338 printf (_("Could Not Compile Regular Expression")); 490 printf(_("sent %s\n"), "AUTH LOGIN");
339 return ERROR;
340 } 491 }
341 excode = regexec (&preg, buffer, 10, pmatch, eflags); 492
342 if (excode == 0) { 493 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
343 result = STATE_OK; 494 ssl_established)) <= 0) {
495 xasprintf(&sc_auth.output, _("recv() failed after AUTH LOGIN"));
496 sc_auth = mp_set_subcheck_state(sc_auth, STATE_WARNING);
497 break;
344 } 498 }
345 else if (excode == REG_NOMATCH) { 499
346 result = STATE_WARNING; 500 if (verbose) {
347 printf (_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text (result), buffer, commands[n]); 501 printf(_("received %s\n"), buffer);
348 } 502 }
349 else { 503
350 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER); 504 if (strncmp(buffer, "334", 3) != 0) {
351 printf (_("Execute Error: %s\n"), errbuf); 505 xasprintf(&sc_auth.output, "invalid response received after AUTH LOGIN");
352 result = STATE_UNKNOWN; 506 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
507 break;
353 } 508 }
354 }
355 n++;
356 }
357 509
358 if (authtype != NULL) { 510 /* encode authuser with base64 */
359 if (strcmp (authtype, "LOGIN") == 0) { 511 base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
360 char *abuf; 512 xasprintf(&abuf, "%s\r\n", abuf);
361 int ret; 513 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
362 do { 514 if (verbose) {
363 if (authuser == NULL) { 515 printf(_("sent %s\n"), abuf);
364 result = STATE_CRITICAL; 516 }
365 xasprintf(&error_msg, _("no authuser specified, ")); 517
366 break; 518 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
367 } 519 ssl_established)) <= 0) {
368 if (authpass == NULL) { 520 xasprintf(&sc_auth.output, "recv() failed after sending authuser");
369 result = STATE_CRITICAL; 521 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
370 xasprintf(&error_msg, _("no authpass specified, "));
371 break;
372 }
373
374 /* send AUTH LOGIN */
375 my_send(SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN));
376 if (verbose)
377 printf (_("sent %s\n"), "AUTH LOGIN");
378
379 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
380 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
381 result = STATE_WARNING;
382 break;
383 }
384 if (verbose)
385 printf (_("received %s\n"), buffer);
386
387 if (strncmp (buffer, "334", 3) != 0) {
388 result = STATE_CRITICAL;
389 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
390 break;
391 }
392
393 /* encode authuser with base64 */
394 base64_encode_alloc (authuser, strlen(authuser), &abuf);
395 xasprintf(&abuf, "%s\r\n", abuf);
396 my_send(abuf, strlen(abuf));
397 if (verbose)
398 printf (_("sent %s\n"), abuf);
399
400 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
401 result = STATE_CRITICAL;
402 xasprintf(&error_msg, _("recv() failed after sending authuser, "));
403 break;
404 }
405 if (verbose) {
406 printf (_("received %s\n"), buffer);
407 }
408 if (strncmp (buffer, "334", 3) != 0) {
409 result = STATE_CRITICAL;
410 xasprintf(&error_msg, _("invalid response received after authuser, "));
411 break;
412 }
413 /* encode authpass with base64 */
414 base64_encode_alloc (authpass, strlen(authpass), &abuf);
415 xasprintf(&abuf, "%s\r\n", abuf);
416 my_send(abuf, strlen(abuf));
417 if (verbose) {
418 printf (_("sent %s\n"), abuf);
419 }
420 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
421 result = STATE_CRITICAL;
422 xasprintf(&error_msg, _("recv() failed after sending authpass, "));
423 break;
424 }
425 if (verbose) {
426 printf (_("received %s\n"), buffer);
427 }
428 if (strncmp (buffer, "235", 3) != 0) {
429 result = STATE_CRITICAL;
430 xasprintf(&error_msg, _("invalid response received after authpass, "));
431 break;
432 }
433 break; 522 break;
434 } while (0); 523 }
435 } else {
436 result = STATE_CRITICAL;
437 xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
438 }
439 }
440 524
441 /* tell the server we're done */ 525 if (verbose) {
442 smtp_quit(); 526 printf(_("received %s\n"), buffer);
527 }
443 528
444 /* finally close the connection */ 529 if (strncmp(buffer, "334", 3) != 0) {
445 close (sd); 530 xasprintf(&sc_auth.output, "invalid response received after authuser");
446 } 531 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
532 break;
533 }
447 534
448 /* reset the alarm */ 535 /* encode authpass with base64 */
449 alarm (0); 536 base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
537 xasprintf(&abuf, "%s\r\n", abuf);
538 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
539
540 if (verbose) {
541 printf(_("sent %s\n"), abuf);
542 }
543
544 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
545 ssl_established)) <= 0) {
546 xasprintf(&sc_auth.output, "recv() failed after sending authpass");
547 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
548 break;
549 }
550
551 if (verbose) {
552 printf(_("received %s\n"), buffer);
553 }
450 554
451 microsec = deltime (tv); 555 if (strncmp(buffer, "235", 3) != 0) {
452 elapsed_time = (double)microsec / 1.0e6; 556 xasprintf(&sc_auth.output, "invalid response received after authpass");
557 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
558 break;
559 }
560 break;
561 } while (false);
562 } else {
563 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
564 xasprintf(&sc_auth.output, "only authtype LOGIN is supported");
565 }
453 566
454 if (result == STATE_OK) { 567 mp_add_subcheck_to_check(&overall, sc_auth);
455 if (check_critical_time && elapsed_time > critical_time)
456 result = STATE_CRITICAL;
457 else if (check_warning_time && elapsed_time > warning_time)
458 result = STATE_WARNING;
459 } 568 }
460 569
461 printf (_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), 570 /* tell the server we're done */
462 state_text (result), 571 smtp_quit(config, buffer, socket_descriptor, ssl_established);
463 error_msg,
464 elapsed_time,
465 verbose?", ":"", verbose?buffer:"",
466 fperfdata ("time", elapsed_time, "s",
467 (int)check_warning_time, warning_time,
468 (int)check_critical_time, critical_time,
469 true, 0, false, 0));
470 572
471 return result; 573 /* finally close the connection */
472} 574 close(socket_descriptor);
473 575
576 /* reset the alarm */
577 alarm(0);
474 578
579 long microsec = deltime(start_time);
580 double elapsed_time = (double)microsec / 1.0e6;
475 581
476/* process command-line arguments */ 582 mp_perfdata pd_elapsed_time = perfdata_init();
477int 583 pd_elapsed_time = mp_set_pd_value(pd_elapsed_time, elapsed_time);
478process_arguments (int argc, char **argv) 584 pd_elapsed_time.label = "time";
479{ 585 pd_elapsed_time.uom = "s";
480 int c;
481 char* temp;
482 586
483 bool implicit_tls = false; 587 pd_elapsed_time = mp_pd_set_thresholds(pd_elapsed_time, config.connection_time);
588
589 mp_subcheck sc_connection_time = mp_subcheck_init();
590 xasprintf(&sc_connection_time.output, "connection time: %.3gs", elapsed_time);
591 sc_connection_time =
592 mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_elapsed_time));
593 mp_add_subcheck_to_check(&overall, sc_connection_time);
484 594
595 mp_exit(overall);
596}
597
598/* process command-line arguments */
599check_smtp_config_wrapper process_arguments(int argc, char **argv) {
485 enum { 600 enum {
486 SNI_OPTION 601 SNI_OPTION = CHAR_MAX + 1,
602 output_format_index,
603 ignore_certificate_expiration_index,
487 }; 604 };
488 605
489 int option = 0; 606 int option = 0;
@@ -509,277 +626,301 @@ process_arguments (int argc, char **argv)
509 {"lmtp", no_argument, 0, 'L'}, 626 {"lmtp", no_argument, 0, 'L'},
510 {"ssl", no_argument, 0, 's'}, 627 {"ssl", no_argument, 0, 's'},
511 {"tls", no_argument, 0, 's'}, 628 {"tls", no_argument, 0, 's'},
512 {"starttls",no_argument,0,'S'}, 629 {"starttls", no_argument, 0, 'S'},
513 {"sni", no_argument, 0, SNI_OPTION}, 630 {"sni", no_argument, 0, SNI_OPTION},
514 {"certificate",required_argument,0,'D'}, 631 {"certificate", required_argument, 0, 'D'},
515 {"ignore-quit-failure",no_argument,0,'q'}, 632 {"ignore-quit-failure", no_argument, 0, 'q'},
516 {"proxy",no_argument,0,'r'}, 633 {"proxy", no_argument, 0, 'r'},
517 {0, 0, 0, 0} 634 {"ignore-certificate-expiration", no_argument, 0, ignore_certificate_expiration_index},
635 {"output-format", required_argument, 0, output_format_index},
636 {0, 0, 0, 0}};
637
638 check_smtp_config_wrapper result = {
639 .config = check_smtp_config_init(),
640 .errorcode = OK,
518 }; 641 };
519 642
520 if (argc < 2) 643 if (argc < 2) {
521 return ERROR; 644 result.errorcode = ERROR;
645 return result;
646 }
522 647
523 for (c = 1; c < argc; c++) { 648 for (int index = 1; index < argc; index++) {
524 if (strcmp ("-to", argv[c]) == 0) 649 if (strcmp("-to", argv[index]) == 0) {
525 strcpy (argv[c], "-t"); 650 strcpy(argv[index], "-t");
526 else if (strcmp ("-wt", argv[c]) == 0) 651 } else if (strcmp("-wt", argv[index]) == 0) {
527 strcpy (argv[c], "-w"); 652 strcpy(argv[index], "-w");
528 else if (strcmp ("-ct", argv[c]) == 0) 653 } else if (strcmp("-ct", argv[index]) == 0) {
529 strcpy (argv[c], "-c"); 654 strcpy(argv[index], "-c");
655 }
530 } 656 }
531 657
532 while (1) { 658 unsigned long command_size = 0;
533 c = getopt_long (argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", 659 unsigned long response_size = 0;
534 longopts, &option); 660 bool implicit_tls = false;
661 int server_port_option = 0;
662 while (true) {
663 int opt_index =
664 getopt_long(argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", longopts, &option);
535 665
536 if (c == -1 || c == EOF) 666 if (opt_index == -1 || opt_index == EOF) {
537 break; 667 break;
668 }
538 669
539 switch (c) { 670 switch (opt_index) {
540 case 'H': /* hostname */ 671 case 'H': /* hostname */
541 if (is_host (optarg)) { 672 if (is_host(optarg)) {
542 server_address = optarg; 673 result.config.server_address = optarg;
543 } 674 } else {
544 else { 675 usage2(_("Invalid hostname/address"), optarg);
545 usage2 (_("Invalid hostname/address"), optarg);
546 } 676 }
547 break; 677 break;
548 case 'p': /* port */ 678 case 'p': /* port */
549 if (is_intpos (optarg)) 679 if (is_intpos(optarg)) {
550 server_port_option = atoi (optarg); 680 server_port_option = atoi(optarg);
551 else 681 } else {
552 usage4 (_("Port must be a positive integer")); 682 usage4(_("Port must be a positive integer"));
683 }
553 break; 684 break;
554 case 'F': 685 case 'F':
555 /* localhostname */ 686 /* localhostname */
556 localhostname = strdup(optarg); 687 result.config.localhostname = strdup(optarg);
557 break; 688 break;
558 case 'f': /* from argument */ 689 case 'f': /* from argument */
559 from_arg = optarg + strspn(optarg, "<"); 690 result.config.from_arg = optarg + strspn(optarg, "<");
560 from_arg = strndup(from_arg, strcspn(from_arg, ">")); 691 result.config.from_arg =
561 send_mail_from = 1; 692 strndup(result.config.from_arg, strcspn(result.config.from_arg, ">"));
693 result.config.send_mail_from = true;
562 break; 694 break;
563 case 'A': 695 case 'A':
564 authtype = optarg; 696 result.config.authtype = optarg;
565 use_ehlo = true; 697 result.config.use_ehlo = true;
566 break; 698 break;
567 case 'U': 699 case 'U':
568 authuser = optarg; 700 result.config.authuser = optarg;
569 break; 701 break;
570 case 'P': 702 case 'P':
571 authpass = optarg; 703 result.config.authpass = optarg;
572 break; 704 break;
573 case 'e': /* server expect string on 220 */ 705 case 'e': /* server expect string on 220 */
574 server_expect = optarg; 706 result.config.server_expect = optarg;
575 break; 707 break;
576 case 'C': /* commands */ 708 case 'C': /* commands */
577 if (ncommands >= command_size) { 709 if (result.config.ncommands >= command_size) {
578 command_size+=8; 710 command_size += 8;
579 commands = realloc (commands, sizeof(char *) * command_size); 711 result.config.commands =
580 if (commands == NULL) 712 realloc(result.config.commands, sizeof(char *) * command_size);
581 die (STATE_UNKNOWN, 713 if (result.config.commands == NULL) {
582 _("Could not realloc() units [%d]\n"), ncommands); 714 die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
715 result.config.ncommands);
716 }
583 } 717 }
584 commands[ncommands] = (char *) malloc (sizeof(char) * 255); 718 result.config.commands[result.config.ncommands] = (char *)malloc(sizeof(char) * 255);
585 strncpy (commands[ncommands], optarg, 255); 719 strncpy(result.config.commands[result.config.ncommands], optarg, 255);
586 ncommands++; 720 result.config.ncommands++;
587 break; 721 break;
588 case 'R': /* server responses */ 722 case 'R': /* server responses */
589 if (nresponses >= response_size) { 723 if (result.config.nresponses >= response_size) {
590 response_size += 8; 724 response_size += 8;
591 responses = realloc (responses, sizeof(char *) * response_size); 725 result.config.responses =
592 if (responses == NULL) 726 realloc(result.config.responses, sizeof(char *) * response_size);
593 die (STATE_UNKNOWN, 727 if (result.config.responses == NULL) {
594 _("Could not realloc() units [%d]\n"), nresponses); 728 die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
729 result.config.nresponses);
730 }
595 } 731 }
596 responses[nresponses] = (char *) malloc (sizeof(char) * 255); 732 result.config.responses[result.config.nresponses] = (char *)malloc(sizeof(char) * 255);
597 strncpy (responses[nresponses], optarg, 255); 733 strncpy(result.config.responses[result.config.nresponses], optarg, 255);
598 nresponses++; 734 result.config.nresponses++;
599 break; 735 break;
600 case 'c': /* critical time threshold */ 736 case 'c': /* critical time threshold */ {
601 if (!is_nonnegative (optarg)) 737 mp_range_parsed tmp = mp_parse_range_string(optarg);
602 usage4 (_("Critical time must be a positive")); 738 if (tmp.error != MP_PARSING_SUCCESS) {
603 else { 739 die(STATE_UNKNOWN, "failed to parse critical time threshold");
604 critical_time = strtod (optarg, NULL);
605 check_critical_time = true;
606 } 740 }
607 break; 741 result.config.connection_time =
608 case 'w': /* warning time threshold */ 742 mp_thresholds_set_warn(result.config.connection_time, tmp.range);
609 if (!is_nonnegative (optarg)) 743 } break;
610 usage4 (_("Warning time must be a positive")); 744 case 'w': /* warning time threshold */ {
611 else { 745 mp_range_parsed tmp = mp_parse_range_string(optarg);
612 warning_time = strtod (optarg, NULL); 746 if (tmp.error != MP_PARSING_SUCCESS) {
613 check_warning_time = true; 747 die(STATE_UNKNOWN, "failed to parse warning time threshold");
614 } 748 }
615 break; 749 result.config.connection_time =
616 case 'v': /* verbose */ 750 mp_thresholds_set_crit(result.config.connection_time, tmp.range);
751 } break;
752 case 'v': /* verbose */
617 verbose++; 753 verbose++;
618 break; 754 break;
619 case 'q': 755 case 'q':
620 ignore_send_quit_failure = true; /* ignore problem sending QUIT */ 756 result.config.ignore_send_quit_failure = true; /* ignore problem sending QUIT */
621 break; 757 break;
622 case 't': /* timeout */ 758 case 't': /* timeout */
623 if (is_intnonneg (optarg)) { 759 if (is_intnonneg(optarg)) {
624 socket_timeout = atoi (optarg); 760 socket_timeout = atoi(optarg);
625 } 761 } else {
626 else { 762 usage4(_("Timeout interval must be a positive integer"));
627 usage4 (_("Timeout interval must be a positive integer"));
628 } 763 }
629 break; 764 break;
630 case 'D': 765 case 'D': {
631 /* Check SSL cert validity */ 766 /* Check SSL cert validity */
632#ifdef USE_OPENSSL 767#ifdef MOPL_USE_OPENSSL
633 if ((temp=strchr(optarg,','))!=NULL) { 768 char *temp;
634 *temp='\0'; 769 if ((temp = strchr(optarg, ',')) != NULL) {
635 if (!is_intnonneg (optarg)) 770 *temp = '\0';
636 usage2 ("Invalid certificate expiration period", optarg); 771 if (!is_intnonneg(optarg)) {
637 days_till_exp_warn = atoi(optarg); 772 usage2("Invalid certificate expiration period", optarg);
638 *temp=','; 773 }
639 temp++; 774 result.config.days_till_exp_warn = atoi(optarg);
640 if (!is_intnonneg (temp)) 775 *temp = ',';
641 usage2 (_("Invalid certificate expiration period"), temp); 776 temp++;
642 days_till_exp_crit = atoi (temp); 777 if (!is_intnonneg(temp)) {
643 } 778 usage2(_("Invalid certificate expiration period"), temp);
644 else { 779 }
645 days_till_exp_crit=0; 780 result.config.days_till_exp_crit = atoi(temp);
646 if (!is_intnonneg (optarg)) 781 } else {
647 usage2 ("Invalid certificate expiration period", optarg); 782 result.config.days_till_exp_crit = 0;
648 days_till_exp_warn = atoi (optarg); 783 if (!is_intnonneg(optarg)) {
649 } 784 usage2("Invalid certificate expiration period", optarg);
650 check_cert = true; 785 }
651 ignore_send_quit_failure = true; 786 result.config.days_till_exp_warn = atoi(optarg);
787 }
788 result.config.ignore_send_quit_failure = true;
652#else 789#else
653 usage (_("SSL support not available - install OpenSSL and recompile")); 790 usage(_("SSL support not available - install OpenSSL and recompile"));
654#endif 791#endif
655 implicit_tls = true; 792 implicit_tls = true;
656 // fallthrough 793 // fallthrough
657 case 's': 794 case 's':
658 /* ssl */ 795 /* ssl */
659 use_ssl = true; 796 result.config.use_ssl = true;
660 server_port = SMTPS_PORT; 797 result.config.server_port = SMTPS_PORT;
661 break; 798 break;
662 case 'S': 799 case 'S':
663 /* starttls */ 800 /* starttls */
664 use_starttls = true; 801 result.config.use_starttls = true;
665 use_ehlo = true; 802 result.config.use_ehlo = true;
666 break; 803 break;
804 }
667 case SNI_OPTION: 805 case SNI_OPTION:
668#ifdef HAVE_SSL 806#ifdef HAVE_SSL
669 use_sni = true; 807 result.config.use_sni = true;
670#else 808#else
671 usage (_("SSL support not available - install OpenSSL and recompile")); 809 usage(_("SSL support not available - install OpenSSL and recompile"));
672#endif 810#endif
673 break; 811 break;
674 case 'r': 812 case 'r':
675 use_proxy_prefix = true; 813 result.config.use_proxy_prefix = true;
676 break; 814 break;
677 case 'L': 815 case 'L':
678 use_lhlo = true; 816 result.config.use_lhlo = true;
679 break; 817 break;
680 case '4': 818 case '4':
681 address_family = AF_INET; 819 address_family = AF_INET;
682 break; 820 break;
683 case '6': 821 case '6':
684#ifdef USE_IPV6
685 address_family = AF_INET6; 822 address_family = AF_INET6;
686#else
687 usage4 (_("IPv6 support not available"));
688#endif
689 break; 823 break;
690 case 'V': /* version */ 824 case 'V': /* version */
691 print_revision (progname, NP_VERSION); 825 print_revision(progname, NP_VERSION);
692 exit (STATE_UNKNOWN); 826 exit(STATE_UNKNOWN);
693 case 'h': /* help */ 827 case 'h': /* help */
694 print_help (); 828 print_help();
695 exit (STATE_UNKNOWN); 829 exit(STATE_UNKNOWN);
696 case '?': /* help */ 830 case '?': /* help */
697 usage5 (); 831 usage5();
832 case output_format_index: {
833 parsed_output_format parser = mp_parse_output_format(optarg);
834 if (!parser.parsing_success) {
835 // TODO List all available formats here, maybe add anothoer usage function
836 printf("Invalid output format: %s\n", optarg);
837 exit(STATE_UNKNOWN);
838 }
839
840 result.config.output_format_is_set = true;
841 result.config.output_format = parser.output_format;
842 break;
843 }
844 case ignore_certificate_expiration_index: {
845 result.config.ignore_certificate_expiration = true;
846 }
698 } 847 }
699 } 848 }
700 849
701 c = optind; 850 int c = optind;
702 if (server_address == NULL) { 851 if (result.config.server_address == NULL) {
703 if (argv[c]) { 852 if (argv[c]) {
704 if (is_host (argv[c])) 853 if (is_host(argv[c])) {
705 server_address = argv[c]; 854 result.config.server_address = argv[c];
706 else 855 } else {
707 usage2 (_("Invalid hostname/address"), argv[c]); 856 usage2(_("Invalid hostname/address"), argv[c]);
708 } 857 }
709 else { 858 } else {
710 xasprintf (&server_address, "127.0.0.1"); 859 result.config.server_address = strdup("localhost");
711 } 860 }
712 } 861 }
713 862
714 if (server_expect == NULL) 863 if (result.config.use_starttls && result.config.use_ssl) {
715 server_expect = strdup (SMTP_EXPECT);
716
717 if (mail_command == NULL)
718 mail_command = strdup("MAIL ");
719
720 if (from_arg==NULL)
721 from_arg = strdup(" ");
722
723 if (use_starttls && use_ssl) {
724 if (implicit_tls) { 864 if (implicit_tls) {
725 use_ssl = false; 865 result.config.use_ssl = false;
726 server_port = SMTP_PORT;
727 } else { 866 } else {
728 usage4 (_("Set either -s/--ssl/--tls or -S/--starttls")); 867 usage4(_("Set either -s/--ssl/--tls or -S/--starttls"));
729 } 868 }
730 } 869 }
731 870
732 if (server_port_option != 0) { 871 if (server_port_option != 0) {
733 server_port = server_port_option; 872 result.config.server_port = server_port_option;
734 } 873 }
735 874
736 return validate_arguments (); 875 if (result.config.authtype) {
737} 876 if (strcmp(result.config.authtype, "LOGIN") == 0) {
738 877 if (result.config.authuser == NULL) {
739 878 usage4("no authuser specified");
879 }
880 if (result.config.authpass == NULL) {
881 usage4("no authpass specified");
882 }
883 } else {
884 usage4("only authtype LOGIN is supported");
885 }
886 }
740 887
741int 888 return result;
742validate_arguments (void)
743{
744 return OK;
745} 889}
746 890
747 891char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int socket_descriptor,
748void 892 bool ssl_established) {
749smtp_quit(void) 893 int sent_bytes =
750{ 894 my_send(config, SMTP_QUIT, strlen(SMTP_QUIT), socket_descriptor, ssl_established);
751 int bytes; 895 if (sent_bytes < 0) {
752 int n; 896 if (config.ignore_send_quit_failure) {
753 897 if (verbose) {
754 n = my_send(SMTP_QUIT, strlen(SMTP_QUIT));
755 if(n < 0) {
756 if(ignore_send_quit_failure) {
757 if(verbose) {
758 printf(_("Connection closed by server before sending QUIT command\n")); 898 printf(_("Connection closed by server before sending QUIT command\n"));
759 } 899 }
760 return; 900 return buffer;
761 } 901 }
762 die (STATE_UNKNOWN, 902 die(STATE_UNKNOWN, _("Connection closed by server before sending QUIT command\n"));
763 _("Connection closed by server before sending QUIT command\n"));
764 } 903 }
765 904
766 if (verbose) 905 if (verbose) {
767 printf(_("sent %s\n"), "QUIT"); 906 printf(_("sent %s\n"), "QUIT");
907 }
768 908
769 /* read the response but don't care about problems */ 909 /* read the response but don't care about problems */
770 bytes = recvlines(buffer, MAX_INPUT_BUFFER); 910 int bytes = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established);
771 if (verbose) { 911 if (verbose) {
772 if (bytes < 0) 912 if (bytes < 0) {
773 printf(_("recv() failed after QUIT.")); 913 printf(_("recv() failed after QUIT."));
774 else if (bytes == 0) 914 } else if (bytes == 0) {
775 printf(_("Connection reset by peer.")); 915 printf(_("Connection reset by peer."));
776 else { 916 } else {
777 buffer[bytes] = '\0'; 917 buffer[bytes] = '\0';
778 printf(_("received %s\n"), buffer); 918 printf(_("received %s\n"), buffer);
779 } 919 }
780 } 920 }
781}
782 921
922 return buffer;
923}
783 924
784/* 925/*
785 * Receive one line, copy it into buf and nul-terminate it. Returns the 926 * Receive one line, copy it into buf and nul-terminate it. Returns the
@@ -790,24 +931,23 @@ smtp_quit(void)
790 * function which buffers the data, move that to netutils.c and change 931 * function which buffers the data, move that to netutils.c and change
791 * check_smtp and other plugins to use that. Also, remove (\r)\n. 932 * check_smtp and other plugins to use that. Also, remove (\r)\n.
792 */ 933 */
793int 934int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor,
794recvline(char *buf, size_t bufsize) 935 bool ssl_established) {
795{
796 int result; 936 int result;
797 unsigned i; 937 size_t counter;
798 938
799 for (i = result = 0; i < bufsize - 1; i++) { 939 for (counter = result = 0; counter < bufsize - 1; counter++) {
800 if ((result = my_recv(&buf[i], 1)) != 1) 940 if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) {
801 break; 941 break;
802 if (buf[i] == '\n') { 942 }
803 buf[++i] = '\0'; 943 if (buf[counter] == '\n') {
804 return i; 944 buf[++counter] = '\0';
945 return (int)counter;
805 } 946 }
806 } 947 }
807 return (result == 1 || i == 0) ? -2 : result; /* -2 if out of space */ 948 return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */
808} 949}
809 950
810
811/* 951/*
812 * Receive one or more lines, copy them into buf and nul-terminate it. Returns 952 * Receive one or more lines, copy them into buf and nul-terminate it. Returns
813 * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on 953 * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on
@@ -822,117 +962,114 @@ recvline(char *buf, size_t bufsize)
822 * 962 *
823 * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n. 963 * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n.
824 */ 964 */
825int 965int recvlines(check_smtp_config config, char *buf, size_t bufsize, int socket_descriptor,
826recvlines(char *buf, size_t bufsize) 966 bool ssl_established) {
827{ 967 int result;
828 int result, i; 968 int counter;
829 969
830 for (i = 0; /* forever */; i += result) 970 for (counter = 0; /* forever */; counter += result) {
831 if (!((result = recvline(buf + i, bufsize - i)) > 3 && 971 if (!((result = recvline(buf + counter, bufsize - counter, config, socket_descriptor,
832 isdigit((int)buf[i]) && 972 ssl_established)) > 3 &&
833 isdigit((int)buf[i + 1]) && 973 isdigit((int)buf[counter]) && isdigit((int)buf[counter + 1]) &&
834 isdigit((int)buf[i + 2]) && 974 isdigit((int)buf[counter + 2]) && buf[counter + 3] == '-')) {
835 buf[i + 3] == '-'))
836 break; 975 break;
976 }
977 }
837 978
838 return (result <= 0) ? result : result + i; 979 return (result <= 0) ? result : result + counter;
839} 980}
840 981
841 982int my_close(int socket_descriptor) {
842int
843my_close (void)
844{
845 int result; 983 int result;
846 result = close(sd); 984 result = close(socket_descriptor);
847#ifdef HAVE_SSL 985#ifdef HAVE_SSL
848 np_net_ssl_cleanup(); 986 np_net_ssl_cleanup();
849#endif 987#endif
850 return result; 988 return result;
851} 989}
852 990
853 991void print_help(void) {
854void
855print_help (void)
856{
857 char *myport; 992 char *myport;
858 xasprintf (&myport, "%d", SMTP_PORT); 993 xasprintf(&myport, "%d", SMTP_PORT);
859 994
860 print_revision (progname, NP_VERSION); 995 print_revision(progname, NP_VERSION);
861 996
862 printf ("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n"); 997 printf("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n");
863 printf (COPYRIGHT, copyright, email); 998 printf(COPYRIGHT, copyright, email);
864 999
865 printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host.")); 1000 printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host."));
866 1001
867 printf ("\n\n"); 1002 printf("\n\n");
868 1003
869 print_usage (); 1004 print_usage();
870 1005
871 printf (UT_HELP_VRSN); 1006 printf(UT_HELP_VRSN);
872 printf (UT_EXTRA_OPTS); 1007 printf(UT_EXTRA_OPTS);
873 1008
874 printf (UT_HOST_PORT, 'p', myport); 1009 printf(UT_HOST_PORT, 'p', myport);
875 1010
876 printf (UT_IPv46); 1011 printf(UT_IPv46);
877 1012
878 printf (" %s\n", "-e, --expect=STRING"); 1013 printf(" %s\n", "-e, --expect=STRING");
879 printf (_(" String to expect in first line of server response (default: '%s')\n"), SMTP_EXPECT); 1014 printf(_(" String to expect in first line of server response (default: '%s')\n"),
880 printf (" %s\n", "-C, --command=STRING"); 1015 SMTP_EXPECT);
881 printf (" %s\n", _("SMTP command (may be used repeatedly)")); 1016 printf(" %s\n", "-C, --command=STRING");
882 printf (" %s\n", "-R, --response=STRING"); 1017 printf(" %s\n", _("SMTP command (may be used repeatedly)"));
883 printf (" %s\n", _("Expected response to command (may be used repeatedly)")); 1018 printf(" %s\n", "-R, --response=STRING");
884 printf (" %s\n", "-f, --from=STRING"); 1019 printf(" %s\n", _("Expected response to command (may be used repeatedly)"));
885 printf (" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")), 1020 printf(" %s\n", "-f, --from=STRING");
886 printf (" %s\n", "-F, --fqdn=STRING"); 1021 printf(" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")),
887 printf (" %s\n", _("FQDN used for HELO")); 1022 printf(" %s\n", "-F, --fqdn=STRING");
888 printf (" %s\n", "-r, --proxy"); 1023 printf(" %s\n", _("FQDN used for HELO"));
889 printf (" %s\n", _("Use PROXY protocol prefix for the connection.")); 1024 printf(" %s\n", "-r, --proxy");
1025 printf(" %s\n", _("Use PROXY protocol prefix for the connection."));
890#ifdef HAVE_SSL 1026#ifdef HAVE_SSL
891 printf (" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); 1027 printf(" %s\n", "-D, --certificate=INTEGER[,INTEGER]");
892 printf (" %s\n", _("Minimum number of days a certificate has to be valid.")); 1028 printf(" %s\n", _("Minimum number of days a certificate has to be valid."));
893 printf (" %s\n", "-s, --ssl, --tls"); 1029 printf(" %s\n", "-s, --ssl, --tls");
894 printf (" %s\n", _("Use SSL/TLS for the connection.")); 1030 printf(" %s\n", _("Use SSL/TLS for the connection."));
895 printf (_(" Sets default port to %d.\n"), SMTPS_PORT); 1031 printf(_(" Sets default port to %d.\n"), SMTPS_PORT);
896 printf (" %s\n", "-S, --starttls"); 1032 printf(" %s\n", "-S, --starttls");
897 printf (" %s\n", _("Use STARTTLS for the connection.")); 1033 printf(" %s\n", _("Use STARTTLS for the connection."));
898 printf (" %s\n", "--sni"); 1034 printf(" %s\n", "--sni");
899 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 1035 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
900#endif 1036#endif
901 1037
902 printf (" %s\n", "-A, --authtype=STRING"); 1038 printf(" %s\n", "-A, --authtype=STRING");
903 printf (" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)")); 1039 printf(" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)"));
904 printf (" %s\n", "-U, --authuser=STRING"); 1040 printf(" %s\n", "-U, --authuser=STRING");
905 printf (" %s\n", _("SMTP AUTH username")); 1041 printf(" %s\n", _("SMTP AUTH username"));
906 printf (" %s\n", "-P, --authpass=STRING"); 1042 printf(" %s\n", "-P, --authpass=STRING");
907 printf (" %s\n", _("SMTP AUTH password")); 1043 printf(" %s\n", _("SMTP AUTH password"));
908 printf (" %s\n", "-L, --lmtp"); 1044 printf(" %s\n", "-L, --lmtp");
909 printf (" %s\n", _("Send LHLO instead of HELO/EHLO")); 1045 printf(" %s\n", _("Send LHLO instead of HELO/EHLO"));
910 printf (" %s\n", "-q, --ignore-quit-failure"); 1046 printf(" %s\n", "-q, --ignore-quit-failure");
911 printf (" %s\n", _("Ignore failure when sending QUIT command to server")); 1047 printf(" %s\n", _("Ignore failure when sending QUIT command to server"));
912 1048 printf(" %s\n", "--ignore-certificate-expiration");
913 printf (UT_WARN_CRIT); 1049 printf(" %s\n", _("Ignore certificate expiration"));
914
915 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
916 1050
917 printf (UT_VERBOSE); 1051 printf(UT_WARN_CRIT);
918 1052
919 printf("\n"); 1053 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
920 printf ("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return"));
921 printf ("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful"));
922 printf ("%s\n", _("connects, but incorrect response messages from the host result in"));
923 printf ("%s\n", _("STATE_WARNING return values."));
924 1054
925 printf (UT_SUPPORT); 1055 printf(UT_OUTPUT_FORMAT);
926}
927 1056
1057 printf(UT_VERBOSE);
928 1058
1059 printf("\n");
1060 printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return"));
1061 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful"));
1062 printf("%s\n", _("connects, but incorrect response messages from the host result in"));
1063 printf("%s\n", _("STATE_WARNING return values."));
929 1064
930void 1065 printf(UT_SUPPORT);
931print_usage (void)
932{
933 printf ("%s\n", _("Usage:"));
934 printf ("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n", progname);
935 printf ("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n");
936 printf ("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] [-v] \n");
937} 1066}
938 1067
1068void print_usage(void) {
1069 printf("%s\n", _("Usage:"));
1070 printf("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n",
1071 progname);
1072 printf("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n");
1073 printf("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] "
1074 "[-v] \n");
1075}
diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h
new file mode 100644
index 00000000..47826362
--- /dev/null
+++ b/plugins/check_smtp.d/config.h
@@ -0,0 +1,96 @@
1#pragma once
2
3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
6#include <stddef.h>
7#include <string.h>
8
9enum {
10 SMTP_PORT = 25,
11 SMTPS_PORT = 465
12};
13
14#define SMTP_EXPECT "220"
15
16typedef struct {
17 int server_port;
18 char *server_address;
19 char *localhostname;
20 char *server_expect;
21 bool ignore_send_quit_failure;
22
23 mp_thresholds connection_time;
24
25 bool use_ehlo;
26 bool use_lhlo;
27
28 char *from_arg;
29 bool send_mail_from;
30
31 unsigned long ncommands;
32 char **commands;
33
34 unsigned long nresponses;
35 char **responses;
36
37 char *authtype;
38 char *authuser;
39 char *authpass;
40
41 bool use_proxy_prefix;
42#ifdef HAVE_SSL
43 unsigned int days_till_exp_warn;
44 unsigned int days_till_exp_crit;
45 bool use_ssl;
46 bool use_starttls;
47 bool use_sni;
48
49 bool ignore_certificate_expiration;
50#endif
51
52 bool output_format_is_set;
53 mp_output_format output_format;
54} check_smtp_config;
55
56check_smtp_config check_smtp_config_init() {
57 check_smtp_config tmp = {
58 .server_port = SMTP_PORT,
59 .server_address = NULL,
60 .localhostname = NULL,
61
62 .server_expect = SMTP_EXPECT,
63 .ignore_send_quit_failure = false,
64
65 .connection_time = mp_thresholds_init(),
66 .use_ehlo = false,
67 .use_lhlo = false,
68
69 .from_arg = strdup(" "),
70 .send_mail_from = false,
71
72 .ncommands = 0,
73 .commands = NULL,
74
75 .nresponses = 0,
76 .responses = NULL,
77
78 .authtype = NULL,
79 .authuser = NULL,
80 .authpass = NULL,
81
82 .use_proxy_prefix = false,
83#ifdef HAVE_SSL
84 .days_till_exp_warn = 0,
85 .days_till_exp_crit = 0,
86 .use_ssl = false,
87 .use_starttls = false,
88 .use_sni = false,
89
90 .ignore_certificate_expiration = false,
91#endif
92
93 .output_format_is_set = false,
94 };
95 return tmp;
96}
diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c
index 937b3a5d..09196dc4 100644
--- a/plugins/check_snmp.c
+++ b/plugins/check_snmp.c
@@ -1,699 +1,397 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_snmp plugin 3 * Monitoring check_snmp plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 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_snmp plugin 10 * This file contains the check_snmp plugin
11* 11 *
12* Check status of remote machines and obtain system information via SNMP 12 * Check status of remote machines and obtain system information via SNMP
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_snmp"; 31const char *progname = "check_snmp";
32const char *copyright = "1999-2007"; 32const char *copyright = "1999-2024";
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 "runcmd.h" 36#include "./runcmd.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_cmd.h" 38#include "../lib/states.h"
39 39
40#define DEFAULT_COMMUNITY "public" 40#include "../lib/utils_base.h"
41#define DEFAULT_PORT "161" 41#include "../lib/output.h"
42#define DEFAULT_MIBLIST "ALL" 42#include "check_snmp.d/check_snmp_helpers.h"
43#define DEFAULT_PROTOCOL "1" 43
44#define DEFAULT_RETRIES 5 44#include <strings.h>
45#include <stdint.h>
46
47#include "check_snmp.d/config.h"
48#include <stdlib.h>
49#include <arpa/inet.h>
50#include <net-snmp/library/parse.h>
51#include <net-snmp/net-snmp-config.h>
52#include <net-snmp/net-snmp-includes.h>
53#include <net-snmp/library/snmp.h>
54#include <net-snmp/library/keytools.h>
55#include <net-snmp/library/snmp_api.h>
56#include <net-snmp/session_api.h>
57#include <net-snmp/definitions.h>
58#include <net-snmp/library/asn1.h>
59#include <net-snmp/mib_api.h>
60#include <net-snmp/library/snmp_impl.h>
61#include <string.h>
62#include "../gl/regex.h"
63#include "../gl/base64.h"
64#include <assert.h>
65
66const char DEFAULT_COMMUNITY[] = "public";
67const char DEFAULT_MIBLIST[] = "ALL";
45#define DEFAULT_AUTH_PROTOCOL "MD5" 68#define DEFAULT_AUTH_PROTOCOL "MD5"
46#define DEFAULT_PRIV_PROTOCOL "DES"
47#define DEFAULT_DELIMITER "="
48#define DEFAULT_OUTPUT_DELIMITER " "
49#define DEFAULT_BUFFER_SIZE 100
50
51#define mark(a) ((a)!=0?"*":"")
52
53#define CHECK_UNDEF 0
54#define CRIT_PRESENT 1
55#define CRIT_STRING 2
56#define CRIT_REGEX 4
57#define WARN_PRESENT 8
58#define WARN_STRING 16
59#define WARN_REGEX 32
60
61#define OID_COUNT_STEP 8
62
63/* Longopts only arguments */
64#define L_CALCULATE_RATE CHAR_MAX+1
65#define L_RATE_MULTIPLIER CHAR_MAX+2
66#define L_INVERT_SEARCH CHAR_MAX+3
67#define L_OFFSET CHAR_MAX+4
68#define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX+5
69
70/* Gobble to string - stop incrementing c when c[0] match one of the
71 * characters in s */
72#define GOBBLE_TOS(c, s) while(c[0]!='\0' && strchr(s, c[0])==NULL) { c++; }
73/* Given c, keep track of backslashes (bk) and double-quotes (dq)
74 * from c[0] */
75#define COUNT_SEQ(c, bk, dq) switch(c[0]) {\
76 case '\\': \
77 if (bk) bk--; \
78 else bk++; \
79 break; \
80 case '"': \
81 if (!dq) { dq++; } \
82 else if(!bk) { dq--; } \
83 else { bk--; } \
84 break; \
85 }
86
87
88 69
89int process_arguments (int, char **); 70#ifdef HAVE_USM_DES_PRIV_PROTOCOL
90int validate_arguments (void); 71# define DEFAULT_PRIV_PROTOCOL "DES"
91char *thisarg (char *str); 72#else
92char *nextarg (char *str); 73# define DEFAULT_PRIV_PROTOCOL "AES"
93void print_usage (void); 74#endif
94void print_help (void);
95char *multiply (char *str);
96
97#include "regex.h"
98char regex_expect[MAX_INPUT_BUFFER] = "";
99regex_t preg;
100regmatch_t pmatch[10];
101char errbuf[MAX_INPUT_BUFFER] = "";
102char perfstr[MAX_INPUT_BUFFER] = "| ";
103int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
104int eflags = 0;
105int errcode, excode;
106
107char *server_address = NULL;
108char *community = NULL;
109char **contextargs = NULL;
110char *context = NULL;
111char **authpriv = NULL;
112char *proto = NULL;
113char *seclevel = NULL;
114char *secname = NULL;
115char *authproto = NULL;
116char *privproto = NULL;
117char *authpasswd = NULL;
118char *privpasswd = NULL;
119int nulloid = STATE_UNKNOWN;
120char **oids = NULL;
121size_t oids_size = 0;
122char *label;
123char *units;
124char *port;
125char *snmpcmd;
126char string_value[MAX_INPUT_BUFFER] = "";
127int invert_search=0;
128char **labels = NULL;
129char **unitv = NULL;
130size_t nlabels = 0;
131size_t labels_size = OID_COUNT_STEP;
132size_t nunits = 0;
133size_t unitv_size = OID_COUNT_STEP;
134size_t numoids = 0;
135int numauthpriv = 0;
136int numcontext = 0;
137int verbose = 0;
138bool usesnmpgetnext = false;
139char *warning_thresholds = NULL;
140char *critical_thresholds = NULL;
141thresholds **thlds;
142size_t thlds_size = OID_COUNT_STEP;
143double *response_value;
144size_t response_size = OID_COUNT_STEP;
145int retries = 0;
146int *eval_method;
147size_t eval_size = OID_COUNT_STEP;
148char *delimiter;
149char *output_delim;
150char *miblist = NULL;
151bool needmibs = false;
152int calculate_rate = 0;
153double offset = 0.0;
154int rate_multiplier = 1;
155state_data *previous_state;
156double *previous_value;
157size_t previous_size = OID_COUNT_STEP;
158int perf_labels = 1;
159char* ip_version = "";
160double multiplier = 1.0;
161char *fmtstr = "";
162bool fmtstr_set = false;
163char buffer[DEFAULT_BUFFER_SIZE];
164bool ignore_mib_parsing_errors = false;
165
166static char *fix_snmp_range(char *th)
167{
168 double left, right;
169 char *colon, *ret;
170
171 if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0')
172 return th;
173
174 left = strtod(th, NULL);
175 right = strtod(colon + 1, NULL);
176 if (right >= left)
177 return th;
178
179 if ((ret = malloc(strlen(th) + 2)) == NULL)
180 die(STATE_UNKNOWN, _("Cannot malloc"));
181 *colon = '\0';
182 sprintf(ret, "@%s:%s", colon + 1, th);
183 free(th);
184 return ret;
185}
186 75
187int 76typedef struct proces_arguments_wrapper {
188main (int argc, char **argv) 77 int errorcode;
189{ 78 check_snmp_config config;
190 int len, total_oids; 79} process_arguments_wrapper;
191 size_t line;
192 unsigned int bk_count = 0, dq_count = 0;
193 int iresult = STATE_UNKNOWN;
194 int result = STATE_UNKNOWN;
195 int return_code = 0;
196 int external_error = 0;
197 char **command_line = NULL;
198 char *cl_hidden_auth = NULL;
199 char *oidname = NULL;
200 char *response = NULL;
201 char *mult_resp = NULL;
202 char *outbuff;
203 char *ptr = NULL;
204 char *show = NULL;
205 char *th_warn=NULL;
206 char *th_crit=NULL;
207 char type[8] = "";
208 output chld_out, chld_err;
209 char *previous_string=NULL;
210 char *ap=NULL;
211 char *state_string=NULL;
212 size_t response_length, current_length, string_length;
213 char *temp_string=NULL;
214 char *quote_string=NULL;
215 time_t current_time;
216 double temp_double;
217 time_t duration;
218 char *conv = "12345678";
219 int is_counter=0;
220
221 setlocale (LC_ALL, "");
222 bindtextdomain (PACKAGE, LOCALEDIR);
223 textdomain (PACKAGE);
224
225 labels = malloc (labels_size * sizeof(*labels));
226 unitv = malloc (unitv_size * sizeof(*unitv));
227 thlds = malloc (thlds_size * sizeof(*thlds));
228 response_value = malloc (response_size * sizeof(*response_value));
229 previous_value = malloc (previous_size * sizeof(*previous_value));
230 eval_method = calloc (eval_size, sizeof(*eval_method));
231 oids = calloc(oids_size, sizeof (char *));
232
233 label = strdup ("SNMP");
234 units = strdup ("");
235 port = strdup (DEFAULT_PORT);
236 outbuff = strdup ("");
237 delimiter = strdup (" = ");
238 output_delim = strdup (DEFAULT_OUTPUT_DELIMITER);
239 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
240 retries = DEFAULT_RETRIES;
241 80
242 np_init( (char *) progname, argc, argv ); 81static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
82static char *trim_whitespaces_and_check_quoting(char *str);
83static char *get_next_argument(char *str);
84void print_usage(void);
85void print_help(void);
243 86
244 /* Parse extra opts if any */ 87int verbose = 0;
245 argv=np_extra_opts (&argc, argv, progname);
246
247 np_set_args(argc, argv);
248 88
249 time(&current_time); 89typedef struct {
90 int errorcode;
91 char *state_string;
92} gen_state_string_type;
93gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) {
94 char *encoded_string = NULL;
95 gen_state_string_type result = {.errorcode = OK, .state_string = NULL};
96
97 if (verbose > 1) {
98 printf("%s:\n", __FUNCTION__);
99 for (size_t i = 0; i < num_of_entries; i++) {
100 printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp));
101 switch (entries[i].type) {
102 case ASN_GAUGE:
103 printf("Type GAUGE\n");
104 break;
105 case ASN_TIMETICKS:
106 printf("Type TIMETICKS\n");
107 break;
108 case ASN_COUNTER:
109 printf("Type COUNTER\n");
110 break;
111 case ASN_UINTEGER:
112 printf("Type UINTEGER\n");
113 break;
114 case ASN_COUNTER64:
115 printf("Type COUNTER64\n");
116 break;
117 case ASN_FLOAT:
118 printf("Type FLOAT\n");
119 break;
120 case ASN_DOUBLE:
121 printf("Type DOUBLE\n");
122 break;
123 case ASN_INTEGER:
124 printf("Type INTEGER\n");
125 break;
126 }
250 127
251 if (process_arguments (argc, argv) == ERROR) 128 switch (entries[i].type) {
252 usage4 (_("Could not parse arguments")); 129 case ASN_GAUGE:
253 130 case ASN_TIMETICKS:
254 if(calculate_rate) { 131 case ASN_COUNTER:
255 if (!strcmp(label, "SNMP")) 132 case ASN_UINTEGER:
256 label = strdup("SNMP RATE"); 133 case ASN_COUNTER64:
257 134 printf("Value %llu\n", entries[i].value.uIntVal);
258 size_t i = 0; 135 break;
259 136 case ASN_FLOAT:
260 previous_state = np_state_read(); 137 case ASN_DOUBLE:
261 if(previous_state!=NULL) { 138 printf("Value %f\n", entries[i].value.doubleVal);
262 /* Split colon separated values */ 139 break;
263 previous_string = strdup((char *) previous_state->data); 140 case ASN_INTEGER:
264 while((ap = strsep(&previous_string, ":")) != NULL) { 141 printf("Value %lld\n", entries[i].value.intVal);
265 if(verbose>2) 142 break;
266 printf("State for %zd=%s\n", i, ap);
267 while (i >= previous_size) {
268 previous_size += OID_COUNT_STEP;
269 previous_value = realloc(previous_value, previous_size * sizeof(*previous_value));
270 }
271 previous_value[i++]=strtod(ap,NULL);
272 } 143 }
273 } 144 }
274 } 145 }
275 146
276 /* Populate the thresholds */ 147 idx_t encoded = base64_encode_alloc((const char *)entries,
277 th_warn=warning_thresholds; 148 (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)),
278 th_crit=critical_thresholds; 149 &encoded_string);
279 for (size_t i = 0; i < numoids; i++) {
280 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
281 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
282 /* translate "2:1" to "@1:2" for backwards compatibility */
283 w = w ? fix_snmp_range(w) : NULL;
284 c = c ? fix_snmp_range(c) : NULL;
285
286 while (i >= thlds_size) {
287 thlds_size += OID_COUNT_STEP;
288 thlds = realloc(thlds, thlds_size * sizeof(*thlds));
289 }
290 150
291 /* Skip empty thresholds, while avoiding segfault */ 151 if (encoded > 0 && encoded_string != NULL) {
292 set_thresholds(&thlds[i], 152 // success
293 w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL, 153 if (verbose > 1) {
294 c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL); 154 printf("encoded string: %s\n", encoded_string);
295 if (w) { 155 printf("encoded string length: %lu\n", strlen(encoded_string));
296 th_warn=strchr(th_warn, ',');
297 if (th_warn) th_warn++;
298 free(w);
299 }
300 if (c) {
301 th_crit=strchr(th_crit, ',');
302 if (th_crit) th_crit++;
303 free(c);
304 } 156 }
157 result.state_string = encoded_string;
158 return result;
305 } 159 }
160 result.errorcode = ERROR;
161 return result;
162}
306 163
307 /* Create the command array to execute */ 164typedef struct {
308 if(usesnmpgetnext) { 165 int errorcode;
309 snmpcmd = strdup (PATH_TO_SNMPGETNEXT); 166 check_snmp_state_entry *state;
310 }else{ 167} recover_state_data_type;
311 snmpcmd = strdup (PATH_TO_SNMPGET); 168recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) {
169 recover_state_data_type result = {.errorcode = OK, .state = NULL};
170
171 if (verbose > 1) {
172 printf("%s:\n", __FUNCTION__);
173 printf("State string: %s\n", state_string);
174 printf("State string length: %lu\n", state_string_length);
312 } 175 }
313 176
314 /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ 177 idx_t outlen = 0;
178 bool decoded =
179 base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen);
315 180
316 unsigned index = 0; 181 if (!decoded) {
317 command_line = calloc (11 + numcontext + numauthpriv + 1 + numoids + 1, sizeof (char *)); 182 if (verbose) {
183 printf("Failed to decode state string\n");
184 }
185 // failure to decode
186 result.errorcode = ERROR;
187 return result;
188 }
318 189
319 command_line[index++] = snmpcmd; 190 if (result.state == NULL) {
320 command_line[index++] = strdup ("-Le"); 191 // Memory Error?
321 command_line[index++] = strdup ("-t"); 192 result.errorcode = ERROR;
322 xasprintf (&command_line[index++], "%d", timeout_interval); 193 return result;
323 command_line[index++] = strdup ("-r"); 194 }
324 xasprintf (&command_line[index++], "%d", retries);
325 command_line[index++] = strdup ("-m");
326 command_line[index++] = strdup (miblist);
327 command_line[index++] = "-v";
328 command_line[index++] = strdup (proto);
329 195
330 xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s", 196 if (verbose > 1) {
331 snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", proto); 197 printf("Recovered %lu entries of size %lu\n",
198 (size_t)outlen / sizeof(check_snmp_state_entry), outlen);
199
200 for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) {
201 printf("Entry timestamp %lu: %s", result.state[i].timestamp,
202 ctime(&result.state[i].timestamp));
203 switch (result.state[i].type) {
204 case ASN_GAUGE:
205 printf("Type GAUGE\n");
206 break;
207 case ASN_TIMETICKS:
208 printf("Type TIMETICKS\n");
209 break;
210 case ASN_COUNTER:
211 printf("Type COUNTER\n");
212 break;
213 case ASN_UINTEGER:
214 printf("Type UINTEGER\n");
215 break;
216 case ASN_COUNTER64:
217 printf("Type COUNTER64\n");
218 break;
219 case ASN_FLOAT:
220 printf("Type FLOAT\n");
221 break;
222 case ASN_DOUBLE:
223 printf("Type DOUBLE\n");
224 break;
225 case ASN_INTEGER:
226 printf("Type INTEGER\n");
227 break;
228 }
332 229
333 if (ignore_mib_parsing_errors) { 230 switch (result.state[i].type) {
334 command_line[index++] = "-Pe"; 231 case ASN_GAUGE:
335 xasprintf(&cl_hidden_auth, "%s -Pe", cl_hidden_auth); 232 case ASN_TIMETICKS:
233 case ASN_COUNTER:
234 case ASN_UINTEGER:
235 case ASN_COUNTER64:
236 printf("Value %llu\n", result.state[i].value.uIntVal);
237 break;
238 case ASN_FLOAT:
239 case ASN_DOUBLE:
240 printf("Value %f\n", result.state[i].value.doubleVal);
241 break;
242 case ASN_INTEGER:
243 printf("Value %lld\n", result.state[i].value.intVal);
244 break;
245 }
246 }
336 } 247 }
337 248
249 return result;
250}
338 251
339 for (int i = 0; i < numcontext; i++) { 252int main(int argc, char **argv) {
340 command_line[index++] = contextargs[i]; 253 setlocale(LC_ALL, "");
341 } 254 bindtextdomain(PACKAGE, LOCALEDIR);
255 textdomain(PACKAGE);
342 256
343 for (int i = 0; i < numauthpriv; i++) { 257 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
344 command_line[index++] = authpriv[i];
345 }
346 258
347 xasprintf (&command_line[index++], "%s:%s", server_address, port); 259 np_init((char *)progname, argc, argv);
348 260
349 xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", 261 state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv);
350 cl_hidden_auth,
351 server_address,
352 port);
353 262
354 for (size_t i = 0; i < numoids; i++) { 263 /* Parse extra opts if any */
355 command_line[index++] = oids[i]; 264 argv = np_extra_opts(&argc, argv, progname);
356 xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]);
357 }
358 265
359 command_line[index++] = NULL; 266 np_set_args(argc, argv);
360 267
361 if (verbose) { 268 // Initialize net-snmp before touching the session we are going to use
362 printf ("%s\n", cl_hidden_auth); 269 init_snmp("check_snmp");
363 }
364 270
365 /* Set signal handling and alarm */ 271 process_arguments_wrapper paw_tmp = process_arguments(argc, argv);
366 if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 272 if (paw_tmp.errorcode == ERROR) {
367 usage4 (_("Cannot catch SIGALRM")); 273 usage4(_("Could not parse arguments"));
368 }
369 alarm(timeout_interval * retries + 5);
370
371 /* Run the command */
372 return_code = cmd_run_array (command_line, &chld_out, &chld_err, 0);
373
374 /* disable alarm again */
375 alarm(0);
376
377 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
378 only return state unknown if return code is non zero or there is no stdout.
379 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
380 */
381 if (return_code != 0)
382 external_error=1;
383 if (chld_out.lines == 0)
384 external_error=1;
385 if (external_error) {
386 if (chld_err.lines > 0) {
387 printf (_("External command error: %s\n"), chld_err.line[0]);
388 for (size_t i = 1; i < chld_err.lines; i++) {
389 printf ("%s\n", chld_err.line[i]);
390 }
391 } else {
392 printf(_("External command error with no output (return code: %d)\n"), return_code);
393 }
394 exit (STATE_UNKNOWN);
395 } 274 }
396 275
397 if (verbose) { 276 check_snmp_config config = paw_tmp.config;
398 for (size_t i = 0; i < chld_out.lines; i++) { 277
399 printf ("%s\n", chld_out.line[i]); 278 if (config.output_format_is_set) {
400 } 279 mp_set_format(config.output_format);
401 } 280 }
402 281
403 line = 0; 282 /* Set signal handling and alarm */
404 total_oids = 0; 283 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
405 for (size_t i = 0; line < chld_out.lines && i < numoids ; line++, i++, total_oids++) { 284 usage4(_("Cannot catch SIGALRM"));
406 if(calculate_rate) 285 }
407 conv = "%.10g";
408 else
409 conv = "%.0f";
410
411 ptr = chld_out.line[line];
412 oidname = strpcpy (oidname, ptr, delimiter);
413 response = strstr (ptr, delimiter);
414 if (response == NULL)
415 break;
416 286
417 if (verbose > 2) { 287 time_t current_time;
418 printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i+1, line+1, oidname, response); 288 time(&current_time);
419 }
420 289
421 /* Clean up type array - Sol10 does not necessarily zero it out */ 290 if (verbose > 2) {
422 bzero(type, sizeof(type)); 291 printf("current time: %s (timestamp: %lu)\n", ctime(&current_time), current_time);
292 }
423 293
424 is_counter=0; 294 snmp_responces response = do_snmp_query(config.snmp_params);
425 /* We strip out the datatype indicator for PHBs */
426 if (strstr (response, "Gauge: ")) {
427 show = multiply (strstr (response, "Gauge: ") + 7);
428 }
429 else if (strstr (response, "Gauge32: ")) {
430 show = multiply (strstr (response, "Gauge32: ") + 9);
431 }
432 else if (strstr (response, "Counter32: ")) {
433 show = strstr (response, "Counter32: ") + 11;
434 is_counter=1;
435 if(!calculate_rate)
436 strcpy(type, "c");
437 }
438 else if (strstr (response, "Counter64: ")) {
439 show = strstr (response, "Counter64: ") + 11;
440 is_counter=1;
441 if(!calculate_rate)
442 strcpy(type, "c");
443 }
444 else if (strstr (response, "INTEGER: ")) {
445 show = multiply (strstr (response, "INTEGER: ") + 9);
446 295
447 if (fmtstr_set) { 296 mp_check overall = mp_check_init();
448 conv = fmtstr;
449 }
450 }
451 else if (strstr (response, "OID: ")) {
452 show = strstr (response, "OID: ") + 5;
453 }
454 else if (strstr (response, "STRING: ")) {
455 show = strstr (response, "STRING: ") + 8;
456 conv = "%.10g";
457
458 /* Get the rest of the string on multi-line strings */
459 ptr = show;
460 COUNT_SEQ(ptr, bk_count, dq_count)
461 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
462 ptr++;
463 GOBBLE_TOS(ptr, "\n\"\\")
464 COUNT_SEQ(ptr, bk_count, dq_count)
465 }
466 297
467 if (dq_count) { /* unfinished line */ 298 if (response.errorcode == OK) {
468 /* copy show verbatim first */ 299 mp_subcheck sc_successfull_query = mp_subcheck_init();
469 if (!mult_resp) mult_resp = strdup(""); 300 xasprintf(&sc_successfull_query.output, "SNMP query was successful");
470 xasprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show); 301 sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK);
471 /* then strip out unmatched double-quote from single-line output */ 302 mp_add_subcheck_to_check(&overall, sc_successfull_query);
472 if (show[0] == '"') show++; 303 } else {
473 304 // Error treatment here, either partial or whole
474 /* Keep reading until we match end of double-quoted string */ 305 mp_subcheck sc_failed_query = mp_subcheck_init();
475 for (line++; line < chld_out.lines; line++) { 306 xasprintf(&sc_failed_query.output, "SNMP query failed");
476 ptr = chld_out.line[line]; 307 sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK);
477 xasprintf (&mult_resp, "%s%s\n", mult_resp, ptr); 308 mp_add_subcheck_to_check(&overall, sc_failed_query);
478 309 mp_exit(overall);
479 COUNT_SEQ(ptr, bk_count, dq_count) 310 }
480 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
481 ptr++;
482 GOBBLE_TOS(ptr, "\n\"\\")
483 COUNT_SEQ(ptr, bk_count, dq_count)
484 }
485 /* Break for loop before next line increment when done */
486 if (!dq_count) break;
487 }
488 }
489 311
490 } 312 check_snmp_state_entry *prev_state = NULL;
491 else if (strstr (response, "Timeticks: ")) { 313 bool have_previous_state = false;
492 show = strstr (response, "Timeticks: ");
493 }
494 else
495 show = response + 3;
496 314
497 iresult = STATE_DEPENDENT; 315 if (config.evaluation_params.calculate_rate) {
316 state_data *previous_state = np_state_read(stateKey);
317 if (previous_state == NULL) {
318 // failed to recover state
319 // or no previous state
320 have_previous_state = false;
321 } else {
322 // sanity check
323 recover_state_data_type prev_state_wrapper =
324 recover_state_data(previous_state->data, (idx_t)previous_state->length);
498 325
499 /* Process this block for numeric comparisons */ 326 if (prev_state_wrapper.errorcode == OK) {
500 /* Make some special values,like Timeticks numeric only if a threshold is defined */ 327 have_previous_state = true;
501 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { 328 prev_state = prev_state_wrapper.state;
502 if (verbose > 2) {
503 print_thresholds(" thresholds", thlds[i]);
504 }
505 ptr = strpbrk (show, "-0123456789");
506 if (ptr == NULL){
507 if (nulloid == 3)
508 die (STATE_UNKNOWN,_("No valid data returned (%s)\n"), show);
509 else if (nulloid == 0)
510 die (STATE_OK,_("No valid data returned (%s)\n"), show);
511 else if (nulloid == 1)
512 die (STATE_WARNING,_("No valid data returned (%s)\n"), show);
513 else if (nulloid == 2)
514 die (STATE_CRITICAL,_("No valid data returned (%s)\n"), show);
515 }
516 while (i >= response_size) {
517 response_size += OID_COUNT_STEP;
518 response_value = realloc(response_value, response_size * sizeof(*response_value));
519 }
520 response_value[i] = strtod (ptr, NULL) + offset;
521
522 if(calculate_rate) {
523 if (previous_state!=NULL) {
524 duration = current_time-previous_state->time;
525 if(duration<=0)
526 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
527 temp_double = response_value[i]-previous_value[i];
528 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
529 if(is_counter) {
530 if(temp_double<(double)0.0)
531 temp_double+=(double)4294967296.0; /* 2^32 */
532 if(temp_double<(double)0.0)
533 temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
534 }
535 /* Convert to per second, then use multiplier */
536 temp_double = temp_double/duration*rate_multiplier;
537 iresult = get_status(temp_double, thlds[i]);
538 xasprintf (&show, conv, temp_double);
539 }
540 } else { 329 } else {
541 iresult = get_status(response_value[i], thlds[i]); 330 have_previous_state = false;
542 xasprintf (&show, conv, response_value[i]); 331 prev_state = NULL;
543 } 332 }
544 } 333 }
334 }
545 335
546 /* Process this block for string matching */ 336 check_snmp_state_entry *new_state = NULL;
547 else if (eval_size > i && eval_method[i] & CRIT_STRING) { 337 if (config.evaluation_params.calculate_rate) {
548 if (strcmp (show, string_value)) 338 new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry));
549 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK; 339 if (new_state == NULL) {
550 else 340 die(STATE_UNKNOWN, "memory allocation failed");
551 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
552 } 341 }
342 }
553 343
554 /* Process this block for regex matching */ 344 // We got the the query results, now process them
555 else if (eval_size > i && eval_method[i] & CRIT_REGEX) { 345 for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) {
556 excode = regexec (&preg, response, 10, pmatch, eflags); 346 if (verbose > 0) {
557 if (excode == 0) { 347 printf("loop_index: %zu\n", loop_index);
558 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
559 }
560 else if (excode != REG_NOMATCH) {
561 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
562 printf (_("Execute Error: %s\n"), errbuf);
563 exit (STATE_CRITICAL);
564 }
565 else {
566 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
567 }
568 } 348 }
569 349
570 /* Process this block for existence-nonexistence checks */ 350 check_snmp_state_entry previous_unit_state = {};
571 /* TV: Should this be outside of this else block? */ 351 if (config.evaluation_params.calculate_rate && have_previous_state) {
572 else { 352 previous_unit_state = prev_state[loop_index];
573 if (eval_size > i && eval_method[i] & CRIT_PRESENT)
574 iresult = STATE_CRITICAL;
575 else if (eval_size > i && eval_method[i] & WARN_PRESENT)
576 iresult = STATE_WARNING;
577 else if (response && iresult == STATE_DEPENDENT)
578 iresult = STATE_OK;
579 } 353 }
580 354
581 /* Result is the worst outcome of all the OIDs tested */ 355 check_snmp_evaluation single_eval =
582 result = max_state (result, iresult); 356 evaluate_single_unit(response.response_values[loop_index], config.evaluation_params,
583 357 config.snmp_params.test_units[loop_index], current_time,
584 /* Prepend a label for this OID if there is one */ 358 previous_unit_state, have_previous_state);
585 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
586 xasprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
587 (i == 0) ? " " : output_delim,
588 labels[i], mark (iresult), show, mark (iresult));
589 else
590 xasprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
591 mark (iresult), show, mark (iresult));
592
593 /* Append a unit string for this OID if there is one */
594 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
595 xasprintf (&outbuff, "%s %s", outbuff, unitv[i]);
596
597 /* Write perfdata with whatever can be parsed by strtod, if possible */
598 ptr = NULL;
599 strtod(show, &ptr);
600 if (ptr > show) {
601 if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
602 temp_string=labels[i];
603 else
604 temp_string=oidname;
605 if (strpbrk (temp_string, " ='\"") == NULL) {
606 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
607 } else {
608 if (strpbrk (temp_string, "'") == NULL) {
609 quote_string="'";
610 } else {
611 quote_string="\"";
612 }
613 strncat(perfstr, quote_string, sizeof(perfstr)-strlen(perfstr)-1);
614 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
615 strncat(perfstr, quote_string, sizeof(perfstr)-strlen(perfstr)-1);
616 }
617 strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
618 len = sizeof(perfstr)-strlen(perfstr)-1;
619 strncat(perfstr, show, len>ptr-show ? ptr-show : len);
620
621 if (strcmp(type, "") != 0) {
622 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
623 }
624
625 if (warning_thresholds) {
626 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
627 if(thlds[i]->warning && thlds[i]->warning->text)
628 strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr)-strlen(perfstr)-1);
629 }
630
631 if (critical_thresholds) {
632 if (!warning_thresholds)
633 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
634 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
635 if(thlds[i]->critical && thlds[i]->critical->text)
636 strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr)-strlen(perfstr)-1);
637 }
638 359
639 strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1); 360 if (config.evaluation_params.calculate_rate &&
361 mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) {
362 new_state[loop_index] = single_eval.state;
640 } 363 }
641 }
642 364
643 /* Save state data, as all data collected now */ 365 mp_add_subcheck_to_check(&overall, single_eval.sc);
644 if(calculate_rate) {
645 string_length=1024;
646 state_string=malloc(string_length);
647 if(state_string==NULL)
648 die(STATE_UNKNOWN, _("Cannot malloc"));
649
650 current_length=0;
651 for(int i = 0; i < total_oids; i++) {
652 xasprintf(&temp_string,"%.0f",response_value[i]);
653 if(temp_string==NULL)
654 die(STATE_UNKNOWN,_("Cannot asprintf()"));
655 response_length = strlen(temp_string);
656 if(current_length+response_length>string_length) {
657 string_length=current_length+1024;
658 state_string=realloc(state_string,string_length);
659 if(state_string==NULL)
660 die(STATE_UNKNOWN, _("Cannot realloc()"));
661 }
662 strcpy(&state_string[current_length],temp_string);
663 current_length=current_length+response_length;
664 state_string[current_length]=':';
665 current_length++;
666 free(temp_string);
667 }
668 state_string[--current_length]='\0';
669 if (verbose > 2)
670 printf("State string=%s\n",state_string);
671
672 /* This is not strictly the same as time now, but any subtle variations will cancel out */
673 np_state_write_string(current_time, state_string );
674 if(previous_state==NULL) {
675 /* Or should this be highest state? */
676 die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
677 }
678 } 366 }
679 367
680 printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr); 368 if (config.evaluation_params.calculate_rate) {
681 if (mult_resp) printf ("%s", mult_resp); 369 // store state
370 gen_state_string_type current_state_wrapper =
371 gen_state_string(new_state, config.snmp_params.num_of_test_units);
682 372
683 return result; 373 if (current_state_wrapper.errorcode == OK) {
374 np_state_write_string(stateKey, current_time, current_state_wrapper.state_string);
375 } else {
376 die(STATE_UNKNOWN, "failed to create state string");
377 }
378 }
379 mp_exit(overall);
684} 380}
685 381
686
687
688/* process command-line arguments */ 382/* process command-line arguments */
689int 383static process_arguments_wrapper process_arguments(int argc, char **argv) {
690process_arguments (int argc, char **argv) 384 enum {
691{ 385 /* Longopts only arguments */
692 char *ptr; 386 invert_search_index = CHAR_MAX + 1,
693 int c = 1; 387 offset_index,
694 size_t j = 0, jj = 0; 388 ignore_mib_parsing_errors_index,
389 connection_prefix_index,
390 output_format_index,
391 calculate_rate,
392 rate_multiplier
393 };
695 394
696 int option = 0;
697 static struct option longopts[] = { 395 static struct option longopts[] = {
698 STD_LONG_OPTS, 396 STD_LONG_OPTS,
699 {"community", required_argument, 0, 'C'}, 397 {"community", required_argument, 0, 'C'},
@@ -721,662 +419,738 @@ process_arguments (int argc, char **argv)
721 {"authpasswd", required_argument, 0, 'A'}, 419 {"authpasswd", required_argument, 0, 'A'},
722 {"privpasswd", required_argument, 0, 'X'}, 420 {"privpasswd", required_argument, 0, 'X'},
723 {"next", no_argument, 0, 'n'}, 421 {"next", no_argument, 0, 'n'},
724 {"rate", no_argument, 0, L_CALCULATE_RATE}, 422 {"offset", required_argument, 0, offset_index},
725 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER}, 423 {"invert-search", no_argument, 0, invert_search_index},
726 {"offset", required_argument, 0, L_OFFSET},
727 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
728 {"perf-oids", no_argument, 0, 'O'}, 424 {"perf-oids", no_argument, 0, 'O'},
729 {"ipv4", no_argument, 0, '4'}, 425 {"ipv4", no_argument, 0, '4'},
730 {"ipv6", no_argument, 0, '6'}, 426 {"ipv6", no_argument, 0, '6'},
731 {"multiplier", required_argument, 0, 'M'}, 427 {"multiplier", required_argument, 0, 'M'},
732 {"fmtstr", required_argument, 0, 'f'}, 428 {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index},
733 {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS}, 429 {"connection-prefix", required_argument, 0, connection_prefix_index},
734 {0, 0, 0, 0} 430 {"output-format", required_argument, 0, output_format_index},
735 }; 431 {"rate", no_argument, 0, calculate_rate},
432 {"rate-multiplier", required_argument, 0, rate_multiplier},
433 {0, 0, 0, 0}};
434
435 if (argc < 2) {
436 process_arguments_wrapper result = {
437 .errorcode = ERROR,
438 };
439 return result;
440 }
441
442 // Count number of OIDs here first
443 int option = 0;
444 size_t oid_counter = 0;
445 while (true) {
446 int option_char = getopt_long(
447 argc, argv,
448 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
449
450 if (CHECK_EOF(option_char)) {
451 break;
452 }
453
454 switch (option_char) {
455 case 'o': {
456 // we are going to parse this again, so we work on a copy of that string
457 char *tmp_oids = strdup(optarg);
458 if (tmp_oids == NULL) {
459 die(STATE_UNKNOWN, "strdup failed");
460 }
461
462 for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL;
463 ptr = strtok(NULL, ", "), oid_counter++) {
464 }
465 break;
466 }
467 case '?': /* usage */
468 usage5();
469 // fallthrough
470 case 'h': /* help */
471 print_help();
472 exit(STATE_UNKNOWN);
473 case 'V': /* version */
474 print_revision(progname, NP_VERSION);
475 exit(STATE_UNKNOWN);
476
477 default:
478 continue;
479 }
480 }
736 481
737 if (argc < 2) 482 /* Check whether at least one OID was given */
738 return ERROR; 483 if (oid_counter == 0) {
739 484 die(STATE_UNKNOWN, _("No OIDs specified\n"));
740 /* reverse compatibility for very old non-POSIX usage forms */
741 for (c = 1; c < argc; c++) {
742 if (strcmp ("-to", argv[c]) == 0)
743 strcpy (argv[c], "-t");
744 if (strcmp ("-wv", argv[c]) == 0)
745 strcpy (argv[c], "-w");
746 if (strcmp ("-cv", argv[c]) == 0)
747 strcpy (argv[c], "-c");
748 } 485 }
749 486
750 while (1) { 487 // Allocate space for test units
751 c = getopt_long (argc, argv, "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", 488 check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit));
752 longopts, &option); 489 if (tmp == NULL) {
490 die(STATE_UNKNOWN, "Failed to calloc");
491 }
753 492
754 if (c == -1 || c == EOF) 493 for (size_t i = 0; i < oid_counter; i++) {
494 tmp[i] = check_snmp_test_unit_init();
495 }
496
497 check_snmp_config config = check_snmp_config_init();
498 config.snmp_params.test_units = tmp;
499 config.snmp_params.num_of_test_units = oid_counter;
500
501 option = 0;
502 optind = 1; // Reset argument scanner
503 size_t tmp_oid_counter = 0;
504 size_t eval_counter = 0;
505 size_t unitv_counter = 0;
506 size_t labels_counter = 0;
507 unsigned char *authpasswd = NULL;
508 unsigned char *privpasswd = NULL;
509 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
510 char *port = NULL;
511 char *miblist = NULL;
512 char *connection_prefix = NULL;
513 bool snmp_version_set_explicitely = false;
514 // TODO error checking
515 while (true) {
516 int option_char = getopt_long(
517 argc, argv,
518 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
519
520 if (CHECK_EOF(option_char)) {
755 break; 521 break;
522 }
756 523
757 switch (c) { 524 switch (option_char) {
758 case '?': /* usage */ 525 case '?': /* usage */
759 usage5 (); 526 usage5();
760 case 'h': /* help */ 527 case 'h': /* help */
761 print_help (); 528 print_help();
762 exit (STATE_UNKNOWN); 529 exit(STATE_UNKNOWN);
763 case 'V': /* version */ 530 case 'V': /* version */
764 print_revision (progname, NP_VERSION); 531 print_revision(progname, NP_VERSION);
765 exit (STATE_UNKNOWN); 532 exit(STATE_UNKNOWN);
766 case 'v': /* verbose */ 533 case 'v': /* verbose */
767 verbose++; 534 verbose++;
768 break; 535 break;
769 536
770 /* Connection info */ 537 /* Connection info */
771 case 'C': /* group or community */ 538 case 'C': /* group or community */
772 community = optarg; 539 config.snmp_params.snmp_session.community = (unsigned char *)optarg;
540 config.snmp_params.snmp_session.community_len = strlen(optarg);
773 break; 541 break;
774 case 'H': /* Host or server */ 542 case 'H': /* Host or server */
775 server_address = optarg; 543 config.snmp_params.snmp_session.peername = optarg;
776 break; 544 break;
777 case 'p': /* TCP port number */ 545 case 'p': /*port number */
546 // Add port to "peername" below to not rely on argument order
778 port = optarg; 547 port = optarg;
779 break; 548 break;
780 case 'm': /* List of MIBS */ 549 case 'm': /* List of MIBS */
781 miblist = optarg; 550 miblist = optarg;
782 break; 551 break;
783 case 'n': /* usesnmpgetnext */ 552 case 'n': /* use_getnext instead of get */
784 usesnmpgetnext = true; 553 config.snmp_params.use_getnext = true;
785 break; 554 break;
786 case 'P': /* SNMP protocol version */ 555 case 'P': /* SNMP protocol version */
787 proto = optarg; 556 if (strcasecmp("1", optarg) == 0) {
557 config.snmp_params.snmp_session.version = SNMP_VERSION_1;
558 } else if (strcasecmp("2c", optarg) == 0) {
559 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
560 } else if (strcasecmp("3", optarg) == 0) {
561 config.snmp_params.snmp_session.version = SNMP_VERSION_3;
562 } else {
563 die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg);
564 }
565 snmp_version_set_explicitely = true;
566
788 break; 567 break;
789 case 'N': /* SNMPv3 context */ 568 case 'N': /* SNMPv3 context name */
790 context = optarg; 569 config.snmp_params.snmp_session.contextName = optarg;
570 config.snmp_params.snmp_session.contextNameLen = strlen(optarg);
791 break; 571 break;
792 case 'L': /* security level */ 572 case 'L': /* security level */
793 seclevel = optarg; 573 if (strcasecmp("noAuthNoPriv", optarg) == 0) {
574 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
575 } else if (strcasecmp("authNoPriv", optarg) == 0) {
576 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
577 } else if (strcasecmp("authPriv", optarg) == 0) {
578 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
579 } else {
580 die(STATE_UNKNOWN, "invalid security level: %s", optarg);
581 }
794 break; 582 break;
795 case 'U': /* security username */ 583 case 'U': /* security username */
796 secname = optarg; 584 config.snmp_params.snmp_session.securityName = optarg;
585 config.snmp_params.snmp_session.securityNameLen = strlen(optarg);
797 break; 586 break;
798 case 'a': /* auth protocol */ 587 case 'a': /* auth protocol */
799 authproto = optarg; 588 // SNMPv3: SHA or MD5
589 // TODO Test for availability of individual protocols
590 if (strcasecmp("MD5", optarg) == 0) {
591 config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol;
592 config.snmp_params.snmp_session.securityAuthProtoLen =
593 OID_LENGTH(usmHMACMD5AuthProtocol);
594 } else if (strcasecmp("SHA", optarg) == 0) {
595 config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol;
596 config.snmp_params.snmp_session.securityAuthProtoLen =
597 OID_LENGTH(usmHMACSHA1AuthProtocol);
598 } else if (strcasecmp("SHA224", optarg) == 0) {
599 config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol;
600 config.snmp_params.snmp_session.securityAuthProtoLen =
601 OID_LENGTH(usmHMAC128SHA224AuthProtocol);
602 } else if (strcasecmp("SHA256", optarg) == 0) {
603 config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol;
604 config.snmp_params.snmp_session.securityAuthProtoLen =
605 OID_LENGTH(usmHMAC192SHA256AuthProtocol);
606 } else if (strcasecmp("SHA384", optarg) == 0) {
607 config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol;
608 config.snmp_params.snmp_session.securityAuthProtoLen =
609 OID_LENGTH(usmHMAC256SHA384AuthProtocol);
610 } else if (strcasecmp("SHA512", optarg) == 0) {
611 config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol;
612 config.snmp_params.snmp_session.securityAuthProtoLen =
613 OID_LENGTH(usmHMAC384SHA512AuthProtocol);
614 } else {
615 die(STATE_UNKNOWN, "Unknown authentication protocol");
616 }
800 break; 617 break;
801 case 'x': /* priv protocol */ 618 case 'x': /* priv protocol */
802 privproto = optarg; 619 if (strcasecmp("DES", optarg) == 0) {
620#ifdef HAVE_USM_DES_PRIV_PROTOCOL
621 config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol;
622 config.snmp_params.snmp_session.securityAuthProtoLen =
623 OID_LENGTH(usmDESPrivProtocol);
624#else
625 die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform");
626#endif
627 } else if (strcasecmp("AES", optarg) == 0) {
628 config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol;
629 config.snmp_params.snmp_session.securityAuthProtoLen =
630 OID_LENGTH(usmAESPrivProtocol);
631 // } else if (strcasecmp("AES128", optarg)) {
632 // config.snmp_session.securityAuthProto = usmAES128PrivProtocol;
633 // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol)
634 // / OID_LENGTH(oid);
635 } else if (strcasecmp("AES192", optarg) == 0) {
636 config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol;
637 config.snmp_params.snmp_session.securityAuthProtoLen =
638 OID_LENGTH(usmAES192PrivProtocol);
639 } else if (strcasecmp("AES256", optarg) == 0) {
640 config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol;
641 config.snmp_params.snmp_session.securityAuthProtoLen =
642 OID_LENGTH(usmAES256PrivProtocol);
643 // } else if (strcasecmp("AES192Cisco", optarg)) {
644 // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol;
645 // config.snmp_session.securityAuthProtoLen =
646 // sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); } else if
647 // (strcasecmp("AES256Cisco", optarg)) { config.snmp_session.securityAuthProto =
648 // usmAES256CiscoPrivProtocol; config.snmp_session.securityAuthProtoLen =
649 // sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); } else if
650 // (strcasecmp("AES192Cisco2", optarg)) { config.snmp_session.securityAuthProto
651 // = usmAES192Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
652 // sizeof(usmAES192Cisco2PrivProtocol) / sizeof(oid); } else if
653 // (strcasecmp("AES256Cisco2", optarg)) { config.snmp_session.securityAuthProto
654 // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
655 // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid);
656 } else {
657 die(STATE_UNKNOWN, "Unknown privacy protocol");
658 }
803 break; 659 break;
804 case 'A': /* auth passwd */ 660 case 'A': /* auth passwd */
805 authpasswd = optarg; 661 authpasswd = (unsigned char *)optarg;
806 break; 662 break;
807 case 'X': /* priv passwd */ 663 case 'X': /* priv passwd */
808 privpasswd = optarg; 664 privpasswd = (unsigned char *)optarg;
809 break; 665 break;
810 case 't': /* timeout period */ 666 case 'e':
811 if (!is_integer (optarg)) 667 case 'E':
812 usage2 (_("Timeout interval must be a positive integer"), optarg); 668 if (!is_integer(optarg)) {
813 else 669 usage2(_("Retries interval must be a positive integer"), optarg);
814 timeout_interval = atoi (optarg); 670 } else {
671 config.snmp_params.snmp_session.retries = atoi(optarg);
672 }
815 break; 673 break;
816 674 case 't': /* timeout period */
817 /* Test parameters */ 675 if (!is_integer(optarg)) {
818 case 'c': /* critical threshold */ 676 usage2(_("Timeout interval must be a positive integer"), optarg);
819 critical_thresholds = optarg; 677 } else {
678 timeout_interval = (unsigned int)atoi(optarg);
679 }
820 break; 680 break;
821 case 'w': /* warning threshold */ 681
822 warning_thresholds = optarg; 682 /* Test parameters */
683 case 'c': /* critical threshold */
684 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true);
823 break; 685 break;
824 case 'e': /* PRELIMINARY - may change */ 686 case 'w': /* warning threshold */
825 case 'E': /* PRELIMINARY - may change */ 687 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false);
826 if (!is_integer (optarg))
827 usage2 (_("Retries interval must be a positive integer"), optarg);
828 else
829 retries = atoi(optarg);
830 break; 688 break;
831 case 'o': /* object identifier */ 689 case 'o': /* object identifier */
832 if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) { 690 if (strspn(optarg, "0123456789.,") != strlen(optarg)) {
833 /* 691 /*
834 * we have something other than digits, periods and comas, 692 * we have something other than digits, periods and comas,
835 * so we have a mib variable, rather than just an SNMP OID, 693 * so we have a mib variable, rather than just an SNMP OID,
836 * so we have to actually read the mib files 694 * so we have to actually read the mib files
837 */ 695 */
838 needmibs = true; 696 config.snmp_params.need_mibs = true;
839 } 697 }
840 for (ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) { 698
841 while (j >= oids_size) { 699 for (char *ptr = strtok(optarg, ", "); ptr != NULL;
842 oids_size += OID_COUNT_STEP; 700 ptr = strtok(NULL, ", "), tmp_oid_counter++) {
843 oids = realloc(oids, oids_size * sizeof (*oids)); 701 config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr);
844 }
845 oids[j] = strdup(ptr);
846 }
847 numoids = j;
848 if (c == 'E' || c == 'e') {
849 jj++;
850 while (j+1 >= eval_size) {
851 eval_size += OID_COUNT_STEP;
852 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
853 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
854 }
855 if (c == 'E')
856 eval_method[j+1] |= WARN_PRESENT;
857 else if (c == 'e')
858 eval_method[j+1] |= CRIT_PRESENT;
859 } 702 }
860 break; 703 break;
861 case 'z': /* Null OID Return Check */ 704 case 'z': /* Null OID Return Check */
862 if (!is_integer (optarg)) 705 if (!is_integer(optarg)) {
863 usage2 (_("Exit status must be a positive integer"), optarg); 706 usage2(_("Exit status must be a positive integer"), optarg);
864 else 707 } else {
865 nulloid = atoi(optarg); 708 config.evaluation_params.nulloid_result = atoi(optarg);
866 break;
867 case 's': /* string or substring */
868 strncpy (string_value, optarg, sizeof (string_value) - 1);
869 string_value[sizeof (string_value) - 1] = 0;
870 while (jj >= eval_size) {
871 eval_size += OID_COUNT_STEP;
872 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
873 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
874 } 709 }
875 eval_method[jj++] = CRIT_STRING;
876 break; 710 break;
877 case 'R': /* regex */ 711 case 's': /* string or substring */
712 strncpy(config.evaluation_params.string_cmp_value, optarg,
713 sizeof(config.evaluation_params.string_cmp_value) - 1);
714 config.evaluation_params
715 .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0;
716 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true;
717 break;
718 case 'R': /* regex */
878 cflags = REG_ICASE; 719 cflags = REG_ICASE;
879 // fall through 720 // fall through
880 case 'r': /* regex */ 721 case 'r': /* regex */
722 {
723 char regex_expect[MAX_INPUT_BUFFER] = "";
881 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 724 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
882 strncpy (regex_expect, optarg, sizeof (regex_expect) - 1); 725 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1);
883 regex_expect[sizeof (regex_expect) - 1] = 0; 726 regex_expect[sizeof(regex_expect) - 1] = 0;
884 errcode = regcomp (&preg, regex_expect, cflags); 727 int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags);
885 if (errcode != 0) { 728 if (errcode != 0) {
886 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 729 char errbuf[MAX_INPUT_BUFFER] = "";
887 printf (_("Could Not Compile Regular Expression")); 730 regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf,
888 return ERROR; 731 MAX_INPUT_BUFFER);
732 printf("Could Not Compile Regular Expression: %s", errbuf);
733 process_arguments_wrapper result = {
734 .errorcode = ERROR,
735 };
736 return result;
889 } 737 }
890 while (jj >= eval_size) { 738 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true;
891 eval_size += OID_COUNT_STEP; 739 } break;
892 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 740 case 'l': /* label */
893 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); 741 {
742 if (labels_counter >= config.snmp_params.num_of_test_units) {
743 break;
894 } 744 }
895 eval_method[jj++] = CRIT_REGEX; 745 char *ptr = trim_whitespaces_and_check_quoting(optarg);
896 break; 746 if (ptr[0] == '\'') {
897 747 config.snmp_params.test_units[labels_counter].label = ptr + 1;
898 /* Format */ 748 } else {
899 case 'd': /* delimiter */ 749 config.snmp_params.test_units[labels_counter].label = ptr;
900 delimiter = strscpy (delimiter, optarg);
901 break;
902 case 'D': /* output-delimiter */
903 output_delim = strscpy (output_delim, optarg);
904 break;
905 case 'l': /* label */
906 nlabels++;
907 if (nlabels > labels_size) {
908 labels_size += 8;
909 labels = realloc (labels, labels_size * sizeof(*labels));
910 if (labels == NULL)
911 die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
912 } 750 }
913 labels[nlabels - 1] = optarg; 751
914 ptr = thisarg (optarg); 752 while (ptr && (ptr = get_next_argument(ptr))) {
915 labels[nlabels - 1] = ptr; 753 labels_counter++;
916 if (ptr[0] == '\'') 754 ptr = trim_whitespaces_and_check_quoting(ptr);
917 labels[nlabels - 1] = ptr + 1; 755 if (ptr[0] == '\'') {
918 while (ptr && (ptr = nextarg (ptr))) { 756 config.snmp_params.test_units[labels_counter].label = ptr + 1;
919 nlabels++; 757 } else {
920 if (nlabels > labels_size) { 758 config.snmp_params.test_units[labels_counter].label = ptr;
921 labels_size += 8;
922 labels = realloc (labels, labels_size * sizeof(*labels));
923 if (labels == NULL)
924 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
925 } 759 }
926 ptr = thisarg (ptr);
927 if (ptr[0] == '\'')
928 labels[nlabels - 1] = ptr + 1;
929 else
930 labels[nlabels - 1] = ptr;
931 } 760 }
932 break; 761 labels_counter++;
933 case 'u': /* units */ 762 } break;
934 units = optarg; 763 case 'u': /* units */
935 nunits++; 764 {
936 if (nunits > unitv_size) { 765 if (unitv_counter >= config.snmp_params.num_of_test_units) {
937 unitv_size += 8; 766 break;
938 unitv = realloc (unitv, unitv_size * sizeof(*unitv)); 767 }
939 if (unitv == NULL) 768 char *ptr = trim_whitespaces_and_check_quoting(optarg);
940 die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits); 769 if (ptr[0] == '\'') {
770 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
771 } else {
772 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
941 } 773 }
942 unitv[nunits - 1] = optarg; 774 while (ptr && (ptr = get_next_argument(ptr))) {
943 ptr = thisarg (optarg); 775 unitv_counter++;
944 unitv[nunits - 1] = ptr; 776 ptr = trim_whitespaces_and_check_quoting(ptr);
945 if (ptr[0] == '\'') 777 if (ptr[0] == '\'') {
946 unitv[nunits - 1] = ptr + 1; 778 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
947 while (ptr && (ptr = nextarg (ptr))) { 779 } else {
948 if (nunits > unitv_size) { 780 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
949 unitv_size += 8;
950 unitv = realloc (unitv, unitv_size * sizeof(*unitv));
951 if (units == NULL)
952 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
953 } 781 }
954 nunits++;
955 ptr = thisarg (ptr);
956 if (ptr[0] == '\'')
957 unitv[nunits - 1] = ptr + 1;
958 else
959 unitv[nunits - 1] = ptr;
960 } 782 }
783 unitv_counter++;
784 } break;
785 case offset_index:
786 config.evaluation_params.offset = strtod(optarg, NULL);
787 config.evaluation_params.offset_set = true;
961 break; 788 break;
962 case L_CALCULATE_RATE: 789 case invert_search_index:
963 if(calculate_rate==0) 790 config.evaluation_params.invert_search = false;
964 np_enable_state(NULL, 1);
965 calculate_rate = 1;
966 break;
967 case L_RATE_MULTIPLIER:
968 if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
969 usage2(_("Rate multiplier must be a positive integer"),optarg);
970 break;
971 case L_OFFSET:
972 offset=strtod(optarg,NULL);
973 break;
974 case L_INVERT_SEARCH:
975 invert_search=1;
976 break; 791 break;
977 case 'O': 792 case 'O':
978 perf_labels=0; 793 config.evaluation_params.use_oid_as_perf_data_label = true;
979 break; 794 break;
980 case '4': 795 case '4':
796 // The default, do something here to be exclusive to -6 instead of doing nothing?
797 connection_prefix = "udp";
981 break; 798 break;
982 case '6': 799 case '6':
983 xasprintf(&ip_version, "udp6:"); 800 connection_prefix = "udp6";
984 if(verbose>2) 801 break;
985 printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); 802 case connection_prefix_index:
803 connection_prefix = optarg;
986 break; 804 break;
987 case 'M': 805 case 'M':
988 if ( strspn( optarg, "0123456789.," ) == strlen( optarg ) ) { 806 if (strspn(optarg, "0123456789.,") == strlen(optarg)) {
989 multiplier=strtod(optarg,NULL); 807 config.evaluation_params.multiplier = strtod(optarg, NULL);
808 config.evaluation_params.multiplier_set = true;
990 } 809 }
991 break; 810 break;
992 case 'f': 811 case ignore_mib_parsing_errors_index:
993 if (multiplier != 1.0) { 812 config.snmp_params.ignore_mib_parsing_errors = true;
994 fmtstr=optarg; 813 break;
995 fmtstr_set = true; 814 case 'f': // Deprecated format option for floating point values
815 break;
816 case output_format_index: {
817 parsed_output_format parser = mp_parse_output_format(optarg);
818 if (!parser.parsing_success) {
819 // TODO List all available formats here, maybe add anothoer usage function
820 printf("Invalid output format: %s\n", optarg);
821 exit(STATE_UNKNOWN);
822 }
823
824 config.output_format_is_set = true;
825 config.output_format = parser.output_format;
826 break;
827 }
828 case calculate_rate:
829 config.evaluation_params.calculate_rate = true;
830 break;
831 case rate_multiplier:
832 if (!is_integer(optarg) ||
833 ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) {
834 usage2(_("Rate multiplier must be a positive integer"), optarg);
996 } 835 }
997 break; 836 break;
998 case L_IGNORE_MIB_PARSING_ERRORS: 837 default:
999 ignore_mib_parsing_errors = true; 838 die(STATE_UNKNOWN, "Unknown option");
1000 } 839 }
1001 } 840 }
1002 841
1003 if (server_address == NULL) 842 if (config.snmp_params.snmp_session.peername == NULL) {
1004 server_address = argv[optind]; 843 config.snmp_params.snmp_session.peername = argv[optind];
1005 844 }
1006 if (community == NULL)
1007 community = strdup (DEFAULT_COMMUNITY);
1008
1009 return validate_arguments ();
1010}
1011
1012
1013/******************************************************************************
1014
1015@@-
1016<sect3>
1017<title>validate_arguments</title>
1018
1019<para>&PROTO_validate_arguments;</para>
1020
1021<para>Checks to see if the default miblist needs to be loaded. Also verifies
1022the authentication and authorization combinations based on protocol version
1023selected.</para>
1024
1025<para></para>
1026
1027</sect3>
1028-@@
1029******************************************************************************/
1030
1031
1032 845
1033int 846 // Build true peername here if necessary
1034validate_arguments () 847 if (connection_prefix != NULL) {
1035{ 848 // We got something in the connection prefix
1036 /* check whether to load locally installed MIBS (CPU/disk intensive) */ 849 if (strcasecmp(connection_prefix, "udp") == 0) {
1037 if (miblist == NULL) { 850 // The default, do nothing
1038 if (needmibs) { 851 } else if (strcasecmp(connection_prefix, "tcp") == 0) {
1039 miblist = strdup (DEFAULT_MIBLIST); 852 // use tcp/ipv4
1040 }else{ 853 xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s",
1041 miblist = ""; /* don't read any mib files for numeric oids */ 854 config.snmp_params.snmp_session.peername);
855 } else if (strcasecmp(connection_prefix, "tcp6") == 0 ||
856 strcasecmp(connection_prefix, "tcpv6") == 0 ||
857 strcasecmp(connection_prefix, "tcpipv6") == 0 ||
858 strcasecmp(connection_prefix, "udp6") == 0 ||
859 strcasecmp(connection_prefix, "udpipv6") == 0 ||
860 strcasecmp(connection_prefix, "udpv6") == 0) {
861 // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it
862 // works anyway therefore do nothing here
863 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix,
864 config.snmp_params.snmp_session.peername);
865 } else if (strcmp(connection_prefix, "tls") == 0) {
866 // TODO: Anything else to do here?
867 xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s",
868 config.snmp_params.snmp_session.peername);
869 } else if (strcmp(connection_prefix, "dtls") == 0) {
870 // TODO: Anything else to do here?
871 xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s",
872 config.snmp_params.snmp_session.peername);
873 } else if (strcmp(connection_prefix, "unix") == 0) {
874 // TODO: Check whether this is a valid path?
875 xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s",
876 config.snmp_params.snmp_session.peername);
877 } else if (strcmp(connection_prefix, "ipx") == 0) {
878 xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s",
879 config.snmp_params.snmp_session.peername);
880 } else {
881 // Don't know that prefix, die here
882 die(STATE_UNKNOWN, "Unknown connection prefix");
1042 } 883 }
1043 } 884 }
1044 885
1045 /* Check server_address is given */ 886 /* Check server_address is given */
1046 if (server_address == NULL) 887 if (config.snmp_params.snmp_session.peername == NULL) {
1047 die(STATE_UNKNOWN, _("No host specified\n")); 888 die(STATE_UNKNOWN, _("No host specified\n"));
889 }
1048 890
1049 /* Check oid is given */ 891 if (port != NULL) {
1050 if (numoids == 0) 892 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s",
1051 die(STATE_UNKNOWN, _("No OIDs specified\n")); 893 config.snmp_params.snmp_session.peername, port);
1052
1053 if (proto == NULL)
1054 xasprintf(&proto, DEFAULT_PROTOCOL);
1055
1056 if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) { /* snmpv1 or snmpv2c */
1057 numauthpriv = 2;
1058 authpriv = calloc (numauthpriv, sizeof (char *));
1059 authpriv[0] = strdup ("-c");
1060 authpriv[1] = strdup (community);
1061 } 894 }
1062 else if ( strcmp (proto, "3") == 0 ) { /* snmpv3 args */ 895
1063 if (!(context == NULL)) { 896 /* check whether to load locally installed MIBS (CPU/disk intensive) */
1064 numcontext = 2; 897 if (miblist == NULL) {
1065 contextargs = calloc (numcontext, sizeof (char *)); 898 if (config.snmp_params.need_mibs) {
1066 contextargs[0] = strdup ("-n"); 899 setenv("MIBLS", DEFAULT_MIBLIST, 1);
1067 contextargs[1] = strdup (context); 900 } else {
901 setenv("MIBLS", "NONE", 1);
902 miblist = ""; /* don't read any mib files for numeric oids */
1068 } 903 }
904 } else {
905 // Blatantly stolen from snmplib/snmp_parse_args
906 setenv("MIBS", miblist, 1);
907 }
1069 908
1070 if (seclevel == NULL) 909 // Historical default is SNMP v2c
1071 xasprintf(&seclevel, "noAuthNoPriv"); 910 if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) {
911 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
912 }
1072 913
1073 if (secname == NULL) 914 if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) ||
915 (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */
916 /*
917 config.numauthpriv = 2;
918 config.authpriv = calloc(config.numauthpriv, sizeof(char *));
919 config.authpriv[0] = strdup("-c");
920 config.authpriv[1] = strdup(community);
921 */
922 } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */
923 // generate keys for priv and auth here (if demanded)
924
925 if (config.snmp_params.snmp_session.securityName == NULL) {
1074 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); 926 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
927 }
1075 928
1076 if (strcmp(seclevel, "noAuthNoPriv") == 0) { 929 switch (config.snmp_params.snmp_session.securityLevel) {
1077 numauthpriv = 4; 930 case SNMP_SEC_LEVEL_AUTHPRIV: {
1078 authpriv = calloc (numauthpriv, sizeof (char *)); 931 if (authpasswd == NULL) {
1079 authpriv[0] = strdup ("-l"); 932 die(STATE_UNKNOWN,
1080 authpriv[1] = strdup ("noAuthNoPriv"); 933 "No authentication passphrase was given, but authorization was requested");
1081 authpriv[2] = strdup ("-u");
1082 authpriv[3] = strdup (secname);
1083 } else {
1084 if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
1085 usage2 (_("Invalid seclevel"), seclevel);
1086 } 934 }
1087 935 // auth and priv
1088 if (authproto == NULL ) 936 int priv_key_generated = generate_Ku(
1089 xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); 937 config.snmp_params.snmp_session.securityPrivProto,
1090 938 (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd,
1091 if (authpasswd == NULL) 939 strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey,
1092 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); 940 &config.snmp_params.snmp_session.securityPrivKeyLen);
1093 941
1094 if ( strcmp(seclevel, "authNoPriv") == 0 ) { 942 if (priv_key_generated != SNMPERR_SUCCESS) {
1095 numauthpriv = 8; 943 die(STATE_UNKNOWN, "Failed to generate privacy key");
1096 authpriv = calloc (numauthpriv, sizeof (char *));
1097 authpriv[0] = strdup ("-l");
1098 authpriv[1] = strdup ("authNoPriv");
1099 authpriv[2] = strdup ("-a");
1100 authpriv[3] = strdup (authproto);
1101 authpriv[4] = strdup ("-u");
1102 authpriv[5] = strdup (secname);
1103 authpriv[6] = strdup ("-A");
1104 authpriv[7] = strdup (authpasswd);
1105 } else if ( strcmp(seclevel, "authPriv") == 0 ) {
1106 if (privproto == NULL )
1107 xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
1108
1109 if (privpasswd == NULL)
1110 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
1111
1112 numauthpriv = 12;
1113 authpriv = calloc (numauthpriv, sizeof (char *));
1114 authpriv[0] = strdup ("-l");
1115 authpriv[1] = strdup ("authPriv");
1116 authpriv[2] = strdup ("-a");
1117 authpriv[3] = strdup (authproto);
1118 authpriv[4] = strdup ("-u");
1119 authpriv[5] = strdup (secname);
1120 authpriv[6] = strdup ("-A");
1121 authpriv[7] = strdup (authpasswd);
1122 authpriv[8] = strdup ("-x");
1123 authpriv[9] = strdup (privproto);
1124 authpriv[10] = strdup ("-X");
1125 authpriv[11] = strdup (privpasswd);
1126 } 944 }
1127 } 945 }
1128 946 // fall through
1129 } 947 case SNMP_SEC_LEVEL_AUTHNOPRIV: {
1130 else { 948 if (privpasswd == NULL) {
1131 usage2 (_("Invalid SNMP version"), proto); 949 die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested");
950 }
951 int auth_key_generated = generate_Ku(
952 config.snmp_params.snmp_session.securityAuthProto,
953 (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd,
954 strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey,
955 &config.snmp_params.snmp_session.securityAuthKeyLen);
956
957 if (auth_key_generated != SNMPERR_SUCCESS) {
958 die(STATE_UNKNOWN, "Failed to generate privacy key");
959 }
960 } break;
961 case SNMP_SEC_LEVEL_NOAUTH:
962 // No auth, no priv, not much todo
963 break;
964 }
1132 } 965 }
1133 966
1134 return OK; 967 process_arguments_wrapper result = {
968 .config = config,
969 .errorcode = OK,
970 };
971 return result;
1135} 972}
1136 973
1137
1138
1139/* trim leading whitespace 974/* trim leading whitespace
1140 if there is a leading quote, make sure it balances */ 975 if there is a leading quote, make sure it balances */
1141 976char *trim_whitespaces_and_check_quoting(char *str) {
1142char * 977 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
1143thisarg (char *str) 978 if (str[0] == '\'') { /* handle SIMPLE quoted strings */
1144{ 979 if (strlen(str) == 1 || !strstr(str + 1, "'")) {
1145 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */ 980 die(STATE_UNKNOWN, _("Unbalanced quotes\n"));
1146 if (str[0] == '\'') { /* handle SIMPLE quoted strings */ 981 }
1147 if (strlen (str) == 1 || !strstr (str + 1, "'"))
1148 die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
1149 } 982 }
1150 return str; 983 return str;
1151} 984}
1152 985
1153
1154
1155/* if there's a leading quote, advance to the trailing quote 986/* if there's a leading quote, advance to the trailing quote
1156 set the trailing quote to '\x0' 987 set the trailing quote to '\x0'
1157 if the string continues, advance beyond the comma */ 988 if the string continues, advance beyond the comma */
1158 989
1159char * 990char *get_next_argument(char *str) {
1160nextarg (char *str)
1161{
1162 if (str[0] == '\'') { 991 if (str[0] == '\'') {
1163 str[0] = 0; 992 str[0] = 0;
1164 if (strlen (str) > 1) { 993 if (strlen(str) > 1) {
1165 str = strstr (str + 1, "'"); 994 str = strstr(str + 1, "'");
1166 return (++str); 995 return (++str);
1167 } 996 }
1168 else { 997 return NULL;
1169 return NULL;
1170 }
1171 } 998 }
1172 if (str[0] == ',') { 999 if (str[0] == ',') {
1173 str[0] = 0; 1000 str[0] = 0;
1174 if (strlen (str) > 1) { 1001 if (strlen(str) > 1) {
1175 return (++str); 1002 return (++str);
1176 } 1003 }
1177 else { 1004 return NULL;
1178 return NULL;
1179 }
1180 } 1005 }
1181 if ((str = strstr (str, ",")) && strlen (str) > 1) { 1006 if ((str = strstr(str, ",")) && strlen(str) > 1) {
1182 str[0] = 0; 1007 str[0] = 0;
1183 return (++str); 1008 return (++str);
1184 } 1009 }
1185 return NULL; 1010 return NULL;
1186} 1011}
1187 1012
1013void print_help(void) {
1014 print_revision(progname, NP_VERSION);
1188 1015
1016 printf(COPYRIGHT, copyright, email);
1189 1017
1190/* multiply result (values 0 < n < 1 work as divider) */ 1018 printf("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1191char *
1192multiply (char *str)
1193{
1194 char *endptr;
1195 double val;
1196 char *conv = "%f";
1197
1198 if(multiplier == 1)
1199 return(str);
1200
1201 if(verbose>2)
1202 printf(" multiply input: %s\n", str);
1203
1204 val = strtod (str, &endptr);
1205 if ((val == 0.0) && (endptr == str)) {
1206 die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, str);
1207 }
1208
1209 if(verbose>2)
1210 printf(" multiply extracted double: %f\n", val);
1211 val *= multiplier;
1212 if (fmtstr_set) {
1213 conv = fmtstr;
1214 }
1215 if (val == (int)val) {
1216 snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val);
1217 } else {
1218 if(verbose>2)
1219 printf(" multiply using format: %s\n", conv);
1220 snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val);
1221 }
1222 if(verbose>2)
1223 printf(" multiply result: %s\n", buffer);
1224 return buffer;
1225}
1226
1227
1228void
1229print_help (void)
1230{
1231 print_revision (progname, NP_VERSION);
1232
1233 printf (COPYRIGHT, copyright, email);
1234
1235 printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1236 1019
1237 printf ("\n\n"); 1020 printf("\n\n");
1238 1021
1239 print_usage (); 1022 print_usage();
1240 1023
1241 printf (UT_HELP_VRSN); 1024 printf(UT_HELP_VRSN);
1242 printf (UT_EXTRA_OPTS); 1025 printf(UT_EXTRA_OPTS);
1243 printf (UT_IPv46); 1026 printf(UT_HOST_PORT, 'p', DEFAULT_PORT);
1244
1245 printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1246 1027
1247 /* SNMP and Authentication Protocol */ 1028 /* SNMP and Authentication Protocol */
1248 printf (" %s\n", "-n, --next"); 1029 printf(" %s\n", "-n, --next");
1249 printf (" %s\n", _("Use SNMP GETNEXT instead of SNMP GET")); 1030 printf(" %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1250 printf (" %s\n", "-P, --protocol=[1|2c|3]"); 1031 printf(" %s\n", "-P, --protocol=[1|2c|3]");
1251 printf (" %s\n", _("SNMP protocol version")); 1032 printf(" %s\n", _("SNMP protocol version"));
1252 printf (" %s\n", "-N, --context=CONTEXT"); 1033 printf(" %s\n", "-N, --context=CONTEXT");
1253 printf (" %s\n", _("SNMPv3 context")); 1034 printf(" %s\n", _("SNMPv3 context"));
1254 printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); 1035 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1255 printf (" %s\n", _("SNMPv3 securityLevel")); 1036 printf(" %s\n", _("SNMPv3 securityLevel"));
1256 printf (" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); 1037 printf(" %s\n", "-a, --authproto=[MD5|SHA|SHA224|SHA256|SHA384|SHA512]");
1257 printf (" %s\n", _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); 1038 printf(" %s\n", _("SNMPv3 auth proto"));
1258 printf (" %s\n", _("if < 5.8 SHA (1) and MD5 should be available, if >= 5.8 additionally SHA-224, SHA-256, SHA-384 and SHA-512")); 1039#ifdef HAVE_USM_DES_PRIV_PROTOCOL
1259 printf (" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); 1040 printf(" %s\n", "-x, --privproto=[DES|AES|AES192|AES256]");
1260 printf (" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools")); 1041 printf(" %s\n", _("SNMPv3 priv proto (default DES)"));
1261 printf (" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); 1042#else
1043 printf(" %s\n", "-x, --privproto=[AES|AES192|AES256]");
1044 printf(" %s\n", _("SNMPv3 priv proto (default AES)"));
1045#endif
1262 1046
1263 /* Authentication Tokens*/ 1047 /* Authentication Tokens*/
1264 printf (" %s\n", "-C, --community=STRING"); 1048 printf(" %s\n", "-C, --community=STRING");
1265 printf (" %s ", _("Optional community string for SNMP communication")); 1049 printf(" %s ", _("Optional community string for SNMP communication"));
1266 printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY); 1050 printf("(%s \"%s\")\n", _("default is"), DEFAULT_COMMUNITY);
1267 printf (" %s\n", "-U, --secname=USERNAME"); 1051 printf(" %s\n", "-U, --secname=USERNAME");
1268 printf (" %s\n", _("SNMPv3 username")); 1052 printf(" %s\n", _("SNMPv3 username"));
1269 printf (" %s\n", "-A, --authpasswd=PASSWORD"); 1053 printf(" %s\n", "-A, --authpasswd=PASSWORD");
1270 printf (" %s\n", _("SNMPv3 authentication password")); 1054 printf(" %s\n", _("SNMPv3 authentication password"));
1271 printf (" %s\n", "-X, --privpasswd=PASSWORD"); 1055 printf(" %s\n", "-X, --privpasswd=PASSWORD");
1272 printf (" %s\n", _("SNMPv3 privacy password")); 1056 printf(" %s\n", _("SNMPv3 privacy password"));
1057 printf(" %s\n", "--connection-prefix");
1058 printf(" Connection prefix, may be one of udp, udp6, tcp, unix, ipx, udp6, udpv6, udpipv6, "
1059 "tcp6, tcpv6, tcpipv6, tls, dtls - "
1060 "default is \"udp\"\n");
1273 1061
1274 /* OID Stuff */ 1062 /* OID Stuff */
1275 printf (" %s\n", "-o, --oid=OID(s)"); 1063 printf(" %s\n", "-o, --oid=OID(s)");
1276 printf (" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); 1064 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1277 printf (" %s\n", "-m, --miblist=STRING"); 1065 printf(" %s\n", "-m, --miblist=STRING");
1278 printf (" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); 1066 printf(" %s\n",
1279 printf (" %s\n", _("for symbolic OIDs.)")); 1067 _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1280 printf (" %s\n", "-d, --delimiter=STRING"); 1068 printf(" %s\n", _("for symbolic OIDs.)"));
1281 printf (" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER); 1069 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1282 printf (" %s\n", _("Any data on the right hand side of the delimiter is considered")); 1070 printf(" %s\n", _("to be the data that should be used in the evaluation."));
1283 printf (" %s\n", _("to be the data that should be used in the evaluation.")); 1071 printf(" %s\n", "-z, --nulloid=#");
1284 printf (" %s\n", "-z, --nulloid=#"); 1072 printf(" %s\n", _("If the check returns a 0 length string or NULL value"));
1285 printf (" %s\n", _("If the check returns a 0 length string or NULL value")); 1073 printf(" %s\n", _("This option allows you to choose what status you want it to exit"));
1286 printf (" %s\n", _("This option allows you to choose what status you want it to exit")); 1074 printf(" %s\n", _("Excluding this option renders the default exit of 3(STATE_UNKNOWN)"));
1287 printf (" %s\n", _("Excluding this option renders the default exit of 3(STATE_UNKNOWN)")); 1075 printf(" %s\n", _("0 = OK"));
1288 printf (" %s\n", _("0 = OK")); 1076 printf(" %s\n", _("1 = WARNING"));
1289 printf (" %s\n", _("1 = WARNING")); 1077 printf(" %s\n", _("2 = CRITICAL"));
1290 printf (" %s\n", _("2 = CRITICAL")); 1078 printf(" %s\n", _("3 = UNKNOWN"));
1291 printf (" %s\n", _("3 = UNKNOWN"));
1292 1079
1293 /* Tests Against Integers */ 1080 /* Tests Against Integers */
1294 printf (" %s\n", "-w, --warning=THRESHOLD(s)"); 1081 printf(" %s\n", "-w, --warning=THRESHOLD(s)");
1295 printf (" %s\n", _("Warning threshold range(s)")); 1082 printf(" %s\n", _("Warning threshold range(s)"));
1296 printf (" %s\n", "-c, --critical=THRESHOLD(s)"); 1083 printf(" %s\n", "-c, --critical=THRESHOLD(s)");
1297 printf (" %s\n", _("Critical threshold range(s)")); 1084 printf(" %s\n", _("Critical threshold range(s)"));
1298 printf (" %s\n", "--rate"); 1085 printf(" %s\n", "--offset=OFFSET");
1299 printf (" %s\n", _("Enable rate calculation. See 'Rate Calculation' below")); 1086 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1300 printf (" %s\n", "--rate-multiplier");
1301 printf (" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1302 printf (" %s\n", "--offset=OFFSET");
1303 printf (" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1304 1087
1305 /* Tests Against Strings */ 1088 /* Tests Against Strings */
1306 printf (" %s\n", "-s, --string=STRING"); 1089 printf(" %s\n", "-s, --string=STRING");
1307 printf (" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); 1090 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1308 printf (" %s\n", "-r, --ereg=REGEX"); 1091 printf(" %s\n", "-r, --ereg=REGEX");
1309 printf (" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); 1092 printf(" %s\n",
1310 printf (" %s\n", "-R, --eregi=REGEX"); 1093 _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1311 printf (" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); 1094 printf(" %s\n", "-R, --eregi=REGEX");
1312 printf (" %s\n", "--invert-search"); 1095 printf(" %s\n",
1313 printf (" %s\n", _("Invert search result (CRITICAL if found)")); 1096 _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1097 printf(" %s\n", "--invert-search");
1098 printf(" %s\n", _("Invert search result (CRITICAL if found)"));
1314 1099
1315 /* Output Formatting */ 1100 /* Output Formatting */
1316 printf (" %s\n", "-l, --label=STRING"); 1101 printf(" %s\n", "-l, --label=STRING");
1317 printf (" %s\n", _("Prefix label for output from plugin")); 1102 printf(" %s\n", _("Prefix label for output from plugin"));
1318 printf (" %s\n", "-u, --units=STRING"); 1103 printf(" %s\n", "-u, --units=STRING");
1319 printf (" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); 1104 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1320 printf (" %s\n", "-D, --output-delimiter=STRING"); 1105 printf(" %s\n", "-M, --multiplier=FLOAT");
1321 printf (" %s\n", _("Separates output on multiple OID requests")); 1106 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1322 printf (" %s\n", "-M, --multiplier=FLOAT"); 1107 printf(UT_OUTPUT_FORMAT);
1323 printf (" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1324 printf (" %s\n", "-f, --fmtstr=STRING");
1325 printf (" %s\n", _("C-style format string for float values (see option -M)"));
1326
1327 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1328 printf (" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5"));
1329 printf (" %s\n", "-e, --retries=INTEGER");
1330 printf (" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES);
1331
1332 printf (" %s\n", "-O, --perf-oids");
1333 printf (" %s\n", _("Label performance data with OIDs instead of --label's"));
1334
1335 printf (" %s\n", "--ignore-mib-parsing-errors");
1336 printf (" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files"));
1337
1338 printf (UT_VERBOSE);
1339
1340 printf ("\n");
1341 printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1342 printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1343 printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1344
1345 printf ("\n");
1346 printf ("%s\n", _("Notes:"));
1347 printf (" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1348 printf (" %s\n", _("list (lists with internal spaces must be quoted)."));
1349 1108
1350 printf(" -%s", UT_THRESHOLDS_NOTES); 1109 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1110 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: "
1111 "timeout_interval * retries + 5"));
1112 printf(" %s\n", "-e, --retries=INTEGER");
1113 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "),
1114 DEFAULT_RETRIES);
1115
1116 printf(" %s\n", "-O, --perf-oids");
1117 printf(" %s\n", _("Label performance data with OIDs instead of --label's"));
1351 1118
1352 printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); 1119 printf(" %s\n", "--ignore-mib-parsing-errors");
1353 printf (" %s\n", _("- Note that only one string and one regex may be checked at present")); 1120 printf(" %s\n", _("Do to not print errors encountered when parsing MIB files"));
1354 printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); 1121
1355 printf (" %s\n", _("returned from the SNMP query is an unsigned integer.")); 1122 printf(UT_VERBOSE);
1356 1123
1357 printf("\n"); 1124 printf("\n");
1358 printf("%s\n", _("Rate Calculation:")); 1125 printf("%s\n", _("This plugin relies (links against) on the NET-SNMP libraries."));
1359 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when")); 1126 printf("%s\n",
1360 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp")); 1127 _("if you don't have the libraries installed, you will need to download them from"));
1361 printf(" %s\n", _("saves the last state information in a file so that the rate per second")); 1128 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1362 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1363 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1364 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1365 printf(" %s\n", _("changing the arguments will create a new state file."));
1366
1367 printf (UT_SUPPORT);
1368}
1369 1129
1130 printf("\n");
1131 printf("%s\n", _("Notes:"));
1132 printf(" %s\n",
1133 _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1134 printf(" %s\n", _("list (lists with internal spaces must be quoted)."));
1370 1135
1136 printf(" -%s", UT_THRESHOLDS_NOTES);
1137
1138 printf(" %s\n",
1139 _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1140 printf(" %s\n", _("- Note that only one string and one regex may be checked at present"));
1141 printf(" %s\n",
1142 _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1143 printf(" %s\n", _("returned from the SNMP query is an unsigned integer."));
1144
1145 printf(UT_SUPPORT);
1146}
1371 1147
1372void 1148void print_usage(void) {
1373print_usage (void) 1149 printf("%s\n", _("Usage:"));
1374{ 1150 printf("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n", progname);
1375 printf ("%s\n", _("Usage:")); 1151 printf("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1376 printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname); 1152 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1377 printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n"); 1153 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n");
1378 printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); 1154 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1379 printf ("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n"); 1155 printf("[-M multiplier]\n");
1380 printf ("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1381 printf ("[-M multiplier [-f format]]\n");
1382} 1156}
diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c
new file mode 100644
index 00000000..83e94a34
--- /dev/null
+++ b/plugins/check_snmp.d/check_snmp_helpers.c
@@ -0,0 +1,938 @@
1#include "./check_snmp_helpers.h"
2#include <string.h>
3#include "../../lib/utils_base.h"
4#include "config.h"
5#include <assert.h>
6#include "../utils.h"
7#include "output.h"
8#include "states.h"
9#include <sys/stat.h>
10#include <ctype.h>
11
12extern int verbose;
13
14check_snmp_test_unit check_snmp_test_unit_init() {
15 check_snmp_test_unit tmp = {
16 .threshold = mp_thresholds_init(),
17 };
18 return tmp;
19}
20
21int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit test_units[],
22 size_t max_test_units, bool is_critical) {
23
24 if (threshold_string == NULL || strlen(threshold_string) == 0) {
25 // No input, do nothing
26 return 0;
27 }
28
29 if (strchr(threshold_string, ',') != NULL) {
30 // Got a comma in the string, should be multiple values
31 size_t tu_index = 0;
32
33 while (threshold_string[0] == ',') {
34 // got commas at the beginning, so skip some values
35 tu_index++;
36 threshold_string++;
37 }
38
39 char *thr_string_copy = strdup(threshold_string);
40 for (char *ptr = strtok(thr_string_copy, ", "); ptr != NULL;
41 ptr = strtok(NULL, ", "), tu_index++) {
42
43 if (tu_index > max_test_units) {
44 // More thresholds then values, just ignore them
45 return 0;
46 }
47
48 // edge case: maybe we got `,,` to skip a value
49 if (strlen(ptr) == 0) {
50 // no threshold given, do not set it then
51 continue;
52 }
53
54 mp_range_parsed tmp = mp_parse_range_string(ptr);
55 if (tmp.error != MP_PARSING_SUCCESS) {
56 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr);
57 }
58
59 if (is_critical) {
60 test_units[tu_index].threshold.critical = tmp.range;
61 test_units[tu_index].threshold.critical_is_set = true;
62 } else {
63 test_units[tu_index].threshold.warning = tmp.range;
64 test_units[tu_index].threshold.warning_is_set = true;
65 }
66 }
67
68 free(thr_string_copy);
69 } else {
70 // Single value
71 // only valid for the first test unit
72 mp_range_parsed tmp = mp_parse_range_string(threshold_string);
73 if (tmp.error != MP_PARSING_SUCCESS) {
74 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string);
75 }
76
77 if (is_critical) {
78 test_units[0].threshold.critical = tmp.range;
79 test_units[0].threshold.critical_is_set = true;
80 } else {
81 test_units[0].threshold.warning = tmp.range;
82 test_units[0].threshold.warning_is_set = true;
83 }
84 }
85
86 return 0;
87}
88
89const int DEFAULT_PROTOCOL = SNMP_VERSION_1;
90const char DEFAULT_OUTPUT_DELIMITER[] = " ";
91
92const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192;
93
94check_snmp_config check_snmp_config_init() {
95 check_snmp_config tmp = {
96 .snmp_params =
97 {
98 .use_getnext = false,
99
100 .ignore_mib_parsing_errors = false,
101 .need_mibs = false,
102
103 .test_units = NULL,
104 .num_of_test_units = 0,
105 },
106
107 .evaluation_params =
108 {
109 .nulloid_result = STATE_UNKNOWN, // state to return if no result for query
110
111 .invert_search = true,
112 .regex_cmp_value = {},
113 .string_cmp_value = "",
114
115 .multiplier = 1.0,
116 .multiplier_set = false,
117 .offset = 0,
118 .offset_set = false,
119
120 .use_oid_as_perf_data_label = false,
121
122 .calculate_rate = false,
123 .rate_multiplier = 1,
124 },
125 };
126
127 snmp_sess_init(&tmp.snmp_params.snmp_session);
128
129 tmp.snmp_params.snmp_session.retries = DEFAULT_RETRIES;
130 tmp.snmp_params.snmp_session.version = DEFAULT_SNMP_VERSION;
131 tmp.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
132 tmp.snmp_params.snmp_session.community = (unsigned char *)"public";
133 tmp.snmp_params.snmp_session.community_len = strlen("public");
134 return tmp;
135}
136
137snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) {
138 if (parameters.ignore_mib_parsing_errors) {
139 char *opt_toggle_res = snmp_mib_toggle_options("e");
140 if (opt_toggle_res != NULL) {
141 die(STATE_UNKNOWN, "Unable to disable MIB parsing errors");
142 }
143 }
144
145 struct snmp_pdu *pdu = NULL;
146 if (parameters.use_getnext) {
147 pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
148 } else {
149 pdu = snmp_pdu_create(SNMP_MSG_GET);
150 }
151
152 for (size_t i = 0; i < parameters.num_of_test_units; i++) {
153 assert(parameters.test_units[i].oid != NULL);
154 if (verbose > 0) {
155 printf("OID %zu to parse: %s\n", i, parameters.test_units[i].oid);
156 }
157
158 oid tmp_OID[MAX_OID_LEN];
159 size_t tmp_OID_len = MAX_OID_LEN;
160 if (snmp_parse_oid(parameters.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) {
161 // success
162 snmp_add_null_var(pdu, tmp_OID, tmp_OID_len);
163 } else {
164 // failed
165 snmp_perror("Parsing failure");
166 die(STATE_UNKNOWN, "Failed to parse OID\n");
167 }
168 }
169
170 const int timeout_safety_tolerance = 5;
171 alarm((timeout_interval * (unsigned int)parameters.snmp_session.retries) +
172 timeout_safety_tolerance);
173
174 struct snmp_session *active_session = snmp_open(&parameters.snmp_session);
175 if (active_session == NULL) {
176 int pcliberr = 0;
177 int psnmperr = 0;
178 char *pperrstring = NULL;
179 snmp_error(&parameters.snmp_session, &pcliberr, &psnmperr, &pperrstring);
180 die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring);
181 }
182
183 struct snmp_pdu *response = NULL;
184 int snmp_query_status = snmp_synch_response(active_session, pdu, &response);
185
186 if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) {
187 int pcliberr = 0;
188 int psnmperr = 0;
189 char *pperrstring = NULL;
190 snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring);
191
192 if (psnmperr == SNMPERR_TIMEOUT) {
193 // We exit with critical here for some historical reason
194 die(STATE_CRITICAL, "SNMP query ran into a timeout\n");
195 }
196 die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring);
197 }
198
199 snmp_close(active_session);
200
201 /* disable alarm again */
202 alarm(0);
203
204 snmp_responces result = {
205 .errorcode = OK,
206 .response_values = calloc(parameters.num_of_test_units, sizeof(response_value)),
207 };
208
209 if (result.response_values == NULL) {
210 result.errorcode = ERROR;
211 return result;
212 }
213
214 // We got the the query results, now process them
215 size_t loop_index = 0;
216 for (netsnmp_variable_list *vars = response->variables; vars;
217 vars = vars->next_variable, loop_index++) {
218
219 for (size_t jdx = 0; jdx < vars->name_length; jdx++) {
220 result.response_values[loop_index].oid[jdx] = vars->name[jdx];
221 }
222 result.response_values[loop_index].oid_length = vars->name_length;
223
224 switch (vars->type) {
225 case ASN_OCTET_STR: {
226 result.response_values[loop_index].string_response = strdup((char *)vars->val.string);
227 result.response_values[loop_index].type = vars->type;
228 if (verbose) {
229 printf("Debug: Got a string as response: %s\n", vars->val.string);
230 }
231 }
232 continue;
233 case ASN_OPAQUE:
234 if (verbose) {
235 printf("Debug: Got OPAQUE\n");
236 }
237 break;
238 /* Numerical values */
239 case ASN_COUNTER64: {
240 if (verbose) {
241 printf("Debug: Got counter64\n");
242 }
243 struct counter64 tmp = *(vars->val.counter64);
244 uint64_t counter = (tmp.high << 32) + tmp.low;
245 result.response_values[loop_index].value.uIntVal = counter;
246 result.response_values[loop_index].type = vars->type;
247 } break;
248 case ASN_GAUGE: // same as ASN_UNSIGNED
249 case ASN_TIMETICKS:
250 case ASN_COUNTER:
251 case ASN_UINTEGER: {
252 if (verbose) {
253 printf("Debug: Got a Integer like\n");
254 }
255 result.response_values[loop_index].value.uIntVal = (unsigned long)*(vars->val.integer);
256 result.response_values[loop_index].type = vars->type;
257 } break;
258 case ASN_INTEGER: {
259 if (verbose) {
260 printf("Debug: Got a Integer\n");
261 }
262 result.response_values[loop_index].value.intVal = *(vars->val.integer);
263 result.response_values[loop_index].type = vars->type;
264 } break;
265 case ASN_FLOAT: {
266 if (verbose) {
267 printf("Debug: Got a float\n");
268 }
269 result.response_values[loop_index].value.doubleVal = *(vars->val.floatVal);
270 result.response_values[loop_index].type = vars->type;
271 } break;
272 case ASN_DOUBLE: {
273 if (verbose) {
274 printf("Debug: Got a double\n");
275 }
276 result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal);
277 result.response_values[loop_index].type = vars->type;
278 } break;
279 case ASN_IPADDRESS:
280 if (verbose) {
281 printf("Debug: Got an IP address\n");
282 }
283 result.response_values[loop_index].type = vars->type;
284
285 // TODO: print address here, state always ok? or regex match?
286 break;
287 default:
288 if (verbose) {
289 printf("Debug: Got a unmatched result type: %hhu\n", vars->type);
290 }
291 // TODO: Error here?
292 break;
293 }
294 }
295
296 return result;
297}
298
299check_snmp_evaluation evaluate_single_unit(response_value response,
300 check_snmp_evaluation_parameters eval_params,
301 check_snmp_test_unit test_unit, time_t query_timestamp,
302 check_snmp_state_entry prev_state,
303 bool have_previous_state) {
304 mp_subcheck sc_oid_test = mp_subcheck_init();
305
306 if ((test_unit.label != NULL) && (strcmp(test_unit.label, "") != 0)) {
307 xasprintf(&sc_oid_test.output, "%s - ", test_unit.label);
308 } else {
309 sc_oid_test.output = strdup("");
310 }
311
312 char oid_string[(MAX_OID_LEN * 2) + 1] = {};
313
314 int oid_string_result =
315 snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, response.oid, response.oid_length);
316 if (oid_string_result <= 0) {
317 // TODO error here
318 die(STATE_UNKNOWN, "snprint_objid failed\n");
319 }
320
321 xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string);
322 sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK);
323
324 if (verbose > 2) {
325 printf("Processing oid %s\n", oid_string);
326 }
327
328 bool got_a_numerical_value = false;
329 mp_perfdata_value pd_result_val = {0};
330
331 check_snmp_state_entry result_state = {
332 .timestamp = query_timestamp,
333 .oid_length = response.oid_length,
334 .type = response.type,
335 };
336
337 for (size_t i = 0; i < response.oid_length; i++) {
338 result_state.oid[i] = response.oid[i];
339 }
340
341 if (have_previous_state) {
342 if (query_timestamp == prev_state.timestamp) {
343 // somehow we have the same timestamp again, that can't be good
344 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_UNKNOWN);
345 xasprintf(&sc_oid_test.output, "Time duration between plugin calls is invalid");
346
347 check_snmp_evaluation result = {
348 .sc = sc_oid_test,
349 .state = result_state,
350 };
351
352 return result;
353 }
354 }
355 // compute rate time difference
356 double timeDiff = 0;
357 if (have_previous_state) {
358 if (verbose) {
359 printf("Previous timestamp: %s", ctime(&prev_state.timestamp));
360 printf("Current timestamp: %s", ctime(&query_timestamp));
361 }
362 timeDiff = difftime(query_timestamp, prev_state.timestamp) / eval_params.rate_multiplier;
363 }
364
365 mp_perfdata pd_num_val = {};
366
367 switch (response.type) {
368 case ASN_OCTET_STR: {
369 char *tmp = response.string_response;
370 if (strchr(tmp, '"') != NULL) {
371 // got double quote in the string
372 if (strchr(tmp, '\'') != NULL) {
373 // got single quote in the string too
374 // dont quote that at all to avoid even more confusion
375 xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp);
376 } else {
377 // quote with single quotes
378 xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp);
379 }
380 } else {
381 // quote with double quotes
382 xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp);
383 }
384
385 if (strlen(tmp) == 0) {
386 sc_oid_test = mp_set_subcheck_state(sc_oid_test, eval_params.nulloid_result);
387 }
388
389 // String matching test
390 if ((test_unit.eval_mthd.crit_string)) {
391 if (strcmp(tmp, eval_params.string_cmp_value)) {
392 sc_oid_test = mp_set_subcheck_state(
393 sc_oid_test, (eval_params.invert_search) ? STATE_CRITICAL : STATE_OK);
394 } else {
395 sc_oid_test = mp_set_subcheck_state(
396 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
397 }
398 } else if (test_unit.eval_mthd.crit_regex) {
399 const size_t nmatch = eval_params.regex_cmp_value.re_nsub + 1;
400 regmatch_t pmatch[nmatch];
401 memset(pmatch, '\0', sizeof(regmatch_t) * nmatch);
402
403 int excode = regexec(&eval_params.regex_cmp_value, tmp, nmatch, pmatch, 0);
404 if (excode == 0) {
405 sc_oid_test = mp_set_subcheck_state(
406 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
407 } else if (excode != REG_NOMATCH) {
408 char errbuf[MAX_INPUT_BUFFER] = "";
409 regerror(excode, &eval_params.regex_cmp_value, errbuf, MAX_INPUT_BUFFER);
410 printf(_("Execute Error: %s\n"), errbuf);
411 exit(STATE_CRITICAL);
412 } else { // REG_NOMATCH
413 sc_oid_test = mp_set_subcheck_state(
414 sc_oid_test, eval_params.invert_search ? STATE_CRITICAL : STATE_OK);
415 }
416 }
417 } break;
418 case ASN_COUNTER64:
419 got_a_numerical_value = true;
420
421 result_state.value.uIntVal = response.value.uIntVal;
422 result_state.type = response.type;
423
424 // TODO: perfdata unit counter
425 if (eval_params.calculate_rate && have_previous_state) {
426 if (prev_state.value.uIntVal > response.value.uIntVal) {
427 // overflow
428 unsigned long long tmp =
429 (UINT64_MAX - prev_state.value.uIntVal) + response.value.uIntVal;
430
431 tmp /= timeDiff;
432 pd_result_val = mp_create_pd_value(tmp);
433 } else {
434 pd_result_val = mp_create_pd_value(
435 (response.value.uIntVal - prev_state.value.uIntVal) / timeDiff);
436 }
437 } else {
438 // It's only a counter if we cont compute rate
439 pd_num_val.uom = "c";
440 pd_result_val = mp_create_pd_value(response.value.uIntVal);
441 }
442 break;
443 case ASN_GAUGE: // same as ASN_UNSIGNED
444 case ASN_TIMETICKS:
445 case ASN_COUNTER:
446 case ASN_UINTEGER: {
447 got_a_numerical_value = true;
448 long long treated_value = (long long)response.value.uIntVal;
449
450 if (eval_params.multiplier_set || eval_params.offset_set) {
451 double processed = (double)response.value.uIntVal;
452
453 if (eval_params.offset_set) {
454 processed += eval_params.offset;
455 }
456
457 if (eval_params.multiplier_set) {
458 processed = processed * eval_params.multiplier;
459 }
460
461 treated_value = lround(processed);
462 }
463
464 result_state.value.intVal = treated_value;
465
466 if (eval_params.calculate_rate && have_previous_state) {
467 if (verbose > 2) {
468 printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__,
469 prev_state.value.intVal);
470 printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__,
471 treated_value);
472 }
473 double rate = (treated_value - prev_state.value.intVal) / timeDiff;
474 pd_result_val = mp_create_pd_value(rate);
475 } else {
476 pd_result_val = mp_create_pd_value(treated_value);
477
478 if (response.type == ASN_COUNTER) {
479 pd_num_val.uom = "c";
480 }
481 }
482
483 } break;
484 case ASN_INTEGER: {
485 if (eval_params.multiplier_set || eval_params.offset_set) {
486 double processed = (double)response.value.intVal;
487
488 if (eval_params.offset_set) {
489 processed += eval_params.offset;
490 }
491
492 if (eval_params.multiplier_set) {
493 processed *= eval_params.multiplier;
494 }
495
496 result_state.value.doubleVal = processed;
497
498 if (eval_params.calculate_rate && have_previous_state) {
499 pd_result_val =
500 mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff);
501 } else {
502 pd_result_val = mp_create_pd_value(processed);
503 }
504 } else {
505 result_state.value.intVal = response.value.intVal;
506
507 if (eval_params.calculate_rate && have_previous_state) {
508 pd_result_val = mp_create_pd_value(
509 (response.value.intVal - prev_state.value.intVal) / timeDiff);
510 } else {
511 pd_result_val = mp_create_pd_value(response.value.intVal);
512 }
513 }
514
515 got_a_numerical_value = true;
516 } break;
517 case ASN_FLOAT: // fallthrough
518 case ASN_DOUBLE: {
519 got_a_numerical_value = true;
520 double tmp = response.value.doubleVal;
521 if (eval_params.offset_set) {
522 tmp += eval_params.offset;
523 }
524
525 if (eval_params.multiplier_set) {
526 tmp *= eval_params.multiplier;
527 }
528
529 if (eval_params.calculate_rate && have_previous_state) {
530 pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff);
531 } else {
532 pd_result_val = mp_create_pd_value(tmp);
533 }
534 got_a_numerical_value = true;
535
536 result_state.value.doubleVal = tmp;
537 } break;
538 case ASN_IPADDRESS:
539 // TODO
540 break;
541 }
542
543 if (got_a_numerical_value) {
544 if (eval_params.use_oid_as_perf_data_label) {
545 // Use oid for perdata label
546 pd_num_val.label = strdup(oid_string);
547 // TODO strdup error checking
548 } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) {
549 pd_num_val.label = strdup(test_unit.label);
550 } else {
551 pd_num_val.label = strdup(test_unit.oid);
552 }
553
554 if (!(eval_params.calculate_rate && !have_previous_state)) {
555 // some kind of numerical value
556 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
557 pd_num_val.uom = test_unit.unit_value;
558 }
559
560 pd_num_val.value = pd_result_val;
561
562 xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output,
563 pd_value_to_string(pd_result_val));
564
565 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
566 xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value);
567 }
568
569 if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) {
570 pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold);
571 mp_state_enum tmp_state = mp_get_pd_status(pd_num_val);
572
573 if (tmp_state == STATE_WARNING) {
574 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING);
575 xasprintf(&sc_oid_test.output, "%s - number violates warning threshold",
576 sc_oid_test.output);
577 } else if (tmp_state == STATE_CRITICAL) {
578 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL);
579 xasprintf(&sc_oid_test.output, "%s - number violates critical threshold",
580 sc_oid_test.output);
581 }
582 }
583
584 mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val);
585 } else {
586 // should calculate rate, but there is no previous state, so first run
587 // exit with ok now
588 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK);
589 xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay",
590 sc_oid_test.output);
591 }
592 }
593
594 check_snmp_evaluation result = {
595 .sc = sc_oid_test,
596 .state = result_state,
597 };
598
599 return result;
600}
601
602char *_np_state_generate_key(int argc, char **argv);
603
604/*
605 * If time=NULL, use current time. Create state file, with state format
606 * version, default text. Writes version, time, and data. Avoid locking
607 * problems - use mv to write and then swap. Possible loss of state data if
608 * two things writing to same key at same time.
609 * Will die with UNKNOWN if errors
610 */
611void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) {
612 time_t current_time;
613 if (timestamp == 0) {
614 time(&current_time);
615 } else {
616 current_time = timestamp;
617 }
618
619 int result = 0;
620
621 /* If file doesn't currently exist, create directories */
622 if (access(stateKey._filename, F_OK) != 0) {
623 char *directories = NULL;
624 result = asprintf(&directories, "%s", stateKey._filename);
625 if (result < 0) {
626 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
627 }
628
629 for (char *p = directories + 1; *p; p++) {
630 if (*p == '/') {
631 *p = '\0';
632 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
633 /* Can't free this! Otherwise error message is wrong! */
634 /* np_free(directories); */
635 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
636 }
637 *p = '/';
638 }
639 }
640
641 if (directories) {
642 free(directories);
643 }
644 }
645
646 char *temp_file = NULL;
647 result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename);
648 if (result < 0) {
649 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
650 }
651
652 int temp_file_desc = 0;
653 if ((temp_file_desc = mkstemp(temp_file)) == -1) {
654 if (temp_file) {
655 free(temp_file);
656 }
657 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
658 }
659
660 FILE *temp_file_pointer = fdopen(temp_file_desc, "w");
661 if (temp_file_pointer == NULL) {
662 close(temp_file_desc);
663 unlink(temp_file);
664 if (temp_file) {
665 free(temp_file);
666 }
667 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
668 }
669
670 fprintf(temp_file_pointer, "# NP State file\n");
671 fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION);
672 fprintf(temp_file_pointer, "%d\n", stateKey.data_version);
673 fprintf(temp_file_pointer, "%lu\n", current_time);
674 fprintf(temp_file_pointer, "%s\n", stringToStore);
675
676 fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP);
677
678 fflush(temp_file_pointer);
679
680 result = fclose(temp_file_pointer);
681
682 fsync(temp_file_desc);
683
684 if (result != 0) {
685 unlink(temp_file);
686 if (temp_file) {
687 free(temp_file);
688 }
689 die(STATE_UNKNOWN, _("Error writing temp file"));
690 }
691
692 if (rename(temp_file, stateKey._filename) != 0) {
693 unlink(temp_file);
694 if (temp_file) {
695 free(temp_file);
696 }
697 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
698 }
699
700 if (temp_file) {
701 free(temp_file);
702 }
703}
704
705/*
706 * Read the state file
707 */
708bool _np_state_read_file(FILE *state_file, state_key stateKey) {
709 time_t current_time;
710 time(&current_time);
711
712 /* Note: This introduces a limit of 8192 bytes in the string data */
713 char *line = (char *)calloc(1, 8192);
714 if (line == NULL) {
715 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
716 }
717
718 bool status = false;
719 enum {
720 STATE_FILE_VERSION,
721 STATE_DATA_VERSION,
722 STATE_DATA_TIME,
723 STATE_DATA_TEXT,
724 STATE_DATA_END
725 } expected = STATE_FILE_VERSION;
726
727 int failure = 0;
728 while (!failure && (fgets(line, 8192, state_file)) != NULL) {
729 size_t pos = strlen(line);
730 if (line[pos - 1] == '\n') {
731 line[pos - 1] = '\0';
732 }
733
734 if (line[0] == '#') {
735 continue;
736 }
737
738 switch (expected) {
739 case STATE_FILE_VERSION: {
740 int i = atoi(line);
741 if (i != NP_STATE_FORMAT_VERSION) {
742 failure++;
743 } else {
744 expected = STATE_DATA_VERSION;
745 }
746 } break;
747 case STATE_DATA_VERSION: {
748 int i = atoi(line);
749 if (i != stateKey.data_version) {
750 failure++;
751 } else {
752 expected = STATE_DATA_TIME;
753 }
754 } break;
755 case STATE_DATA_TIME: {
756 /* If time > now, error */
757 time_t data_time = strtoul(line, NULL, 10);
758 if (data_time > current_time) {
759 failure++;
760 } else {
761 stateKey.state_data->time = data_time;
762 expected = STATE_DATA_TEXT;
763 }
764 } break;
765 case STATE_DATA_TEXT:
766 stateKey.state_data->data = strdup(line);
767 if (stateKey.state_data->data == NULL) {
768 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
769 }
770 stateKey.state_data->length = strlen(line);
771 expected = STATE_DATA_END;
772 status = true;
773 break;
774 case STATE_DATA_END:;
775 }
776 }
777
778 if (line) {
779 free(line);
780 }
781 return status;
782}
783/*
784 * Will return NULL if no data is available (first run). If key currently
785 * exists, read data. If state file format version is not expected, return
786 * as if no data. Get state data version number and compares to expected.
787 * If numerically lower, then return as no previous state. die with UNKNOWN
788 * if exceptional error.
789 */
790state_data *np_state_read(state_key stateKey) {
791 /* Open file. If this fails, no previous state found */
792 FILE *statefile = fopen(stateKey._filename, "r");
793 state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data));
794 if (statefile != NULL) {
795
796 if (this_state_data == NULL) {
797 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
798 }
799
800 this_state_data->data = NULL;
801 stateKey.state_data = this_state_data;
802
803 if (_np_state_read_file(statefile, stateKey)) {
804 this_state_data->errorcode = OK;
805 } else {
806 this_state_data->errorcode = ERROR;
807 }
808
809 fclose(statefile);
810 } else {
811 // Failed to open state file
812 this_state_data->errorcode = ERROR;
813 }
814
815 return stateKey.state_data;
816}
817
818/*
819 * Internal function. Returns either:
820 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
821 * statically compiled shared state directory
822 */
823char *_np_state_calculate_location_prefix(void) {
824 char *env_dir;
825
826 /* Do not allow passing MP_STATE_PATH in setuid plugins
827 * for security reasons */
828 if (!mp_suid()) {
829 env_dir = getenv("MP_STATE_PATH");
830 if (env_dir && env_dir[0] != '\0') {
831 return env_dir;
832 }
833 /* This is the former ENV, for backward-compatibility */
834 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
835 if (env_dir && env_dir[0] != '\0') {
836 return env_dir;
837 }
838 }
839
840 return NP_STATE_DIR_PREFIX;
841}
842
843/*
844 * Initiatializer for state routines.
845 * Sets variables. Generates filename. Returns np_state_key. die with
846 * UNKNOWN if exception
847 */
848state_key np_enable_state(char *keyname, int expected_data_version, const char *plugin_name,
849 int argc, char **argv) {
850 state_key *this_state = (state_key *)calloc(1, sizeof(state_key));
851 if (this_state == NULL) {
852 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
853 }
854
855 char *temp_keyname = NULL;
856 if (keyname == NULL) {
857 temp_keyname = _np_state_generate_key(argc, argv);
858 } else {
859 temp_keyname = strdup(keyname);
860 if (temp_keyname == NULL) {
861 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
862 }
863 }
864
865 /* Die if invalid characters used for keyname */
866 char *tmp_char = temp_keyname;
867 while (*tmp_char != '\0') {
868 if (!(isalnum(*tmp_char) || *tmp_char == '_')) {
869 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
870 }
871 tmp_char++;
872 }
873 this_state->name = temp_keyname;
874 this_state->plugin_name = (char *)plugin_name;
875 this_state->data_version = expected_data_version;
876 this_state->state_data = NULL;
877
878 /* Calculate filename */
879 char *temp_filename = NULL;
880 int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(),
881 (unsigned long)geteuid(), plugin_name, this_state->name);
882 if (error < 0) {
883 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
884 }
885
886 this_state->_filename = temp_filename;
887
888 return *this_state;
889}
890
891/*
892 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
893 * hopefully a unique key per service/plugin invocation. Use the extra-opts
894 * parse of argv, so that uniqueness in parameters are reflected there.
895 */
896char *_np_state_generate_key(int argc, char **argv) {
897 unsigned char result[256];
898
899#ifdef USE_OPENSSL
900 /*
901 * This code path is chosen if openssl is available (which should be the most common
902 * scenario). Alternatively, the gnulib implementation/
903 *
904 */
905 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
906
907 EVP_DigestInit(ctx, EVP_sha256());
908
909 for (int i = 0; i < argc; i++) {
910 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
911 }
912
913 EVP_DigestFinal(ctx, result, NULL);
914#else
915
916 struct sha256_ctx ctx;
917
918 for (int i = 0; i < this_monitoring_plugin->argc; i++) {
919 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
920 }
921
922 sha256_finish_ctx(&ctx, result);
923#endif // FOUNDOPENSSL
924
925 char keyname[41];
926 for (int i = 0; i < 20; ++i) {
927 sprintf(&keyname[2 * i], "%02x", result[i]);
928 }
929
930 keyname[40] = '\0';
931
932 char *keyname_copy = strdup(keyname);
933 if (keyname_copy == NULL) {
934 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
935 }
936
937 return keyname_copy;
938}
diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h
new file mode 100644
index 00000000..95b361ac
--- /dev/null
+++ b/plugins/check_snmp.d/check_snmp_helpers.h
@@ -0,0 +1,71 @@
1#pragma once
2
3#include "./config.h"
4#include <net-snmp/library/asn1.h>
5
6check_snmp_test_unit check_snmp_test_unit_init();
7int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool);
8check_snmp_config check_snmp_config_init();
9
10typedef struct {
11 oid oid[MAX_OID_LEN];
12 size_t oid_length;
13 unsigned char type;
14 union {
15 uint64_t uIntVal;
16 int64_t intVal;
17 double doubleVal;
18 } value;
19 char *string_response;
20} response_value;
21
22typedef struct {
23 int errorcode;
24 response_value *response_values;
25} snmp_responces;
26snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters);
27
28// state is similar to response, but only numerics and a timestamp
29typedef struct {
30 time_t timestamp;
31 oid oid[MAX_OID_LEN];
32 size_t oid_length;
33 unsigned char type;
34 union {
35 unsigned long long uIntVal;
36 long long intVal;
37 double doubleVal;
38 } value;
39} check_snmp_state_entry;
40
41typedef struct {
42 check_snmp_state_entry state;
43 mp_subcheck sc;
44} check_snmp_evaluation;
45check_snmp_evaluation evaluate_single_unit(response_value response,
46 check_snmp_evaluation_parameters eval_params,
47 check_snmp_test_unit test_unit, time_t query_timestamp,
48 check_snmp_state_entry prev_state,
49 bool have_previous_state);
50
51#define NP_STATE_FORMAT_VERSION 1
52
53typedef struct state_data_struct {
54 time_t time;
55 void *data;
56 size_t length; /* Of binary data */
57 int errorcode;
58} state_data;
59
60typedef struct state_key_struct {
61 char *name;
62 char *plugin_name;
63 int data_version;
64 char *_filename;
65 state_data *state_data;
66} state_key;
67
68state_data *np_state_read(state_key stateKey);
69state_key np_enable_state(char *keyname, int expected_data_version, const char *plugin_name,
70 int argc, char **argv);
71void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore);
diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h
new file mode 100644
index 00000000..e7b6d1b3
--- /dev/null
+++ b/plugins/check_snmp.d/config.h
@@ -0,0 +1,81 @@
1#pragma once
2
3#include "../../lib/thresholds.h"
4#include "../../lib/states.h"
5#include <stdlib.h>
6#include <stdbool.h>
7#include <regex.h>
8#include "../common.h"
9
10// defines for snmp libs
11#define u_char unsigned char
12#define u_long unsigned long
13#define u_short unsigned short
14#define u_int unsigned int
15
16#include <net-snmp/net-snmp-config.h>
17#include <net-snmp/net-snmp-includes.h>
18#include <net-snmp/library/snmp.h>
19#include <net-snmp/session_api.h>
20
21#define DEFAULT_PORT "161"
22#define DEFAULT_RETRIES 5
23
24typedef struct eval_method {
25 bool crit_string;
26 bool crit_regex;
27} eval_method;
28
29typedef struct check_snmp_test_unit {
30 char *oid;
31 char *label;
32 char *unit_value;
33 eval_method eval_mthd;
34 mp_thresholds threshold;
35} check_snmp_test_unit;
36
37typedef struct {
38 struct snmp_session snmp_session;
39 // use getnet instead of get
40 bool use_getnext;
41
42 // TODO actually make these useful
43 bool ignore_mib_parsing_errors;
44 bool need_mibs;
45
46 check_snmp_test_unit *test_units;
47 size_t num_of_test_units;
48} check_snmp_config_snmp_parameters;
49
50typedef struct {
51 // State if an empty value is encountered
52 mp_state_enum nulloid_result;
53
54 // String evaluation stuff
55 bool invert_search;
56 regex_t regex_cmp_value; // regex to match query results against
57 char string_cmp_value[MAX_INPUT_BUFFER];
58
59 // Modify data
60 double multiplier;
61 bool multiplier_set;
62 double offset;
63 bool offset_set;
64
65 // Modify output
66 bool use_oid_as_perf_data_label;
67
68 // activate rate calculation
69 bool calculate_rate;
70 unsigned int rate_multiplier;
71} check_snmp_evaluation_parameters;
72
73typedef struct check_snmp_config {
74 // SNMP session to use
75 check_snmp_config_snmp_parameters snmp_params;
76
77 check_snmp_evaluation_parameters evaluation_params;
78
79 mp_output_format output_format;
80 bool output_format_is_set;
81} check_snmp_config;
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c
index 34ef37b7..911f6787 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -1,103 +1,119 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ssh plugin 3 * Monitoring check_ssh plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 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_ssh plugin 10 * This file contains the check_ssh plugin
11* 11 *
12* Try to connect to an SSH server at specified server and port 12 * Try to connect to an SSH server at specified server and port
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
31#include "output.h"
32#include "perfdata.h"
33#include "states.h"
31const char *progname = "check_ssh"; 34const char *progname = "check_ssh";
32const char *copyright = "2000-2007"; 35const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 36const char *email = "devel@monitoring-plugins.org";
34 37
35#include "./common.h" 38#include "./common.h"
36#include "./netutils.h" 39#include "./netutils.h"
37#include "utils.h" 40#include "utils.h"
41#include "./check_ssh.d/config.h"
38 42
39#ifndef MSG_DONTWAIT 43#ifndef MSG_DONTWAIT
40#define MSG_DONTWAIT 0 44# define MSG_DONTWAIT 0
41#endif 45#endif
42 46
43#define SSH_DFL_PORT 22 47#define BUFF_SZ 256
44#define BUFF_SZ 256
45 48
46int port = -1; 49static bool verbose = false;
47char *server_name = NULL;
48char *remote_version = NULL;
49char *remote_protocol = NULL;
50bool verbose = false;
51 50
52int process_arguments (int, char **); 51typedef struct process_arguments_wrapper {
53int validate_arguments (void); 52 int errorcode;
54void print_help (void); 53 check_ssh_config config;
55void print_usage (void); 54} process_arguments_wrapper;
56 55
57int ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol); 56static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57static void print_help(void);
58void print_usage(void);
58 59
60static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version,
61 char *remote_protocol);
59 62
60int 63int main(int argc, char **argv) {
61main (int argc, char **argv) 64#ifdef __OpenBSD__
62{ 65 /* - rpath is required to read --extra-opts (given up later)
63 int result = STATE_UNKNOWN; 66 * - inet is required for sockets
67 * - unix is required for Unix domain sockets
68 * - dns is required for name lookups */
69 pledge("stdio rpath inet unix dns", NULL);
70#endif // __OpenBSD__
64 71
65 setlocale (LC_ALL, ""); 72 setlocale(LC_ALL, "");
66 bindtextdomain (PACKAGE, LOCALEDIR); 73 bindtextdomain(PACKAGE, LOCALEDIR);
67 textdomain (PACKAGE); 74 textdomain(PACKAGE);
68 75
69 /* Parse extra opts if any */ 76 /* Parse extra opts if any */
70 argv=np_extra_opts (&argc, argv, progname); 77 argv = np_extra_opts(&argc, argv, progname);
71 78
72 if (process_arguments (argc, argv) == ERROR) 79 process_arguments_wrapper tmp_config = process_arguments(argc, argv);
73 usage4 (_("Could not parse arguments"));
74 80
75 /* initialize alarm signal handling */ 81 if (tmp_config.errorcode == ERROR) {
76 signal (SIGALRM, socket_timeout_alarm_handler); 82 usage4(_("Could not parse arguments"));
83 }
84
85#ifdef __OpenBSD__
86 pledge("stdio inet unix dns", NULL);
87#endif // __OpenBSD__
77 88
78 alarm (socket_timeout); 89 check_ssh_config config = tmp_config.config;
90
91 mp_check overall = mp_check_init();
92 if (config.output_format_is_set) {
93 mp_set_format(config.output_format);
94 }
95
96 /* initialize alarm signal handling */
97 signal(SIGALRM, socket_timeout_alarm_handler);
98 alarm(socket_timeout);
79 99
80 /* ssh_connect exits if error is found */ 100 /* ssh_connect exits if error is found */
81 result = ssh_connect (server_name, port, remote_version, remote_protocol); 101 ssh_connect(&overall, config.server_name, config.port, config.remote_version,
102 config.remote_protocol);
82 103
83 alarm (0); 104 alarm(0);
84 105
85 return (result); 106 mp_exit(overall);
86} 107}
87 108
88 109#define output_format_index CHAR_MAX + 1
89 110
90/* process command-line arguments */ 111/* process command-line arguments */
91int 112process_arguments_wrapper process_arguments(int argc, char **argv) {
92process_arguments (int argc, char **argv)
93{
94 int c;
95
96 int option = 0;
97 static struct option longopts[] = { 113 static struct option longopts[] = {
98 {"help", no_argument, 0, 'h'}, 114 {"help", no_argument, 0, 'h'},
99 {"version", no_argument, 0, 'V'}, 115 {"version", no_argument, 0, 'V'},
100 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 116 {"host", required_argument, 0, 'H'}, /* backward compatibility */
101 {"hostname", required_argument, 0, 'H'}, 117 {"hostname", required_argument, 0, 'H'},
102 {"port", required_argument, 0, 'p'}, 118 {"port", required_argument, 0, 'p'},
103 {"use-ipv4", no_argument, 0, '4'}, 119 {"use-ipv4", no_argument, 0, '4'},
@@ -106,180 +122,203 @@ process_arguments (int argc, char **argv)
106 {"verbose", no_argument, 0, 'v'}, 122 {"verbose", no_argument, 0, 'v'},
107 {"remote-version", required_argument, 0, 'r'}, 123 {"remote-version", required_argument, 0, 'r'},
108 {"remote-protocol", required_argument, 0, 'P'}, 124 {"remote-protocol", required_argument, 0, 'P'},
109 {0, 0, 0, 0} 125 {"output-format", required_argument, 0, output_format_index},
126 {0, 0, 0, 0}};
127
128 process_arguments_wrapper result = {
129 .config = check_ssh_config_init(),
130 .errorcode = OK,
110 }; 131 };
111 132
112 if (argc < 2) 133 if (argc < 2) {
113 return ERROR; 134 result.errorcode = ERROR;
135 return result;
136 }
114 137
115 for (c = 1; c < argc; c++) 138 for (int i = 1; i < argc; i++) {
116 if (strcmp ("-to", argv[c]) == 0) 139 if (strcmp("-to", argv[i]) == 0) {
117 strcpy (argv[c], "-t"); 140 strcpy(argv[i], "-t");
141 }
142 }
118 143
119 while (1) { 144 int option_char;
120 c = getopt_long (argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option); 145 while (true) {
146 int option = 0;
147 option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option);
121 148
122 if (c == -1 || c == EOF) 149 if (option_char == -1 || option_char == EOF) {
123 break; 150 break;
151 }
124 152
125 switch (c) { 153 switch (option_char) {
126 case '?': /* help */ 154 case '?': /* help */
127 usage5 (); 155 usage5();
128 case 'V': /* version */ 156 case 'V': /* version */
129 print_revision (progname, NP_VERSION); 157 print_revision(progname, NP_VERSION);
130 exit (STATE_UNKNOWN); 158 exit(STATE_UNKNOWN);
131 case 'h': /* help */ 159 case 'h': /* help */
132 print_help (); 160 print_help();
133 exit (STATE_UNKNOWN); 161 exit(STATE_UNKNOWN);
134 case 'v': /* verbose */ 162 case 'v': /* verbose */
135 verbose = true; 163 verbose = true;
136 break; 164 break;
137 case 't': /* timeout period */ 165 case 't': /* timeout period */
138 if (!is_integer (optarg)) 166 if (!is_intpos(optarg)) {
139 usage2 (_("Timeout interval must be a positive integer"), optarg); 167 usage2(_("Timeout interval must be a positive integer"), optarg);
140 else 168 } else {
141 socket_timeout = atoi (optarg); 169 socket_timeout = (unsigned int)atoi(optarg);
170 }
142 break; 171 break;
143 case '4': 172 case '4':
144 address_family = AF_INET; 173 address_family = AF_INET;
145 break; 174 break;
146 case '6': 175 case '6':
147#ifdef USE_IPV6
148 address_family = AF_INET6; 176 address_family = AF_INET6;
149#else
150 usage4 (_("IPv6 support not available"));
151#endif
152 break; 177 break;
153 case 'r': /* remote version */ 178 case 'r': /* remote version */
154 remote_version = optarg; 179 result.config.remote_version = optarg;
155 break; 180 break;
156 case 'P': /* remote version */ 181 case 'P': /* remote version */
157 remote_protocol = optarg; 182 result.config.remote_protocol = optarg;
158 break; 183 break;
159 case 'H': /* host */ 184 case 'H': /* host */
160 if (!is_host (optarg)) 185 if (!is_host(optarg)) {
161 usage2 (_("Invalid hostname/address"), optarg); 186 usage2(_("Invalid hostname/address"), optarg);
162 server_name = optarg; 187 }
188 result.config.server_name = optarg;
163 break; 189 break;
164 case 'p': /* port */ 190 case 'p': /* port */
165 if (is_intpos (optarg)) { 191 if (is_intpos(optarg)) {
166 port = atoi (optarg); 192 result.config.port = atoi(optarg);
193 } else {
194 usage2(_("Port number must be a positive integer"), optarg);
167 } 195 }
168 else { 196 break;
169 usage2 (_("Port number must be a positive integer"), optarg); 197 case output_format_index: {
198 parsed_output_format parser = mp_parse_output_format(optarg);
199 if (!parser.parsing_success) {
200 // TODO List all available formats here, maybe add anothoer usage function
201 printf("Invalid output format: %s\n", optarg);
202 exit(STATE_UNKNOWN);
170 } 203 }
204
205 result.config.output_format_is_set = true;
206 result.config.output_format = parser.output_format;
207 break;
208 }
171 } 209 }
172 } 210 }
173 211
174 c = optind; 212 option_char = optind;
175 if (server_name == NULL && c < argc) { 213 if (result.config.server_name == NULL && option_char < argc) {
176 if (is_host (argv[c])) { 214 if (is_host(argv[option_char])) {
177 server_name = argv[c++]; 215 result.config.server_name = argv[option_char++];
178 } 216 }
179 } 217 }
180 218
181 if (port == -1 && c < argc) { 219 if (result.config.port == -1 && option_char < argc) {
182 if (is_intpos (argv[c])) { 220 if (is_intpos(argv[option_char])) {
183 port = atoi (argv[c++]); 221 result.config.port = atoi(argv[option_char++]);
184 } 222 } else {
185 else { 223 print_usage();
186 print_usage (); 224 exit(STATE_UNKNOWN);
187 exit (STATE_UNKNOWN);
188 } 225 }
189 } 226 }
190 227
191 return validate_arguments (); 228 if (result.config.server_name == NULL) {
192} 229 result.errorcode = ERROR;
230 return result;
231 }
193 232
194int 233 return result;
195validate_arguments (void)
196{
197 if (server_name == NULL)
198 return ERROR;
199 if (port == -1) /* funky, but allows -p to override stray integer in args */
200 port = SSH_DFL_PORT;
201 return OK;
202} 234}
203 235
204
205/************************************************************************ 236/************************************************************************
206* 237 *
207* Try to connect to SSH server at specified server and port 238 * Try to connect to SSH server at specified server and port
208* 239 *
209*-----------------------------------------------------------------------*/ 240 *-----------------------------------------------------------------------*/
210
211
212int
213ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol)
214{
215 int sd;
216 int result;
217 int len = 0;
218 ssize_t recv_ret = 0;
219 char *version_control_string = NULL;
220 char *buffer = NULL;
221 char *ssh_proto = NULL;
222 char *ssh_server = NULL;
223 static char *rev_no = VERSION;
224 struct timeval tv;
225 double elapsed_time;
226 241
242int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version,
243 char *desired_remote_protocol) {
244 struct timeval tv;
227 gettimeofday(&tv, NULL); 245 gettimeofday(&tv, NULL);
228 246
229 result = my_tcp_connect (haddr, hport, &sd); 247 int socket;
248 int result = my_tcp_connect(haddr, hport, &socket);
230 249
231 if (result != STATE_OK) 250 mp_subcheck connection_sc = mp_subcheck_init();
251 if (result != STATE_OK) {
252 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
253 xasprintf(&connection_sc.output,
254 "Failed to establish TCP connection to Host %s and Port %d", haddr, hport);
255 mp_add_subcheck_to_check(overall, connection_sc);
232 return result; 256 return result;
257 }
233 258
234 char *output = (char *) calloc (BUFF_SZ + 1, sizeof(char)); 259 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char));
235 260 char *buffer = NULL;
236 unsigned int iteration = 0; 261 ssize_t recv_ret = 0;
237 ssize_t byte_offset = 0; 262 char *version_control_string = NULL;
238 263 size_t byte_offset = 0;
239 while ((version_control_string == NULL) && (recv_ret = recv(sd, output+byte_offset, BUFF_SZ - byte_offset, 0) > 0)) { 264 while ((version_control_string == NULL) &&
265 (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset),
266 0) > 0)) {
240 267
241 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/ 268 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/
242 byte_offset = 0; 269 byte_offset = 0;
243 270
244 char *index = NULL; 271 char *index = NULL;
245 while ((index = strchr(output+byte_offset, '\n')) != NULL) { 272 while ((index = strchr(output + byte_offset, '\n')) != NULL) {
246 /*Partition the buffer so that this line is a separate string, 273 /*Partition the buffer so that this line is a separate string,
247 * by replacing the newline with NUL*/ 274 * by replacing the newline with NUL*/
248 output[(index - output)] = '\0'; 275 output[(index - output)] = '\0';
249 len = strlen(output + byte_offset); 276 size_t len = strlen(output + byte_offset);
250 277
251 if ((len >= 4) && (strncmp (output+byte_offset, "SSH-", 4) == 0)) { 278 if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) {
252 /*if the string starts with SSH-, this _should_ be a valid version control string*/ 279 /*if the string starts with SSH-, this _should_ be a valid version control
253 version_control_string = output+byte_offset; 280 * string*/
254 break; 281 version_control_string = output + byte_offset;
282 break;
255 } 283 }
256 284
257 /*the start of the next line (if one exists) will be after the current one (+ NUL)*/ 285 /*the start of the next line (if one exists) will be after the current one (+ NUL)*/
258 byte_offset += (len + 1); 286 byte_offset += (len + 1);
259 } 287 }
260 288
261 if(version_control_string == NULL) { 289 if (version_control_string == NULL) {
262 /* move unconsumed data to beginning of buffer, null rest */ 290 /* move unconsumed data to beginning of buffer */
263 memmove((void *)output, (void *)output+byte_offset+1, BUFF_SZ - len+1); 291 memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset);
264 memset(output+byte_offset+1, 0, BUFF_SZ-byte_offset+1);
265 292
266 /*start reading from end of current line chunk on next recv*/ 293 /*start reading from end of current line chunk on next recv*/
267 byte_offset = strlen(output); 294 byte_offset = strlen(output);
295
296 /* NUL the rest of the buffer */
297 memset(output + byte_offset, 0, BUFF_SZ - byte_offset);
268 } 298 }
269 } else { 299 } else {
270 byte_offset += recv_ret; 300 byte_offset += (size_t)recv_ret;
271 } 301 }
272 } 302 }
273 303
274 if (recv_ret < 0) { 304 if (recv_ret < 0) {
275 printf("SSH CRITICAL - %s", strerror(errno)); 305 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
276 exit(STATE_CRITICAL); 306 xasprintf(&connection_sc.output, "%s - %s", "SSH CRITICAL - ", strerror(errno));
307 mp_add_subcheck_to_check(overall, connection_sc);
308 return OK;
277 } 309 }
278 310
279 if (version_control_string == NULL) { 311 if (version_control_string == NULL) {
280 printf("SSH CRITICAL - No version control string received"); 312 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
281 exit(STATE_CRITICAL); 313 xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - No version control string received");
314 mp_add_subcheck_to_check(overall, connection_sc);
315 return OK;
282 } 316 }
317
318 connection_sc = mp_set_subcheck_state(connection_sc, STATE_OK);
319 xasprintf(&connection_sc.output, "%s", "Initial connection succeeded");
320 mp_add_subcheck_to_check(overall, connection_sc);
321
283 /* 322 /*
284 * "When the connection has been established, both sides MUST send an 323 * "When the connection has been established, both sides MUST send an
285 * identification string. This identification string MUST be 324 * identification string. This identification string MUST be
@@ -287,10 +326,12 @@ ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol
287 * SSH-protoversion-softwareversion SP comments CR LF" 326 * SSH-protoversion-softwareversion SP comments CR LF"
288 * - RFC 4253:4.2 327 * - RFC 4253:4.2
289 */ 328 */
290 strip (version_control_string); 329 strip(version_control_string);
291 if (verbose) 330 if (verbose) {
292 printf ("%s\n", version_control_string); 331 printf("%s\n", version_control_string);
293 ssh_proto = version_control_string + 4; 332 }
333
334 char *ssh_proto = version_control_string + 4;
294 335
295 /* 336 /*
296 * We assume the protoversion is of the form Major.Minor, although 337 * We assume the protoversion is of the form Major.Minor, although
@@ -308,7 +349,8 @@ ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol
308 * "1.x" (e.g., "1.5" or "1.3")." 349 * "1.x" (e.g., "1.5" or "1.3")."
309 * - RFC 4253:5 350 * - RFC 4253:5
310 */ 351 */
311 ssh_server = ssh_proto + strspn (ssh_proto, "0123456789.") + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ 352 char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") +
353 1; /* (+1 for the '-' separating protoversion from softwareversion) */
312 354
313 /* If there's a space in the version string, whatever's after the space is a comment 355 /* If there's a space in the version string, whatever's after the space is a comment
314 * (which is NOT part of the server name/version)*/ 356 * (which is NOT part of the server name/version)*/
@@ -316,88 +358,105 @@ ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol
316 if (tmp) { 358 if (tmp) {
317 ssh_server[tmp - ssh_server] = '\0'; 359 ssh_server[tmp - ssh_server] = '\0';
318 } 360 }
361
362 mp_subcheck protocol_validity_sc = mp_subcheck_init();
319 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { 363 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) {
320 printf(_("SSH CRITICAL - Invalid protocol version control string %s\n"), version_control_string); 364 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL);
321 exit (STATE_CRITICAL); 365 xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s",
366 version_control_string);
367 mp_add_subcheck_to_check(overall, protocol_validity_sc);
368 return OK;
322 } 369 }
323 ssh_proto[strspn (ssh_proto, "0123456789. ")] = 0; 370
324 371 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK);
325 xasprintf (&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no); 372 xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s",
326 send (sd, buffer, strlen (buffer), MSG_DONTWAIT); 373 version_control_string);
327 if (verbose) 374 mp_add_subcheck_to_check(overall, protocol_validity_sc);
328 printf ("%s\n", buffer); 375
329 376 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0;
330 if (remote_version && strcmp(remote_version, ssh_server)) { 377
331 printf 378 static char *rev_no = VERSION;
332 (_("SSH CRITICAL - %s (protocol %s) version mismatch, expected '%s'\n"), 379 xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
333 ssh_server, ssh_proto, remote_version); 380 send(socket, buffer, strlen(buffer), MSG_DONTWAIT);
334 close(sd); 381 if (verbose) {
335 exit (STATE_CRITICAL); 382 printf("%s\n", buffer);
336 } 383 }
337 384
338 if (remote_protocol && strcmp(remote_protocol, ssh_proto)) { 385 if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) {
339 printf 386 mp_subcheck remote_version_sc = mp_subcheck_init();
340 (_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s' | %s\n"), 387 remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL);
341 ssh_server, ssh_proto, remote_protocol, fperfdata("time", elapsed_time, "s", 388 xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"),
342 false, 0, false, 0, true, 0, true, (int)socket_timeout)); 389 ssh_server, ssh_proto, desired_remote_version);
343 close(sd); 390 close(socket);
344 exit (STATE_CRITICAL); 391 mp_add_subcheck_to_check(overall, remote_version_sc);
392 return OK;
345 } 393 }
346 elapsed_time = (double)deltime(tv) / 1.0e6;
347
348 printf
349 (_("SSH OK - %s (protocol %s) | %s\n"),
350 ssh_server, ssh_proto, fperfdata("time", elapsed_time, "s",
351 false, 0, false, 0, true, 0, true, (int)socket_timeout));
352 close(sd);
353 exit (STATE_OK);
354}
355 394
395 double elapsed_time = (double)deltime(tv) / 1.0e6;
396 mp_perfdata time_pd = perfdata_init();
397 time_pd.value = mp_create_pd_value(elapsed_time);
398 time_pd.label = "time";
399 time_pd.max_present = true;
400 time_pd.max = mp_create_pd_value(socket_timeout);
401
402 mp_subcheck protocol_version_sc = mp_subcheck_init();
403 mp_add_perfdata_to_subcheck(&protocol_version_sc, time_pd);
404
405 if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) {
406 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL);
407 xasprintf(&protocol_version_sc.output,
408 _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server,
409 ssh_proto, desired_remote_protocol);
410 } else {
411 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK);
412 xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)",
413 ssh_server, ssh_proto);
414 }
356 415
416 mp_add_subcheck_to_check(overall, protocol_version_sc);
417 close(socket);
418 return OK;
419}
357 420
358void 421void print_help(void) {
359print_help (void)
360{
361 char *myport; 422 char *myport;
362 xasprintf (&myport, "%d", SSH_DFL_PORT); 423 xasprintf(&myport, "%d", default_ssh_port);
363 424
364 print_revision (progname, NP_VERSION); 425 print_revision(progname, NP_VERSION);
365 426
366 printf ("Copyright (c) 1999 Remi Paulmier <remi@sinfomic.fr>\n"); 427 printf("Copyright (c) 1999 Remi Paulmier <remi@sinfomic.fr>\n");
367 printf (COPYRIGHT, copyright, email); 428 printf(COPYRIGHT, copyright, email);
368 429
369 printf ("%s\n", _("Try to connect to an SSH server at specified server and port")); 430 printf("%s\n", _("Try to connect to an SSH server at specified server and port"));
370 431
371 printf ("\n\n"); 432 printf("\n\n");
372 433
373 print_usage (); 434 print_usage();
374 435
375 printf (UT_HELP_VRSN); 436 printf(UT_HELP_VRSN);
376 printf (UT_EXTRA_OPTS); 437 printf(UT_EXTRA_OPTS);
377 438
378 printf (UT_HOST_PORT, 'p', myport); 439 printf(UT_HOST_PORT, 'p', myport);
379 440
380 printf (UT_IPv46); 441 printf(UT_IPv46);
381 442
382 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 443 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
383 444
384 printf (" %s\n", "-r, --remote-version=STRING"); 445 printf(" %s\n", "-r, --remote-version=STRING");
385 printf (" %s\n", _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); 446 printf(" %s\n",
447 _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)"));
386 448
387 printf (" %s\n", "-P, --remote-protocol=STRING"); 449 printf(" %s\n", "-P, --remote-protocol=STRING");
388 printf (" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); 450 printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)"));
451 printf(UT_OUTPUT_FORMAT);
389 452
390 printf (UT_VERBOSE); 453 printf(UT_VERBOSE);
391 454
392 printf (UT_SUPPORT); 455 printf(UT_SUPPORT);
393} 456}
394 457
395 458void print_usage(void) {
396 459 printf("%s\n", _("Usage:"));
397void 460 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n",
398print_usage (void) 461 progname);
399{
400 printf ("%s\n", _("Usage:"));
401 printf ("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] <host>\n", progname);
402} 462}
403
diff --git a/plugins/check_ssh.d/config.h b/plugins/check_ssh.d/config.h
new file mode 100644
index 00000000..c150fd30
--- /dev/null
+++ b/plugins/check_ssh.d/config.h
@@ -0,0 +1,29 @@
1#pragma once
2
3#include <stddef.h>
4#include "../../lib/monitoringplug.h"
5
6const int default_ssh_port = 22;
7
8typedef struct check_ssh_config {
9 int port;
10 char *server_name;
11 char *remote_version;
12 char *remote_protocol;
13
14 bool output_format_is_set;
15 mp_output_format output_format;
16} check_ssh_config;
17
18check_ssh_config check_ssh_config_init(void) {
19 check_ssh_config tmp = {
20 .port = default_ssh_port,
21 .server_name = NULL,
22 .remote_version = NULL,
23 .remote_protocol = NULL,
24
25 .output_format_is_set = false,
26 };
27
28 return tmp;
29}
diff --git a/plugins/check_swap.c b/plugins/check_swap.c
index e7ee785d..dbf53a00 100644
--- a/plugins/check_swap.c
+++ b/plugins/check_swap.c
@@ -1,607 +1,415 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_swap plugin 3 * Monitoring check_swap plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net) 6 * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net)
7* Copyright (c) 2000-2024 Monitoring Plugins Development Team 7 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_swap plugin 11 * This file contains the check_swap plugin
12* 12 *
13* 13 *
14* This program is free software: you can redistribute it and/or modify 14 * This program is free software: you can redistribute it and/or modify
15* it under the terms of the GNU General Public License as published by 15 * it under the terms of the GNU General Public License as published by
16* the Free Software Foundation, either version 3 of the License, or 16 * the Free Software Foundation, either version 3 of the License, or
17* (at your option) any later version. 17 * (at your option) any later version.
18* 18 *
19* This program is distributed in the hope that it will be useful, 19 * This program is distributed in the hope that it will be useful,
20* but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22* GNU General Public License for more details. 22 * GNU General Public License for more details.
23* 23 *
24* You should have received a copy of the GNU General Public License 24 * You should have received a copy of the GNU General Public License
25* along with this program. If not, see <http://www.gnu.org/licenses/>. 25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26* 26 *
27* 27 *
28*****************************************************************************/ 28 *****************************************************************************/
29
30const char *progname = "check_swap";
31const char *copyright = "2000-2024";
32const char *email = "devel@monitoring-plugins.org";
33 29
34#include "common.h" 30#include "common.h"
35#include "popen.h" 31#include "output.h"
36#include "utils.h" 32#include "states.h"
37 33#include <limits.h>
38#ifdef HAVE_DECL_SWAPCTL 34#ifdef HAVE_DECL_SWAPCTL
39# ifdef HAVE_SYS_PARAM_H 35# ifdef HAVE_SYS_PARAM_H
40# include <sys/param.h> 36# include <sys/param.h>
41# endif 37# endif
42# ifdef HAVE_SYS_SWAP_H 38# ifdef HAVE_SYS_SWAP_H
43# include <sys/swap.h> 39# include <sys/swap.h>
44# endif 40# endif
45# ifdef HAVE_SYS_STAT_H 41# ifdef HAVE_SYS_STAT_H
46# include <sys/stat.h> 42# include <sys/stat.h>
47# endif 43# endif
48#endif 44#endif
49 45
50#ifndef SWAP_CONVERSION 46#include <stdint.h>
51# define SWAP_CONVERSION 1 47#include "./check_swap.d/check_swap.h"
52#endif 48#include "./utils.h"
53 49
54typedef struct { 50typedef struct {
55 bool is_percentage; 51 int errorcode;
56 uint64_t value; 52 swap_config config;
57} threshold; 53} swap_config_wrapper;
58 54
59int check_swap (float free_swap_mb, float total_swap_mb); 55static swap_config_wrapper process_arguments(int argc, char **argv);
60int process_arguments (int argc, char **argv); 56void print_usage(void);
61int validate_arguments (void); 57static void print_help(swap_config /*config*/);
62void print_usage (void); 58
63void print_help (void);
64
65threshold warn;
66threshold crit;
67int verbose; 59int verbose;
68bool allswaps = false;
69int no_swap_state = STATE_CRITICAL;
70
71int
72main (int argc, char **argv)
73{
74 unsigned int percent_used, percent;
75 uint64_t total_swap_mb = 0, used_swap_mb = 0, free_swap_mb = 0;
76 uint64_t dsktotal_mb = 0, dskused_mb = 0, dskfree_mb = 0;
77 uint64_t tmp_KB = 0;
78 int result = STATE_UNKNOWN;
79 char input_buffer[MAX_INPUT_BUFFER];
80#ifdef HAVE_PROC_MEMINFO
81 FILE *fp;
82#else
83 int conv_factor = SWAP_CONVERSION;
84# ifdef HAVE_SWAP
85 char *temp_buffer;
86 char *swap_command;
87 char *swap_format;
88# else
89# ifdef HAVE_DECL_SWAPCTL
90 int i=0, nswaps=0, swapctl_res=0;
91# ifdef CHECK_SWAP_SWAPCTL_SVR4
92 swaptbl_t *tbl=NULL;
93 swapent_t *ent=NULL;
94# else
95# ifdef CHECK_SWAP_SWAPCTL_BSD
96 struct swapent *ent;
97# endif /* CHECK_SWAP_SWAPCTL_BSD */
98# endif /* CHECK_SWAP_SWAPCTL_SVR4 */
99# endif /* HAVE_DECL_SWAPCTL */
100# endif
101#endif
102 char str[32];
103 char *status;
104 60
105 setlocale (LC_ALL, ""); 61#define HUNDRED_PERCENT 100
106 bindtextdomain (PACKAGE, LOCALEDIR); 62
107 textdomain (PACKAGE); 63#define BYTES_TO_KiB(number) (number / 1024)
64#define BYTES_TO_MiB(number) (BYTES_TO_KiB(number) / 1024)
65
66const char *progname = "check_swap";
67const char *copyright = "2000-2024";
68const char *email = "devel@monitoring-plugins.org";
108 69
109 status = strdup (""); 70int main(int argc, char **argv) {
71 setlocale(LC_ALL, "");
72 bindtextdomain(PACKAGE, LOCALEDIR);
73 textdomain(PACKAGE);
110 74
111 /* Parse extra opts if any */ 75 /* Parse extra opts if any */
112 argv=np_extra_opts (&argc, argv, progname); 76 argv = np_extra_opts(&argc, argv, progname);
113 77
114 if (process_arguments (argc, argv) == ERROR) 78 swap_config_wrapper tmp = process_arguments(argc, argv);
115 usage4 (_("Could not parse arguments"));
116 79
117#ifdef HAVE_PROC_MEMINFO 80 if (tmp.errorcode != OK) {
118 if (verbose >= 3) { 81 usage4(_("Could not parse arguments"));
119 printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO);
120 } 82 }
121 fp = fopen (PROC_MEMINFO, "r");
122 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) {
123 /*
124 * The following sscanf call looks for a line looking like: "Swap: 123 123 123"
125 * On which kind of system this format exists, I can not say, but I wanted to
126 * document this for people who are not adapt with sscanf anymore, like me
127 */
128 if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &dsktotal_mb, &dskused_mb, &dskfree_mb) == 3) {
129 dsktotal_mb = dsktotal_mb / (1024 * 1024); /* Apply conversion */
130 dskused_mb = dskused_mb / (1024 * 1024);
131 dskfree_mb = dskfree_mb / (1024 * 1024);
132 total_swap_mb += dsktotal_mb;
133 used_swap_mb += dskused_mb;
134 free_swap_mb += dskfree_mb;
135 if (allswaps) {
136 if (dsktotal_mb == 0)
137 percent=100.0;
138 else
139 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
140 result = max_state (result, check_swap (dskfree_mb, dsktotal_mb));
141 if (verbose)
142 xasprintf (&status, "%s [%lu (%d%%)]", status, dskfree_mb, 100 - percent);
143 }
144 }
145 83
146 /* 84 swap_config config = tmp.config;
147 * The following sscanf call looks for lines looking like: "SwapTotal: 123" and "SwapFree: 123"
148 * This format exists at least on Debian Linux with a 5.* kernel
149 */
150 else if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu %*[k]%*[B]", str, &tmp_KB)) {
151 if (verbose >= 3) {
152 printf("Got %s with %lu\n", str, tmp_KB);
153 }
154 /* I think this part is always in Kb, so convert to mb */
155 if (strcmp ("Total", str) == 0) {
156 dsktotal_mb = tmp_KB / 1024;
157 }
158 else if (strcmp ("Free", str) == 0) {
159 dskfree_mb = dskfree_mb + tmp_KB / 1024;
160 }
161 else if (strcmp ("Cached", str) == 0) {
162 dskfree_mb = dskfree_mb + tmp_KB / 1024;
163 }
164 }
165 }
166 fclose(fp);
167 dskused_mb = dsktotal_mb - dskfree_mb;
168 total_swap_mb = dsktotal_mb;
169 used_swap_mb = dskused_mb;
170 free_swap_mb = dskfree_mb;
171#else
172# ifdef HAVE_SWAP
173 xasprintf(&swap_command, "%s", SWAP_COMMAND);
174 xasprintf(&swap_format, "%s", SWAP_FORMAT);
175
176/* These override the command used if a summary (and thus ! allswaps) is required */
177/* The summary flag returns more accurate information about swap usage on these OSes */
178# ifdef _AIX
179 if (!allswaps) {
180 xasprintf(&swap_command, "%s", "/usr/sbin/lsps -s");
181 xasprintf(&swap_format, "%s", "%lu%*s %lu");
182 conv_factor = 1;
183 }
184# endif
185 85
186 if (verbose >= 2) 86 swap_result data = get_swap_data(config);
187 printf (_("Command: %s\n"), swap_command);
188 if (verbose >= 3)
189 printf (_("Format: %s\n"), swap_format);
190 87
191 child_process = spopen (swap_command); 88 if (data.errorcode != STATE_OK) {
192 if (child_process == NULL) { 89 puts("SWAP UNKNOWN - Failed to retrieve Swap usage");
193 printf (_("Could not open pipe: %s\n"), swap_command); 90 exit(STATE_UNKNOWN);
194 return STATE_UNKNOWN;
195 } 91 }
196 92
197 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 93 if (verbose) {
198 if (child_stderr == NULL) 94 printf("Swap retrieval result:\n"
199 printf (_("Could not open stderr for %s\n"), swap_command); 95 "\tFree: %llu\n"
200 96 "\tUsed: %llu\n"
201 sprintf (str, "%s", ""); 97 "\tTotal: %llu\n",
202 /* read 1st line */ 98 data.metrics.free, data.metrics.used, data.metrics.total);
203 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
204 if (strcmp (swap_format, "") == 0) {
205 temp_buffer = strtok (input_buffer, " \n");
206 while (temp_buffer) {
207 if (strstr (temp_buffer, "blocks"))
208 sprintf (str, "%s %s", str, "%lu");
209 else if (strstr (temp_buffer, "dskfree"))
210 sprintf (str, "%s %s", str, "%lu");
211 else
212 sprintf (str, "%s %s", str, "%*s");
213 temp_buffer = strtok (NULL, " \n");
214 }
215 } 99 }
216 100
217/* If different swap command is used for summary switch, need to read format differently */ 101 double percent_used;
218# ifdef _AIX 102 mp_check overall = mp_check_init();
219 if (!allswaps) { 103 if (config.output_format_is_set) {
220 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */ 104 mp_set_format(config.output_format);
221 sscanf (input_buffer, swap_format, &total_swap_mb, &used_swap_mb);
222 free_swap_mb = total_swap_mb * (100 - used_swap_mb) /100;
223 used_swap_mb = total_swap_mb - free_swap_mb;
224 if (verbose >= 3)
225 printf (_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb);
226 } else {
227# endif
228 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
229 sscanf (input_buffer, swap_format, &dsktotal_mb, &dskfree_mb);
230
231 dsktotal_mb = dsktotal_mb / conv_factor;
232 /* AIX lists percent used, so this converts to dskfree in MBs */
233# ifdef _AIX
234 dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100;
235# else
236 dskfree_mb = dskfree_mb / conv_factor;
237# endif
238 if (verbose >= 3)
239 printf (_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb);
240
241 dskused_mb = dsktotal_mb - dskfree_mb;
242 total_swap_mb += dsktotal_mb;
243 used_swap_mb += dskused_mb;
244 free_swap_mb += dskfree_mb;
245 if (allswaps) {
246 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
247 result = max_state (result, check_swap (dskfree_mb, dsktotal_mb));
248 if (verbose)
249 xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
250 }
251 }
252# ifdef _AIX
253 } 105 }
254# endif 106 mp_subcheck sc1 = mp_subcheck_init();
255 107 sc1 = mp_set_subcheck_default_state(sc1, STATE_OK);
256 /* If we get anything on STDERR, at least set warning */
257 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr))
258 result = max_state (result, STATE_WARNING);
259
260 /* close stderr */
261 (void) fclose (child_stderr);
262 108
263 /* close the pipe */ 109 /* if total_swap_mb == 0, let's not divide by 0 */
264 if (spclose (child_process)) 110 if (data.metrics.total != 0) {
265 result = max_state (result, STATE_WARNING); 111 percent_used = HUNDRED_PERCENT * ((double)data.metrics.used) / ((double)data.metrics.total);
266# else 112 } else {
267# ifdef CHECK_SWAP_SWAPCTL_SVR4 113 sc1 = mp_set_subcheck_state(sc1, config.no_swap_state);
114 sc1.output = (char *)_("Swap is either disabled, not present, or of zero size.");
268 115
269 /* get the number of active swap devices */ 116 mp_add_subcheck_to_check(&overall, sc1);
270 if((nswaps=swapctl(SC_GETNSWP, NULL))== -1) 117 mp_exit(overall);
271 die(STATE_UNKNOWN, _("Error getting swap devices\n") ); 118 }
272 119
273 if(nswaps == 0) 120 if (verbose) {
274 die(STATE_OK, _("SWAP OK: No swap devices defined\n")); 121 printf("Computed usage percentage: %g\n", percent_used);
122 }
275 123
276 if(verbose >= 3) 124 mp_perfdata pd = perfdata_init();
277 printf("Found %d swap device(s)\n", nswaps); 125 pd.label = "swap";
126 pd = mp_set_pd_value(pd, data.metrics.free);
127 pd.uom = "B";
278 128
279 /* initialize swap table + entries */ 129 if (config.warn_is_set) {
280 tbl=(swaptbl_t*)malloc(sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps)); 130 uint64_t warn_print = config.warn.value;
131 if (config.warn.is_percentage) {
132 warn_print = config.warn.value * (data.metrics.total / HUNDRED_PERCENT);
133 }
281 134
282 if(tbl==NULL) 135 mp_perfdata_value warn_pd = mp_create_pd_value(warn_print);
283 die(STATE_UNKNOWN, _("malloc() failed!\n"));
284 136
285 memset(tbl, 0, sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps)); 137 mp_range warn_range = mp_range_init();
286 tbl->swt_n=nswaps; 138 warn_range.end_infinity = false;
287 for(i=0;i<nswaps;i++){ 139 warn_range.end = warn_pd;
288 if((tbl->swt_ent[i].ste_path=(char*)malloc(sizeof(char)*MAXPATHLEN)) == NULL)
289 die(STATE_UNKNOWN, _("malloc() failed!\n"));
290 }
291 140
292 /* and now, tally 'em up */ 141 pd.warn = warn_range;
293 swapctl_res=swapctl(SC_LIST, tbl); 142 pd.warn_present = true;
294 if(swapctl_res < 0){
295 perror(_("swapctl failed: "));
296 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
297 } 143 }
298 144
299 for(i=0;i<nswaps;i++){ 145 if (config.crit_is_set) {
300 dsktotal_mb = (float) tbl->swt_ent[i].ste_pages / SWAP_CONVERSION; 146 uint64_t crit_print = config.crit.value;
301 dskfree_mb = (float) tbl->swt_ent[i].ste_free / SWAP_CONVERSION; 147 if (config.crit.is_percentage) {
302 dskused_mb = ( dsktotal_mb - dskfree_mb ); 148 crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT);
303
304 if (verbose >= 3)
305 printf ("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb);
306
307 if(allswaps && dsktotal_mb > 0){
308 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
309 result = max_state (result, check_swap (dskfree_mb, dsktotal_mb));
310 if (verbose) {
311 xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
312 }
313 } 149 }
314 150
315 total_swap_mb += dsktotal_mb; 151 mp_perfdata_value crit_pd = mp_create_pd_value(crit_print);
316 free_swap_mb += dskfree_mb;
317 used_swap_mb += dskused_mb;
318 }
319 152
320 /* and clean up after ourselves */ 153 mp_range crit_range = mp_range_init();
321 for(i=0;i<nswaps;i++){ 154 crit_range.end_infinity = false;
322 free(tbl->swt_ent[i].ste_path); 155 crit_range.end = crit_pd;
156
157 pd.crit = crit_range;
158 pd.crit_present = true;
323 } 159 }
324 free(tbl);
325# else
326# ifdef CHECK_SWAP_SWAPCTL_BSD
327 160
328 /* get the number of active swap devices */ 161 mp_perfdata_value max = mp_create_pd_value(data.metrics.total);
329 nswaps=swapctl(SWAP_NSWAP, NULL, 0); 162 pd.max = max;
163 pd.max_present = true;
330 164
331 /* initialize swap table + entries */ 165 mp_perfdata_value min = mp_create_pd_value(0);
332 ent=(struct swapent*)malloc(sizeof(struct swapent)*nswaps); 166 pd.min = min;
167 pd.min_present = true;
333 168
334 /* and now, tally 'em up */ 169 mp_add_perfdata_to_subcheck(&sc1, pd);
335 swapctl_res=swapctl(SWAP_STATS, ent, nswaps); 170 if (verbose > 1) {
336 if(swapctl_res < 0){ 171 printf("Warn threshold value: %" PRIu64 "\n", config.warn.value);
337 perror(_("swapctl failed: "));
338 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
339 } 172 }
340 173
341 for(i=0;i<nswaps;i++){ 174 if (config.warn_is_set) {
342 dsktotal_mb = (float) ent[i].se_nblks / conv_factor; 175 if ((config.warn.is_percentage && (percent_used >= (100 - (double)config.warn.value))) ||
343 dskused_mb = (float) ent[i].se_inuse / conv_factor; 176 config.warn.value >= data.metrics.free) {
344 dskfree_mb = ( dsktotal_mb - dskused_mb ); 177 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
345
346 if(allswaps && dsktotal_mb > 0){
347 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
348 result = max_state (result, check_swap(dskfree_mb, dsktotal_mb));
349 if (verbose) {
350 xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
351 }
352 } 178 }
353
354 total_swap_mb += dsktotal_mb;
355 free_swap_mb += dskfree_mb;
356 used_swap_mb += dskused_mb;
357 } 179 }
358 180
359 /* and clean up after ourselves */ 181 if (verbose > 1) {
360 free(ent); 182 printf("Crit threshold value: %" PRIu64 "\n", config.crit.value);
361
362# endif /* CHECK_SWAP_SWAPCTL_BSD */
363# endif /* CHECK_SWAP_SWAPCTL_SVR4 */
364# endif /* HAVE_SWAP */
365#endif /* HAVE_PROC_MEMINFO */
366
367 /* if total_swap_mb == 0, let's not divide by 0 */
368 if(total_swap_mb) {
369 percent_used = 100 * ((double) used_swap_mb) / ((double) total_swap_mb);
370 } else {
371 percent_used = 100;
372 status = "- Swap is either disabled, not present, or of zero size. ";
373 } 183 }
374 184
375 result = max_state (result, check_swap(free_swap_mb, total_swap_mb)); 185 if (config.crit_is_set) {
376 printf (_("SWAP %s - %d%% free (%dMB out of %dMB) %s|"), 186 if ((config.crit.is_percentage && (percent_used >= (100 - (double)config.crit.value))) ||
377 state_text (result), 187 config.crit.value >= data.metrics.free) {
378 (100 - percent_used), (int) free_swap_mb, (int) total_swap_mb, status); 188 sc1 = mp_set_subcheck_state(sc1, STATE_CRITICAL);
189 }
190 }
379 191
380 uint64_t warn_print = warn.value; 192 xasprintf(&sc1.output, _("%g%% free (%lluMiB out of %lluMiB)"), (100 - percent_used),
381 if (warn.is_percentage) warn_print = warn.value * (total_swap_mb *1024 *1024/100); 193 data.metrics.free >> 20, data.metrics.total >> 20);
382 uint64_t crit_print = crit.value;
383 if (crit.is_percentage) crit_print = crit.value * (total_swap_mb *1024 *1024/100);
384 194
385 puts (perfdata_uint64 ("swap", free_swap_mb *1024 *1024, "B", 195 overall.summary = "Swap";
386 true, warn_print, 196 mp_add_subcheck_to_check(&overall, sc1);
387 true, crit_print,
388 true, 0,
389 true, (long) total_swap_mb * 1024 * 1024));
390 197
391 return result; 198 mp_exit(overall);
392} 199}
393 200
201int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
202 if (total_swap_mb == 0) {
203 return config.no_swap_state;
204 }
394 205
395int 206 uint64_t free_swap =
396check_swap(float free_swap_mb, float total_swap_mb) 207 (uint64_t)(free_swap_mb *
397{ 208 (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */
398
399 if (!total_swap_mb) return no_swap_state;
400 209
401 uint64_t free_swap = free_swap_mb * (1024 * 1024); /* Convert back to bytes as warn and crit specified in bytes */ 210 if (!config.crit.is_percentage && config.crit.value >= free_swap) {
402 uint64_t usage_percentage = ((total_swap_mb - free_swap_mb) / total_swap_mb) * 100; 211 return STATE_CRITICAL;
212 }
213 if (!config.warn.is_percentage && config.warn.value >= free_swap) {
214 return STATE_WARNING;
215 }
403 216
404 if (warn.value || crit.value) { /* Thresholds defined */ 217 uint64_t usage_percentage =
405 if (!crit.is_percentage && crit.value >= free_swap) return STATE_CRITICAL; 218 (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT;
406 if (!warn.is_percentage && warn.value >= free_swap) return STATE_WARNING;
407 219
408 if (crit.is_percentage && 220 if (config.crit.is_percentage && config.crit.value != 0 &&
409 crit.value != 0 && 221 usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) {
410 usage_percentage >= (100 - crit.value)) 222 return STATE_CRITICAL;
411 { 223 }
412 return STATE_CRITICAL;
413 }
414 224
415 if (warn.is_percentage && 225 if (config.warn.is_percentage && config.warn.value != 0 &&
416 warn.value != 0 && 226 usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) {
417 usage_percentage >= (100 - warn.value)) 227 return STATE_WARNING;
418 { 228 }
419 return STATE_WARNING;
420 }
421 229
422 return STATE_OK; 230 return STATE_OK;
423 } else { /* Without thresholds */
424 return STATE_OK;
425 }
426} 231}
427 232
428 233#define output_format_index CHAR_MAX + 1
429 234
430/* process command-line arguments */ 235/* process command-line arguments */
431int 236swap_config_wrapper process_arguments(int argc, char **argv) {
432process_arguments (int argc, char **argv) 237 swap_config_wrapper conf_wrapper = {.errorcode = OK};
433{ 238 conf_wrapper.config = swap_config_init();
434 int c = 0; /* option character */ 239
435 240 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
436 int option = 0; 241 {"critical", required_argument, 0, 'c'},
437 static struct option longopts[] = { 242 {"allswaps", no_argument, 0, 'a'},
438 {"warning", required_argument, 0, 'w'}, 243 {"no-swap", required_argument, 0, 'n'},
439 {"critical", required_argument, 0, 'c'}, 244 {"verbose", no_argument, 0, 'v'},
440 {"allswaps", no_argument, 0, 'a'}, 245 {"version", no_argument, 0, 'V'},
441 {"no-swap", required_argument, 0, 'n'}, 246 {"help", no_argument, 0, 'h'},
442 {"verbose", no_argument, 0, 'v'}, 247 {"output-format", required_argument, 0, output_format_index},
443 {"version", no_argument, 0, 'V'}, 248 {0, 0, 0, 0}};
444 {"help", no_argument, 0, 'h'}, 249
445 {0, 0, 0, 0} 250 while (true) {
446 }; 251 int option = 0;
447 252 int option_char = getopt_long(argc, argv, "+?Vvhac:w:n:", longopts, &option);
448 while (1) { 253
449 c = getopt_long (argc, argv, "+?Vvhac:w:n:", longopts, &option); 254 if (option_char == -1 || option_char == EOF) {
450
451 if (c == -1 || c == EOF)
452 break; 255 break;
256 }
453 257
454 switch (c) { 258 switch (option_char) {
455 case 'w': /* warning size threshold */ 259 case 'w': /* warning size threshold */
456 { 260 {
457 /* 261 /*
458 * We expect either a positive integer value without a unit, which means 262 * We expect either a positive integer value without a unit, which
459 * the unit is Bytes or a positive integer value and a percentage sign (%), 263 * means the unit is Bytes or a positive integer value and a
460 * which means the value must be with 0 and 100 and is relative to the total swap 264 * percentage sign (%), which means the value must be with 0 and 100
461 */ 265 * and is relative to the total swap
462 size_t length; 266 */
463 length = strlen(optarg); 267 size_t length;
464 268 length = strlen(optarg);
465 if (optarg[length - 1] == '%') { 269 conf_wrapper.config.warn_is_set = true;
466 /* It's percentage */ 270
467 warn.is_percentage = true; 271 if (optarg[length - 1] == '%') {
468 optarg[length - 1] = '\0'; 272 /* It's percentage */
469 if (is_uint64(optarg, &warn.value)) { 273 conf_wrapper.config.warn.is_percentage = true;
470 if (warn.value > 100) { 274 optarg[length - 1] = '\0';
471 usage4 (_("Warning threshold percentage must be <= 100!")); 275 if (is_uint64(optarg, &conf_wrapper.config.warn.value)) {
472 } 276 if (conf_wrapper.config.warn.value > HUNDRED_PERCENT) {
473 } 277 usage4(_("Warning threshold percentage must be <= 100!"));
474 break;
475 } else {
476 /* It's Bytes */
477 warn.is_percentage = false;
478 if (is_uint64(optarg, &warn.value)) {
479 break;
480 } else {
481 usage4 (_("Warning threshold be positive integer or percentage!"));
482 } 278 }
483 } 279 }
280 break;
281 } /* It's Bytes */
282 conf_wrapper.config.warn.is_percentage = false;
283 if (is_uint64(optarg, &conf_wrapper.config.warn.value)) {
284 break;
484 } 285 }
286 usage4(_("Warning threshold be positive integer or "
287 "percentage!"));
288 }
485 case 'c': /* critical size threshold */ 289 case 'c': /* critical size threshold */
486 { 290 {
487 /* 291 /*
488 * We expect either a positive integer value without a unit, which means 292 * We expect either a positive integer value without a unit, which
489 * the unit is Bytes or a positive integer value and a percentage sign (%), 293 * means the unit is Bytes or a positive integer value and a
490 * which means the value must be with 0 and 100 and is relative to the total swap 294 * percentage sign (%), which means the value must be with 0 and 100
491 */ 295 * and is relative to the total swap
492 size_t length; 296 */
493 length = strlen(optarg); 297 size_t length;
494 298 length = strlen(optarg);
495 if (optarg[length - 1] == '%') { 299 conf_wrapper.config.crit_is_set = true;
496 /* It's percentage */ 300
497 crit.is_percentage = true; 301 if (optarg[length - 1] == '%') {
498 optarg[length - 1] = '\0'; 302 /* It's percentage */
499 if (is_uint64(optarg, &crit.value)) { 303 conf_wrapper.config.crit.is_percentage = true;
500 if (crit.value> 100) { 304 optarg[length - 1] = '\0';
501 usage4 (_("Critical threshold percentage must be <= 100!")); 305 if (is_uint64(optarg, &conf_wrapper.config.crit.value)) {
502 } 306 if (conf_wrapper.config.crit.value > HUNDRED_PERCENT) {
503 } 307 usage4(_("Critical threshold percentage must be <= 100!"));
504 break;
505 } else {
506 /* It's Bytes */
507 crit.is_percentage = false;
508 if (is_uint64(optarg, &crit.value)) {
509 break;
510 } else {
511 usage4 (_("Critical threshold be positive integer or percentage!"));
512 } 308 }
513 } 309 }
514 } 310 break;
515 case 'a': /* all swap */ 311 } /* It's Bytes */
516 allswaps = true; 312 conf_wrapper.config.crit.is_percentage = false;
313 if (is_uint64(optarg, &conf_wrapper.config.crit.value)) {
314 break;
315 }
316 usage4(_("Critical threshold be positive integer or "
317 "percentage!"));
318 }
319 case 'a': /* all swap */
320 conf_wrapper.config.allswaps = true;
517 break; 321 break;
518 case 'n': 322 case 'n':
519 if ((no_swap_state = mp_translate_state(optarg)) == ERROR) { 323 if ((conf_wrapper.config.no_swap_state = mp_translate_state(optarg)) == ERROR) {
520 usage4 (_("no-swap result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 324 usage4(_("no-swap result must be a valid state name (OK, "
325 "WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
521 } 326 }
522 break; 327 break;
523 case 'v': /* verbose */ 328 case 'v': /* verbose */
524 verbose++; 329 verbose++;
525 break; 330 break;
526 case 'V': /* version */ 331 case output_format_index: {
527 print_revision (progname, NP_VERSION); 332 parsed_output_format parser = mp_parse_output_format(optarg);
528 exit (STATE_UNKNOWN); 333 if (!parser.parsing_success) {
529 case 'h': /* help */ 334 // TODO List all available formats here, maybe add anothoer usage function
530 print_help (); 335 printf("Invalid output format: %s\n", optarg);
531 exit (STATE_UNKNOWN); 336 exit(STATE_UNKNOWN);
532 case '?': /* error */ 337 }
533 usage5 (); 338
339 conf_wrapper.config.output_format_is_set = true;
340 conf_wrapper.config.output_format = parser.output_format;
341 break;
342 }
343 case 'V': /* version */
344 print_revision(progname, NP_VERSION);
345 exit(STATE_UNKNOWN);
346 case 'h': /* help */
347 print_help(conf_wrapper.config);
348 exit(STATE_UNKNOWN);
349 case '?': /* error */
350 usage5();
534 } 351 }
535 } 352 }
536 353
537 c = optind; 354 if ((conf_wrapper.config.warn.is_percentage == conf_wrapper.config.crit.is_percentage) &&
538 if (c == argc) 355 (conf_wrapper.config.warn.value < conf_wrapper.config.crit.value)) {
539 return validate_arguments (); 356 /* This is NOT triggered if warn and crit are different units, e.g warn
540 357 * is percentage and crit is absolute. We cannot determine the condition
541 return validate_arguments (); 358 * at this point since we dont know the value of total swap yet
542}
543
544
545
546int
547validate_arguments (void)
548{
549 if ((warn.is_percentage == crit.is_percentage) && (warn.value < crit.value)) {
550 /* This is NOT triggered if warn and crit are different units, e.g warn is percentage
551 * and crit is absolute. We cannot determine the condition at this point since we
552 * dont know the value of total swap yet
553 */ 359 */
554 usage4(_("Warning should be more than critical")); 360 usage4(_("Warning should be more than critical"));
555 } 361 }
556 return OK;
557}
558
559 362
560 363 return conf_wrapper;
561void
562print_help (void)
563{
564 print_revision (progname, NP_VERSION);
565
566 printf (_(COPYRIGHT), copyright, email);
567
568 printf ("%s\n", _("Check swap space on local machine."));
569
570 printf ("\n\n");
571
572 print_usage ();
573
574 printf (UT_HELP_VRSN);
575 printf (UT_EXTRA_OPTS);
576
577 printf (" %s\n", "-w, --warning=INTEGER");
578 printf (" %s\n", _("Exit with WARNING status if less than INTEGER bytes of swap space are free"));
579 printf (" %s\n", "-w, --warning=PERCENT%");
580 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of swap space is free"));
581 printf (" %s\n", "-c, --critical=INTEGER");
582 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER bytes of swap space are free"));
583 printf (" %s\n", "-c, --critical=PERCENT%");
584 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of swap space is free"));
585 printf (" %s\n", "-a, --allswaps");
586 printf (" %s\n", _("Conduct comparisons for all swap partitions, one by one"));
587 printf (" %s\n", "-n, --no-swap=<ok|warning|critical|unknown>");
588 printf (" %s %s\n", _("Resulting state when there is no swap regardless of thresholds. Default:"), state_text(no_swap_state));
589 printf (UT_VERBOSE);
590
591 printf ("\n");
592 printf ("%s\n", _("Notes:"));
593 printf (" %s\n", _("Both INTEGER and PERCENT thresholds can be specified, they are all checked."));
594 printf (" %s\n", _("Without thresholds, the plugin shows free swap space and performance data, but always returns OK."));
595 printf (" %s\n", _("On AIX, if -a is specified, uses lsps -a, otherwise uses lsps -s."));
596
597 printf (UT_SUPPORT);
598} 364}
599 365
366void print_help(swap_config config) {
367 print_revision(progname, NP_VERSION);
368
369 printf(_(COPYRIGHT), copyright, email);
370
371 printf("%s\n", _("Check swap space on local machine."));
372
373 printf("\n\n");
374
375 print_usage();
376
377 printf(UT_HELP_VRSN);
378 printf(UT_EXTRA_OPTS);
379
380 printf(" %s\n", "-w, --warning=INTEGER");
381 printf(" %s\n", _("Exit with WARNING status if less than INTEGER bytes "
382 "of swap space are free"));
383 printf(" %s\n", "-w, --warning=PERCENT%");
384 printf(" %s\n", _("Exit with WARNING status if less than PERCENT of "
385 "swap space is free"));
386 printf(" %s\n", "-c, --critical=INTEGER");
387 printf(" %s\n", _("Exit with CRITICAL status if less than INTEGER bytes "
388 "of swap space are free"));
389 printf(" %s\n", "-c, --critical=PERCENT%");
390 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of "
391 "swap space is free"));
392 printf(" %s\n", "-a, --allswaps");
393 printf(" %s\n", _("Conduct comparisons for all swap partitions, one by one"));
394 printf(" %s\n", "-n, --no-swap=<ok|warning|critical|unknown>");
395 printf(" %s %s\n",
396 _("Resulting state when there is no swap regardless of thresholds. "
397 "Default:"),
398 state_text(config.no_swap_state));
399 printf(UT_OUTPUT_FORMAT);
400 printf(UT_VERBOSE);
401
402 printf("\n");
403 printf("%s\n", _("Notes:"));
404 printf(" %s\n", _("Both INTEGER and PERCENT thresholds can be specified, "
405 "they are all checked."));
406 printf(" %s\n", _("On AIX, if -a is specified, uses lsps -a, otherwise uses lsps -s."));
407
408 printf(UT_SUPPORT);
409}
600 410
601void 411void print_usage(void) {
602print_usage (void) 412 printf("%s\n", _("Usage:"));
603{ 413 printf(" %s [-av] -w <percent_free>%% -c <percent_free>%%\n", progname);
604 printf ("%s\n", _("Usage:")); 414 printf(" -w <bytes_free> -c <bytes_free> [-n <state>]\n");
605 printf (" %s [-av] [-w <percent_free>%%] [-c <percent_free>%%]\n",progname);
606 printf (" [-w <bytes_free>] [-c <bytes_free>] [-n <state>]\n");
607} 415}
diff --git a/plugins/check_swap.d/check_swap.h b/plugins/check_swap.d/check_swap.h
new file mode 100644
index 00000000..8d3c7fcf
--- /dev/null
+++ b/plugins/check_swap.d/check_swap.h
@@ -0,0 +1,49 @@
1#pragma once
2
3#include "../common.h"
4#include "../../lib/output.h"
5#include "../../lib/states.h"
6
7#ifndef SWAP_CONVERSION
8# define SWAP_CONVERSION 1
9#endif
10
11typedef struct {
12 bool is_percentage;
13 uint64_t value;
14} check_swap_threshold;
15
16typedef struct {
17 unsigned long long free; // Free swap in Bytes!
18 unsigned long long used; // Used swap in Bytes!
19 unsigned long long total; // Total swap size, you guessed it, in Bytes!
20} swap_metrics;
21
22typedef struct {
23 int errorcode;
24 int statusCode;
25 swap_metrics metrics;
26} swap_result;
27
28typedef struct {
29 bool allswaps;
30 mp_state_enum no_swap_state;
31 bool warn_is_set;
32 check_swap_threshold warn;
33 bool crit_is_set;
34 check_swap_threshold crit;
35 bool on_aix;
36 int conversion_factor;
37
38 bool output_format_is_set;
39 mp_output_format output_format;
40} swap_config;
41
42swap_config swap_config_init(void);
43
44swap_result get_swap_data(swap_config config);
45swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]);
46swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[],
47 const char swap_format[]);
48swap_result getSwapFromSwapctl_BSD(swap_config config);
49swap_result getSwapFromSwap_SRV4(swap_config config);
diff --git a/plugins/check_swap.d/swap.c b/plugins/check_swap.d/swap.c
new file mode 100644
index 00000000..58213a3c
--- /dev/null
+++ b/plugins/check_swap.d/swap.c
@@ -0,0 +1,471 @@
1#include "./check_swap.d/check_swap.h"
2#include "../popen.h"
3#include "../utils.h"
4#include "common.h"
5
6extern int verbose;
7
8swap_config swap_config_init(void) {
9 swap_config tmp = {0};
10 tmp.allswaps = false;
11 tmp.no_swap_state = STATE_CRITICAL;
12 tmp.conversion_factor = SWAP_CONVERSION;
13
14 tmp.warn_is_set = false;
15 tmp.crit_is_set = false;
16
17 tmp.output_format_is_set = false;
18
19#ifdef _AIX
20 tmp.on_aix = true;
21#else
22 tmp.on_aix = false;
23#endif
24
25 return tmp;
26}
27
28swap_result get_swap_data(swap_config config) {
29#ifdef HAVE_PROC_MEMINFO
30 if (verbose >= 3) {
31 printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO);
32 }
33
34 return getSwapFromProcMeminfo(PROC_MEMINFO);
35#else // HAVE_PROC_MEMINFO
36# ifdef HAVE_SWAP
37 if (verbose >= 3) {
38 printf("Using swap command %s with format: %s\n", SWAP_COMMAND, SWAP_FORMAT);
39 }
40
41 /* These override the command used if a summary (and thus ! allswaps) is
42 * required
43 * The summary flag returns more accurate information about swap usage on these
44 * OSes */
45 if (config.on_aix && !config.allswaps) {
46
47 config.conversion_factor = 1;
48
49 return getSwapFromSwapCommand(config, "/usr/sbin/lsps -s", "%lu%*s %lu");
50 } else {
51 return getSwapFromSwapCommand(config, SWAP_COMMAND, SWAP_FORMAT);
52 }
53# else // HAVE_SWAP
54# ifdef CHECK_SWAP_SWAPCTL_SVR4
55 return getSwapFromSwapctl_SRV4(config);
56# else // CHECK_SWAP_SWAPCTL_SVR4
57# ifdef CHECK_SWAP_SWAPCTL_BSD
58 return getSwapFromSwapctl_BSD(config);
59# else // CHECK_SWAP_SWAPCTL_BSD
60# error No way found to retrieve swap
61# endif /* CHECK_SWAP_SWAPCTL_BSD */
62# endif /* CHECK_SWAP_SWAPCTL_SVR4 */
63# endif /* HAVE_SWAP */
64#endif /* HAVE_PROC_MEMINFO */
65}
66
67swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
68 FILE *meminfo_file_ptr;
69 meminfo_file_ptr = fopen(proc_meminfo, "r");
70
71 swap_result result = {};
72 result.errorcode = STATE_UNKNOWN;
73
74 if (meminfo_file_ptr == NULL) {
75 // failed to open meminfo file
76 // errno should contain an error
77 result.errorcode = STATE_UNKNOWN;
78 return result;
79 }
80
81 unsigned long swap_total = 0;
82 unsigned long swap_used = 0;
83 unsigned long swap_free = 0;
84
85 bool found_total = false;
86 bool found_free = false;
87
88 char input_buffer[MAX_INPUT_BUFFER];
89 char str[32];
90
91 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) {
92
93 /*
94 * The following sscanf call looks for a line looking like: "Swap: 123
95 * 123 123" which exists on NetBSD (at least),
96 * The unit should be Bytes
97 */
98 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used,
99 &swap_free) == 3) {
100 found_total = true;
101 found_free = true;
102 // Set error
103 result.errorcode = STATE_OK;
104 // Break out of fgets here, since both scanf expressions might match (NetBSD for
105 // example)
106 break;
107 }
108
109 /*
110 * The following sscanf call looks for lines looking like:
111 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least
112 * on Debian Linux with a 5.* kernel
113 */
114 unsigned long tmp_KB = 0;
115 int sscanf_result = sscanf(input_buffer,
116 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu "
117 "%*[k]%*[B]",
118 str, &tmp_KB);
119
120 if (sscanf_result == 2) {
121
122 if (verbose >= 3) {
123 printf("Got %s with %lu\n", str, tmp_KB);
124 }
125
126 /* I think this part is always in Kb, so convert to bytes */
127 if (strcmp("Total", str) == 0) {
128 swap_total = tmp_KB * 1000;
129 found_total = true;
130 } else if (strcmp("Free", str) == 0) {
131 swap_free += tmp_KB * 1000;
132 found_free = true;
133 } else if (strcmp("Cached", str) == 0) {
134 swap_free += tmp_KB * 1000;
135 }
136
137 result.errorcode = STATE_OK;
138 }
139 }
140
141 fclose(meminfo_file_ptr);
142
143 result.metrics.total = swap_total;
144 result.metrics.free = swap_free;
145 result.metrics.used = swap_total - swap_free;
146
147 if (!found_free || !found_total) {
148 result.errorcode = STATE_UNKNOWN;
149 }
150
151 return result;
152}
153
154swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[],
155 const char swap_format[]) {
156 swap_result result = {0};
157
158 char *temp_buffer;
159
160 if (verbose >= 2) {
161 printf(_("Command: %s\n"), swap_command);
162 }
163 if (verbose >= 3) {
164 printf(_("Format: %s\n"), swap_format);
165 }
166
167 child_process = spopen(swap_command);
168 if (child_process == NULL) {
169 printf(_("Could not open pipe: %s\n"), swap_command);
170 swap_result tmp = {
171 .errorcode = STATE_UNKNOWN,
172 };
173 return tmp;
174 }
175
176 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
177 if (child_stderr == NULL) {
178 printf(_("Could not open stderr for %s\n"), swap_command);
179 }
180
181 char str[32] = {0};
182 char input_buffer[MAX_INPUT_BUFFER];
183
184 /* read 1st line */
185 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process);
186 if (strcmp(swap_format, "") == 0) {
187 temp_buffer = strtok(input_buffer, " \n");
188 while (temp_buffer) {
189 if (strstr(temp_buffer, "blocks")) {
190 sprintf(str, "%s %s", str, "%lu");
191 } else if (strstr(temp_buffer, "dskfree")) {
192 sprintf(str, "%s %s", str, "%lu");
193 } else {
194 sprintf(str, "%s %s", str, "%*s");
195 }
196 temp_buffer = strtok(NULL, " \n");
197 }
198 }
199
200 double total_swap_mb = 0;
201 double free_swap_mb = 0;
202 double used_swap_mb = 0;
203 double dsktotal_mb = 0;
204 double dskused_mb = 0;
205 double dskfree_mb = 0;
206
207 /*
208 * If different swap command is used for summary switch, need to read format
209 * differently
210 */
211 if (config.on_aix && !config.allswaps) {
212 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */
213 sscanf(input_buffer, swap_format, &total_swap_mb, &used_swap_mb);
214 free_swap_mb = total_swap_mb * (100 - used_swap_mb) / 100;
215 used_swap_mb = total_swap_mb - free_swap_mb;
216
217 if (verbose >= 3) {
218 printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb,
219 free_swap_mb);
220 }
221 } else {
222 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
223 sscanf(input_buffer, swap_format, &dsktotal_mb, &dskfree_mb);
224
225 dsktotal_mb = dsktotal_mb / config.conversion_factor;
226 /* AIX lists percent used, so this converts to dskfree in MBs */
227
228 if (config.on_aix) {
229 dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100;
230 } else {
231 dskfree_mb = dskfree_mb / config.conversion_factor;
232 }
233
234 if (verbose >= 3) {
235 printf(_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb);
236 }
237
238 dskused_mb = dsktotal_mb - dskfree_mb;
239 total_swap_mb += dsktotal_mb;
240 used_swap_mb += dskused_mb;
241 free_swap_mb += dskfree_mb;
242 }
243 }
244
245 result.metrics.free = free_swap_mb * 1024 * 1024;
246 result.metrics.used = used_swap_mb * 1024 * 1024;
247 result.metrics.total = free_swap_mb * 1024 * 1024;
248
249 /* If we get anything on STDERR, at least set warning */
250 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
251 result.statusCode = max_state(result.statusCode, STATE_WARNING);
252 // TODO Set error here
253 }
254
255 /* close stderr */
256 (void)fclose(child_stderr);
257
258 /* close the pipe */
259 if (spclose(child_process)) {
260 result.statusCode = max_state(result.statusCode, STATE_WARNING);
261 // TODO set error here
262 }
263
264 return result;
265}
266
267#ifndef CHECK_SWAP_SWAPCTL_BSD
268# define CHECK_SWAP_SWAPCTL_BSD
269
270// Stub functionality for BSD stuff, so the compiler always sees the following BSD code
271
272# define SWAP_NSWAP 0
273# define SWAP_STATS 1
274
275int bsd_swapctl(int cmd, const void *arg, int misc) {
276 (void)cmd;
277 (void)arg;
278 (void)misc;
279 return 512;
280}
281
282struct swapent {
283 dev_t se_dev; /* device id */
284 int se_flags; /* entry flags */
285 int se_nblks; /* total blocks */
286 int se_inuse; /* blocks in use */
287 int se_priority; /* priority */
288 char se_path[PATH_MAX]; /* path to entry */
289};
290
291#else
292
293// Includes for NetBSD
294# include <unistd.h>
295# include <sys/swap.h>
296
297# define bsd_swapctl swapctl
298
299#endif // CHECK_SWAP_SWAPCTL_BSD
300
301swap_result getSwapFromSwapctl_BSD(swap_config config) {
302 /* get the number of active swap devices */
303 int nswaps = bsd_swapctl(SWAP_NSWAP, NULL, 0);
304
305 /* initialize swap table + entries */
306 struct swapent *ent = (struct swapent *)malloc(sizeof(struct swapent) * (unsigned long)nswaps);
307
308 /* and now, tally 'em up */
309 int swapctl_res = bsd_swapctl(SWAP_STATS, ent, nswaps);
310 if (swapctl_res < 0) {
311 perror(_("swapctl failed: "));
312 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
313 }
314
315 double dsktotal_mb = 0.0;
316 double dskfree_mb = 0.0;
317 double dskused_mb = 0.0;
318 unsigned long long total_swap_mb = 0;
319 unsigned long long free_swap_mb = 0;
320 unsigned long long used_swap_mb = 0;
321
322 for (int i = 0; i < nswaps; i++) {
323 dsktotal_mb = (double)ent[i].se_nblks / (double)config.conversion_factor;
324 dskused_mb = (double)ent[i].se_inuse / (double)config.conversion_factor;
325 dskfree_mb = (dsktotal_mb - dskused_mb);
326
327 if (config.allswaps && dsktotal_mb > 0) {
328 double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb));
329
330 if (verbose) {
331 printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent);
332 }
333 }
334
335 total_swap_mb += (unsigned long long)dsktotal_mb;
336 free_swap_mb += (unsigned long long)dskfree_mb;
337 used_swap_mb += (unsigned long long)dskused_mb;
338 }
339
340 /* and clean up after ourselves */
341 free(ent);
342
343 swap_result result = {0};
344
345 result.statusCode = OK;
346 result.errorcode = OK;
347
348 result.metrics.total = total_swap_mb * 1024 * 1024;
349 result.metrics.free = free_swap_mb * 1024 * 1024;
350 result.metrics.used = used_swap_mb * 1024 * 1024;
351
352 return result;
353}
354
355#ifndef CHECK_SWAP_SWAPCTL_SVR4
356int srv4_swapctl(int cmd, void *arg) {
357 (void)cmd;
358 (void)arg;
359 return 512;
360}
361
362typedef struct srv4_swapent {
363 char *ste_path; /* name of the swap file */
364 off_t ste_start; /* starting block for swapping */
365 off_t ste_length; /* length of swap area */
366 long ste_pages; /* number of pages for swapping */
367 long ste_free; /* number of ste_pages free */
368 long ste_flags; /* ST_INDEL bit set if swap file */
369 /* is now being deleted */
370} swapent_t;
371
372typedef struct swaptbl {
373 int swt_n; /* number of swapents following */
374 struct srv4_swapent swt_ent[]; /* array of swt_n swapents */
375} swaptbl_t;
376
377# define SC_LIST 2
378# define SC_GETNSWP 3
379
380# ifndef MAXPATHLEN
381# define MAXPATHLEN 2048
382# endif
383
384#else
385# define srv4_swapctl swapctl
386#endif
387
388swap_result getSwapFromSwap_SRV4(swap_config config) {
389 int nswaps = 0;
390
391 /* get the number of active swap devices */
392 if ((nswaps = srv4_swapctl(SC_GETNSWP, NULL)) == -1) {
393 die(STATE_UNKNOWN, _("Error getting swap devices\n"));
394 }
395
396 if (nswaps == 0) {
397 die(STATE_OK, _("SWAP OK: No swap devices defined\n"));
398 }
399
400 if (verbose >= 3) {
401 printf("Found %d swap device(s)\n", nswaps);
402 }
403
404 /* initialize swap table + entries */
405 swaptbl_t *tbl =
406 (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
407
408 if (tbl == NULL) {
409 die(STATE_UNKNOWN, _("malloc() failed!\n"));
410 }
411
412 memset(tbl, 0, sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
413 tbl->swt_n = nswaps;
414
415 for (int i = 0; i < nswaps; i++) {
416 if ((tbl->swt_ent[i].ste_path = (char *)malloc(sizeof(char) * MAXPATHLEN)) == NULL) {
417 die(STATE_UNKNOWN, _("malloc() failed!\n"));
418 }
419 }
420
421 /* and now, tally 'em up */
422 int swapctl_res = srv4_swapctl(SC_LIST, tbl);
423 if (swapctl_res < 0) {
424 perror(_("swapctl failed: "));
425 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
426 }
427
428 double dsktotal_mb = 0.0;
429 double dskfree_mb = 0.0;
430 double dskused_mb = 0.0;
431 unsigned long long total_swap_mb = 0;
432 unsigned long long free_swap_mb = 0;
433 unsigned long long used_swap_mb = 0;
434
435 for (int i = 0; i < nswaps; i++) {
436 dsktotal_mb = (float)tbl->swt_ent[i].ste_pages / SWAP_CONVERSION;
437 dskfree_mb = (float)tbl->swt_ent[i].ste_free / SWAP_CONVERSION;
438 dskused_mb = (dsktotal_mb - dskfree_mb);
439
440 if (verbose >= 3) {
441 printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb,
442 dskused_mb);
443 }
444
445 if (config.allswaps && dsktotal_mb > 0) {
446 double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb));
447
448 if (verbose) {
449 printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent);
450 }
451 }
452
453 total_swap_mb += (unsigned long long)dsktotal_mb;
454 free_swap_mb += (unsigned long long)dskfree_mb;
455 used_swap_mb += (unsigned long long)dskused_mb;
456 }
457
458 /* and clean up after ourselves */
459 for (int i = 0; i < nswaps; i++) {
460 free(tbl->swt_ent[i].ste_path);
461 }
462 free(tbl);
463
464 swap_result result = {0};
465 result.errorcode = OK;
466 result.metrics.total = total_swap_mb * 1024 * 1024;
467 result.metrics.free = free_swap_mb * 1024 * 1024;
468 result.metrics.used = used_swap_mb * 1024 * 1024;
469
470 return result;
471}
diff --git a/plugins/check_tcp.c b/plugins/check_tcp.c
index 01dd35eb..924322e4 100644
--- a/plugins/check_tcp.c
+++ b/plugins/check_tcp.c
@@ -1,415 +1,508 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_tcp plugin 3 * Monitoring check_tcp plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2013 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2025 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_tcp plugin 10 * This file contains the check_tcp plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 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 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 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 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/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* $Id$ 26 * $Id$
27* 27 *
28*****************************************************************************/ 28 *****************************************************************************/
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-2008"; 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, char **);
55void print_help (void);
56void print_usage (void);
57
58#define EXPECT server_expect[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
89#ifdef HAVE_SSL 56#ifdef HAVE_SSL
90static char *sni = NULL; 57 if (use_tls) {
91static bool sni_specified = false; 58 return np_net_ssl_write(buf, (int)len);
59 }
92#endif 60#endif
61 return write(socket_descriptor, buf, len);
62}
93 63
94#define FLAG_SSL 0x01 64typedef struct {
95#define FLAG_VERBOSE 0x02 65 int errorcode;
96#define FLAG_TIME_WARN 0x04 66 check_tcp_config config;
97#define FLAG_TIME_CRIT 0x08 67} check_tcp_config_wrapper;
98#define FLAG_HIDE_OUTPUT 0x10 68static check_tcp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/,
99static size_t flags; 69 check_tcp_config /*config*/);
100 70void print_help(const char *service);
101int 71void print_usage(void);
102main (int argc, char **argv) 72
103{ 73int verbosity = 0;
104 int result = STATE_UNKNOWN; 74
105 char *status = NULL; 75static const int READ_TIMEOUT = 2;
106 struct timeval tv; 76
107 struct timeval timeout; 77const int MAXBUF = 1024;
108 int match = -1; 78
109 fd_set rfds; 79const int DEFAULT_FTP_PORT = 21;
110 80const int DEFAULT_POP_PORT = 110;
111 FD_ZERO(&rfds); 81const int DEFAULT_SPOP_PORT = 995;
112 82const int DEFAULT_SMTP_PORT = 25;
113 setlocale (LC_ALL, ""); 83const int DEFAULT_SSMTP_PORT = 465;
114 bindtextdomain (PACKAGE, LOCALEDIR); 84const int DEFAULT_IMAP_PORT = 143;
115 textdomain (PACKAGE); 85const int DEFAULT_SIMAP_PORT = 993;
86const int DEFAULT_XMPP_C2S_PORT = 5222;
87const int DEFAULT_NNTP_PORT = 119;
88const int DEFAULT_NNTPS_PORT = 563;
89const int DEFAULT_CLAMD_PORT = 3310;
90
91int main(int argc, char **argv) {
92#ifdef __OpenBSD__
93 /* - rpath is required to read --extra-opts (given up later)
94 * - inet is required for sockets
95 * - unix is required for Unix domain sockets
96 * - dns is required for name lookups */
97 pledge("stdio rpath inet unix dns", NULL);
98#endif // __OpenBSD__
99
100 setlocale(LC_ALL, "");
101 bindtextdomain(PACKAGE, LOCALEDIR);
102 textdomain(PACKAGE);
116 103
117 /* determine program- and service-name quickly */ 104 /* determine program- and service-name quickly */
118 progname = strrchr(argv[0], '/'); 105 progname = strrchr(argv[0], '/');
119 if(progname != NULL) progname++; 106 if (progname != NULL) {
120 else progname = argv[0]; 107 progname++;
108 } else {
109 progname = argv[0];
110 }
111
112 // Initialize config here with values from above,
113 // might be changed by on disk config or cli commands
114 check_tcp_config config = check_tcp_config_init();
121 115
122 size_t prog_name_len = strlen(progname); 116 size_t prog_name_len = strlen(progname);
123 if(prog_name_len > 6 && !memcmp(progname, "check_", 6)) { 117 const size_t prefix_length = strlen("check_");
124 SERVICE = strdup(progname + 6); 118
125 for(size_t i = 0; i < prog_name_len - 6; i++) 119 if (prog_name_len <= prefix_length) {
126 SERVICE[i] = toupper(SERVICE[i]); 120 die(STATE_UNKNOWN, _("Weird progname"));
121 }
122
123 if (!memcmp(progname, "check_", prefix_length)) {
124 config.service = strdup(progname + prefix_length);
125 if (config.service == NULL) {
126 die(STATE_UNKNOWN, _("Allocation failed"));
127 }
128
129 for (size_t i = 0; i < prog_name_len - prefix_length; i++) {
130 config.service[i] = toupper(config.service[i]);
131 }
127 } 132 }
128 133
129 /* set up a reasonable buffer at first (will be realloc()'ed if 134 /* set up a reasonable buffer at first (will be realloc()'ed if
130 * user specifies other options) */ 135 * user specifies other options) */
131 server_expect = calloc(sizeof(char *), 2); 136 config.server_expect = calloc(2, sizeof(char *));
132 137
133 /* determine defaults for this service's protocol */ 138 if (config.server_expect == NULL) {
134 if (!strncmp(SERVICE, "UDP", 3)) { 139 die(STATE_UNKNOWN, _("Allocation failed"));
135 PROTOCOL = IPPROTO_UDP;
136 }
137 else if (!strncmp(SERVICE, "FTP", 3)) {
138 EXPECT = "220";
139 QUIT = "QUIT\r\n";
140 PORT = 21;
141 } 140 }
142 else if (!strncmp(SERVICE, "POP", 3) || !strncmp(SERVICE, "POP3", 4)) { 141
143 EXPECT = "+OK"; 142 /* determine defaults for this service's protocol */
144 QUIT = "QUIT\r\n"; 143 if (!strncmp(config.service, "UDP", strlen("UDP"))) {
145 PORT = 110; 144 config.protocol = IPPROTO_UDP;
146 } 145 } else if (!strncmp(config.service, "FTP", strlen("FTP"))) {
147 else if (!strncmp(SERVICE, "SMTP", 4)) { 146 config.server_expect[0] = "220";
148 EXPECT = "220"; 147 config.quit = "QUIT\r\n";
149 QUIT = "QUIT\r\n"; 148 config.server_port = DEFAULT_FTP_PORT;
150 PORT = 25; 149 } else if (!strncmp(config.service, "POP", strlen("POP")) ||
151 } 150 !strncmp(config.service, "POP3", strlen("POP3"))) {
152 else if (!strncmp(SERVICE, "IMAP", 4)) { 151 config.server_expect[0] = "+OK";
153 EXPECT = "* OK"; 152 config.quit = "QUIT\r\n";
154 QUIT = "a1 LOGOUT\r\n"; 153 config.server_port = DEFAULT_POP_PORT;
155 PORT = 143; 154 } else if (!strncmp(config.service, "SMTP", strlen("SMTP"))) {
155 config.server_expect[0] = "220";
156 config.quit = "QUIT\r\n";
157 config.server_port = DEFAULT_SMTP_PORT;
158 } else if (!strncmp(config.service, "IMAP", strlen("IMAP"))) {
159 config.server_expect[0] = "* OK";
160 config.quit = "a1 LOGOUT\r\n";
161 config.server_port = DEFAULT_IMAP_PORT;
156 } 162 }
157#ifdef HAVE_SSL 163#ifdef HAVE_SSL
158 else if (!strncmp(SERVICE, "SIMAP", 5)) { 164 else if (!strncmp(config.service, "SIMAP", strlen("SIMAP"))) {
159 EXPECT = "* OK"; 165 config.server_expect[0] = "* OK";
160 QUIT = "a1 LOGOUT\r\n"; 166 config.quit = "a1 LOGOUT\r\n";
161 flags |= FLAG_SSL; 167 config.use_tls = true;
162 PORT = 993; 168 config.server_port = DEFAULT_SIMAP_PORT;
163 } 169 } else if (!strncmp(config.service, "SPOP", strlen("SPOP"))) {
164 else if (!strncmp(SERVICE, "SPOP", 4)) { 170 config.server_expect[0] = "+OK";
165 EXPECT = "+OK"; 171 config.quit = "QUIT\r\n";
166 QUIT = "QUIT\r\n"; 172 config.use_tls = true;
167 flags |= FLAG_SSL; 173 config.server_port = DEFAULT_SPOP_PORT;
168 PORT = 995; 174 } else if (!strncmp(config.service, "SSMTP", strlen("SSMTP"))) {
169 } 175 config.server_expect[0] = "220";
170 else if (!strncmp(SERVICE, "SSMTP", 5)) { 176 config.quit = "QUIT\r\n";
171 EXPECT = "220"; 177 config.use_tls = true;
172 QUIT = "QUIT\r\n"; 178 config.server_port = DEFAULT_SSMTP_PORT;
173 flags |= FLAG_SSL; 179 } else if (!strncmp(config.service, "JABBER", strlen("JABBER"))) {
174 PORT = 465; 180 config.send = "<stream:stream to=\'host\' xmlns=\'jabber:client\' "
175 } 181 "xmlns:stream=\'http://etherx.jabber.org/streams\'>\n";
176 else if (!strncmp(SERVICE, "JABBER", 6)) { 182 config.server_expect[0] = "<?xml version=\'1.0\'";
177 SEND = "<stream:stream to=\'host\' xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'>\n"; 183 config.quit = "</stream:stream>\n";
178 EXPECT = "<?xml version=\'1.0\'"; 184 config.hide_output = true;
179 QUIT = "</stream:stream>\n"; 185 config.server_port = DEFAULT_XMPP_C2S_PORT;
180 flags |= FLAG_HIDE_OUTPUT; 186 } else if (!strncmp(config.service, "NNTPS", strlen("NNTPS"))) {
181 PORT = 5222; 187 config.server_expect_count = 2;
182 } 188 config.server_expect[0] = "200";
183 else if (!strncmp (SERVICE, "NNTPS", 5)) { 189 config.server_expect[1] = "201";
184 server_expect_count = 2; 190 config.quit = "QUIT\r\n";
185 server_expect[0] = "200"; 191 config.use_tls = true;
186 server_expect[1] = "201"; 192 config.server_port = DEFAULT_NNTPS_PORT;
187 QUIT = "QUIT\r\n";
188 flags |= FLAG_SSL;
189 PORT = 563;
190 } 193 }
191#endif 194#endif
192 else if (!strncmp (SERVICE, "NNTP", 4)) { 195 else if (!strncmp(config.service, "NNTP", strlen("NNTP"))) {
193 server_expect_count = 2; 196 config.server_expect_count = 2;
194 server_expect = malloc(sizeof(char *) * server_expect_count); 197 char **tmp = realloc(config.server_expect, config.server_expect_count * sizeof(char *));
195 server_expect[0] = strdup("200"); 198 if (tmp == NULL) {
196 server_expect[1] = strdup("201"); 199 free(config.server_expect);
197 QUIT = "QUIT\r\n"; 200 die(STATE_UNKNOWN, _("Allocation failed"));
198 PORT = 119; 201 }
199 } 202 config.server_expect = tmp;
200 else if (!strncmp(SERVICE, "CLAMD", 5)) { 203
201 SEND = "PING"; 204 config.server_expect[0] = strdup("200");
202 EXPECT = "PONG"; 205 config.server_expect[1] = strdup("201");
203 QUIT = NULL; 206 config.quit = "QUIT\r\n";
204 PORT = 3310; 207 config.server_port = DEFAULT_NNTP_PORT;
208 } else if (!strncmp(config.service, "CLAMD", strlen("CLAMD"))) {
209 config.send = "PING";
210 config.server_expect[0] = "PONG";
211 config.quit = NULL;
212 config.server_port = DEFAULT_CLAMD_PORT;
205 } 213 }
206 /* fallthrough check, so it's supposed to use reverse matching */ 214 /* fallthrough check, so it's supposed to use reverse matching */
207 else if (strcmp (SERVICE, "TCP")) 215 else if (strcmp(config.service, "TCP")) {
208 usage (_("CRITICAL - Generic check_tcp called with unknown service\n")); 216 usage(_("CRITICAL - Generic check_tcp called with unknown service\n"));
209 217 }
210 server_address = "127.0.0.1";
211 server_port = PORT;
212 server_send = SEND;
213 server_quit = QUIT;
214 status = NULL;
215 218
216 /* Parse extra opts if any */ 219 /* Parse extra opts if any */
217 argv=np_extra_opts (&argc, argv, progname); 220 argv = np_extra_opts(&argc, argv, progname);
218 221
219 if (process_arguments (argc, argv) == ERROR) 222 check_tcp_config_wrapper paw = process_arguments(argc, argv, config);
220 usage4 (_("Could not parse arguments")); 223 if (paw.errorcode == ERROR) {
224 usage4(_("Could not parse arguments"));
225 }
221 226
222 if(flags & FLAG_VERBOSE) { 227#ifdef __OpenBSD__
223 printf("Using service %s\n", SERVICE); 228 pledge("stdio inet unix dns", NULL);
224 printf("Port: %d\n", server_port); 229#endif // __OpenBSD__
225 printf("flags: 0x%x\n", (int)flags); 230
231 config = paw.config;
232
233 if (verbosity > 0) {
234 printf("Using service %s\n", config.service);
235 printf("Port: %d\n", config.server_port);
226 } 236 }
227 237
228 if(EXPECT && !server_expect_count) 238 if ((config.server_expect_count == 0) && config.server_expect[0]) {
229 server_expect_count++; 239 config.server_expect_count++;
240 }
230 241
231 if(PROTOCOL==IPPROTO_UDP && !(server_expect_count && server_send)){ 242 if (config.protocol == IPPROTO_UDP && !(config.server_expect_count && config.send)) {
232 usage(_("With UDP checks, a send/expect string must be specified.")); 243 usage(_("With UDP checks, a send/expect string must be specified."));
233 } 244 }
234 245
246 // Initialize check stuff before setting timers
247 mp_check overall = mp_check_init();
248 if (config.output_format_set) {
249 mp_set_format(config.output_format);
250 }
251
235 /* set up the timer */ 252 /* set up the timer */
236 signal (SIGALRM, socket_timeout_alarm_handler); 253 signal(SIGALRM, socket_timeout_alarm_handler);
237 alarm (socket_timeout); 254 alarm(socket_timeout);
238 255
239 /* try to connect to the host at the given port number */ 256 /* try to connect to the host at the given port number */
240 gettimeofday (&tv, NULL); 257 struct timeval start_time;
241 258 gettimeofday(&start_time, NULL);
242 result = np_net_connect (server_address, server_port, &sd, PROTOCOL); 259
243 if (result == STATE_CRITICAL) return econn_refuse_state; 260 int socket_descriptor = 0;
261 mp_subcheck inital_connect_result = mp_subcheck_init();
262
263 // Try initial connection
264 if (np_net_connect(config.server_address, config.server_port, &socket_descriptor,
265 config.protocol) == STATE_CRITICAL) {
266 // Early exit here, we got connection refused
267 inital_connect_result =
268 mp_set_subcheck_state(inital_connect_result, config.econn_refuse_state);
269 xasprintf(&inital_connect_result.output, "Connection to %s on port %i was REFUSED",
270 config.server_address, config.server_port);
271 mp_add_subcheck_to_check(&overall, inital_connect_result);
272 mp_exit(overall);
273 } else {
274 inital_connect_result = mp_set_subcheck_state(inital_connect_result, STATE_OK);
275 xasprintf(&inital_connect_result.output, "Connection to %s on port %i was a SUCCESS",
276 config.server_address, config.server_port);
277 mp_add_subcheck_to_check(&overall, inital_connect_result);
278 }
244 279
245#ifdef HAVE_SSL 280#ifdef HAVE_SSL
246 if (flags & FLAG_SSL){ 281 if (config.use_tls) {
247 result = np_net_ssl_init_with_hostname(sd, (sni_specified ? sni : NULL)); 282 mp_subcheck tls_connection_result = mp_subcheck_init();
248 if (result == STATE_OK && check_cert) { 283 mp_state_enum result = np_net_ssl_init_with_hostname(
249 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 284 socket_descriptor, (config.sni_specified ? config.sni : NULL));
285 tls_connection_result = mp_set_subcheck_default_state(tls_connection_result, result);
286
287 if (result == STATE_OK) {
288 xasprintf(&tls_connection_result.output, "TLS connection succeeded");
289
290 if (config.check_cert) {
291 result =
292 np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit);
293
294 mp_subcheck tls_certificate_lifetime_result = mp_subcheck_init();
295 tls_certificate_lifetime_result =
296 mp_set_subcheck_state(tls_certificate_lifetime_result, result);
297
298 if (result == STATE_OK) {
299 xasprintf(&tls_certificate_lifetime_result.output,
300 "Certificate lifetime is within thresholds");
301 } else if (result == STATE_WARNING) {
302 xasprintf(&tls_certificate_lifetime_result.output,
303 "Certificate lifetime is violating warning threshold (%i)",
304 config.days_till_exp_warn);
305 } else if (result == STATE_CRITICAL) {
306 xasprintf(&tls_certificate_lifetime_result.output,
307 "Certificate lifetime is violating critical threshold (%i)",
308 config.days_till_exp_crit);
309 } else {
310 xasprintf(&tls_certificate_lifetime_result.output,
311 "Certificate lifetime is somehow unknown");
312 }
313
314 mp_add_subcheck_to_subcheck(&tls_connection_result,
315 tls_certificate_lifetime_result);
316 }
317
318 mp_add_subcheck_to_check(&overall, tls_connection_result);
319 } else {
320 xasprintf(&tls_connection_result.output, "TLS connection failed");
321 mp_add_subcheck_to_check(&overall, tls_connection_result);
322
323 if (socket_descriptor) {
324 close(socket_descriptor);
325 }
326 np_net_ssl_cleanup();
327
328 mp_exit(overall);
250 } 329 }
251 } 330 }
252 if(result != STATE_OK){
253 if(sd) close(sd);
254 np_net_ssl_cleanup();
255 return result;
256 }
257#endif /* HAVE_SSL */ 331#endif /* HAVE_SSL */
258 332
259 if (server_send != NULL) { /* Something to send? */ 333 if (config.send != NULL) { /* Something to send? */
260 my_send(server_send, strlen(server_send)); 334 my_send(socket_descriptor, config.send, strlen(config.send), config.use_tls);
261 } 335 }
262 336
263 if (delay > 0) { 337 if (config.delay > 0) {
264 tv.tv_sec += delay; 338 start_time.tv_sec += config.delay;
265 sleep (delay); 339 sleep(config.delay);
266 } 340 }
267 341
268 if(flags & FLAG_VERBOSE) { 342 if (verbosity > 0) {
269 if (server_send) { 343 if (config.send) {
270 printf("Send string: %s\n", server_send); 344 printf("Send string: %s\n", config.send);
345 }
346 if (config.quit) {
347 printf("Quit string: %s\n", config.quit);
271 } 348 }
272 if (server_quit) { 349 printf("server_expect_count: %d\n", (int)config.server_expect_count);
273 printf("Quit string: %s\n", server_quit); 350 for (size_t i = 0; i < config.server_expect_count; i++) {
351 printf("\t%zd: %s\n", i, config.server_expect[i]);
274 } 352 }
275 printf("server_expect_count: %d\n", (int)server_expect_count);
276 for(size_t i = 0; i < server_expect_count; i++)
277 printf("\t%zd: %s\n", i, server_expect[i]);
278 } 353 }
279 354
280 /* if(len) later on, we know we have a non-NULL response */ 355 /* if(len) later on, we know we have a non-NULL response */
281 ssize_t len = 0; 356 ssize_t len = 0;
357 char *received_buffer = NULL;
358 enum np_match_result match = NP_MATCH_NONE;
359 mp_subcheck expected_data_result = mp_subcheck_init();
282 360
283 if (server_expect_count) { 361 if (config.server_expect_count) {
284 ssize_t received = 0; 362 ssize_t received = 0;
363 char buffer[MAXBUF];
285 364
286 /* watch for the expect string */ 365 /* watch for the expect string */
287 while ((received = my_recv(buffer, sizeof(buffer))) > 0) { 366 while ((received = my_recv(socket_descriptor, buffer, sizeof(buffer), config.use_tls)) >
288 status = realloc(status, len + received + 1); 367 0) {
289 memcpy(&status[len], buffer, received); 368 received_buffer = realloc(received_buffer, len + received + 1);
369
370 if (received_buffer == NULL) {
371 die(STATE_UNKNOWN, _("Allocation failed"));
372 }
373
374 memcpy(&received_buffer[len], buffer, received);
290 len += received; 375 len += received;
291 status[len] = '\0'; 376 received_buffer[len] = '\0';
292 377
293 /* stop reading if user-forced */ 378 /* stop reading if user-forced */
294 if (maxbytes && len >= maxbytes) 379 if (config.maxbytes && len >= config.maxbytes) {
295 break; 380 break;
381 }
296 382
297 if ((match = np_expect_match(status, 383 if ((match = np_expect_match(received_buffer, config.server_expect,
298 server_expect, 384 config.server_expect_count, config.match_flags)) !=
299 server_expect_count, 385 NP_MATCH_RETRY) {
300 match_flags)) != NP_MATCH_RETRY)
301 break; 386 break;
387 }
388
389 fd_set rfds;
390 FD_ZERO(&rfds);
391 FD_SET(socket_descriptor, &rfds);
302 392
303 /* some protocols wait for further input, so make sure we don't wait forever */ 393 /* some protocols wait for further input, so make sure we don't wait forever */
304 FD_SET(sd, &rfds); 394 struct timeval timeout;
305 timeout.tv_sec = READ_TIMEOUT; 395 timeout.tv_sec = READ_TIMEOUT;
306 timeout.tv_usec = 0; 396 timeout.tv_usec = 0;
307 if(select(sd + 1, &rfds, NULL, NULL, &timeout) <= 0) 397
398 if (select(socket_descriptor + 1, &rfds, NULL, NULL, &timeout) <= 0) {
308 break; 399 break;
400 }
309 } 401 }
310 402
311 if (match == NP_MATCH_RETRY) 403 if (match == NP_MATCH_RETRY) {
312 match = NP_MATCH_FAILURE; 404 match = NP_MATCH_FAILURE;
405 }
313 406
314 /* no data when expected, so return critical */ 407 /* no data when expected, so return critical */
315 if (len == 0) 408 if (len == 0) {
316 die (STATE_CRITICAL, _("No data received from host\n")); 409 xasprintf(&expected_data_result.output, "Received no data when some was expected");
410 expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_CRITICAL);
411 mp_add_subcheck_to_check(&overall, expected_data_result);
412 mp_exit(overall);
413 }
317 414
318 /* print raw output if we're debugging */ 415 /* print raw output if we're debugging */
319 if(flags & FLAG_VERBOSE) 416 if (verbosity > 0) {
320 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n", 417 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n",
321 (int)len + 1, status); 418 (int)len + 1, received_buffer);
419 }
322 /* strip whitespace from end of output */ 420 /* strip whitespace from end of output */
323 while(--len > 0 && isspace(status[len])) 421 while (--len > 0 && isspace(received_buffer[len])) {
324 status[len] = '\0'; 422 received_buffer[len] = '\0';
423 }
424 }
425
426 if (config.quit != NULL) {
427 my_send(socket_descriptor, config.quit, strlen(config.quit), config.use_tls);
325 } 428 }
326 429
327 if (server_quit != NULL) { 430 if (socket_descriptor) {
328 my_send(server_quit, strlen(server_quit)); 431 close(socket_descriptor);
329 } 432 }
330 if (sd) close (sd);
331#ifdef HAVE_SSL 433#ifdef HAVE_SSL
332 np_net_ssl_cleanup(); 434 np_net_ssl_cleanup();
333#endif 435#endif
334 436
335 microsec = deltime (tv); 437 long microsec = deltime(start_time);
336 elapsed_time = (double)microsec / 1.0e6; 438 double elapsed_time = (double)microsec / 1.0e6;
439
440 mp_subcheck elapsed_time_result = mp_subcheck_init();
441
442 mp_perfdata time_pd = perfdata_init();
443 time_pd = mp_set_pd_value(time_pd, elapsed_time);
444 time_pd.label = "time";
445 time_pd.uom = "s";
446
447 if (config.critical_time_set && elapsed_time > config.critical_time) {
448 xasprintf(&elapsed_time_result.output,
449 "Connection time %fs exceeded critical threshold (%f)", elapsed_time,
450 config.critical_time);
451
452 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_CRITICAL);
453 time_pd.crit_present = true;
454 mp_range crit_val = mp_range_init();
455
456 crit_val.end = mp_create_pd_value(config.critical_time);
457 crit_val.end_infinity = false;
458
459 time_pd.crit = crit_val;
460 } else if (config.warning_time_set && elapsed_time > config.warning_time) {
461 xasprintf(&elapsed_time_result.output,
462 "Connection time %fs exceeded warning threshold (%f)", elapsed_time,
463 config.critical_time);
464
465 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_WARNING);
466 time_pd.warn_present = true;
467 mp_range warn_val = mp_range_init();
468 warn_val.end = mp_create_pd_value(config.critical_time);
469 warn_val.end_infinity = false;
470
471 time_pd.warn = warn_val;
472 } else {
473 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_OK);
474 xasprintf(&elapsed_time_result.output, "Connection time %fs is within thresholds",
475 elapsed_time);
476 }
337 477
338 if (flags & FLAG_TIME_CRIT && elapsed_time > critical_time) 478 mp_add_perfdata_to_subcheck(&elapsed_time_result, time_pd);
339 result = STATE_CRITICAL; 479 mp_add_subcheck_to_check(&overall, elapsed_time_result);
340 else if (flags & FLAG_TIME_WARN && elapsed_time > warning_time)
341 result = STATE_WARNING;
342 480
343 /* did we get the response we hoped? */ 481 /* did we get the response we hoped? */
344 if(match == NP_MATCH_FAILURE && result != STATE_CRITICAL) 482 if (match == NP_MATCH_FAILURE) {
345 result = expect_mismatch_state; 483 expected_data_result =
484 mp_set_subcheck_state(expected_data_result, config.expect_mismatch_state);
485 xasprintf(&expected_data_result.output, "Answer failed to match expectation");
486 mp_add_subcheck_to_check(&overall, expected_data_result);
487 } else if (match == NP_MATCH_SUCCESS) {
488 expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_OK);
489 xasprintf(&expected_data_result.output, "The answer of the server matched the expectation");
490 mp_add_subcheck_to_check(&overall, expected_data_result);
491 }
346 492
347 /* reset the alarm */ 493 /* reset the alarm */
348 alarm (0); 494 alarm(0);
349
350 /* this is a bit stupid, because we don't want to print the
351 * response time (which can look ok to the user) if we didn't get
352 * the response we were looking for. if-else */
353 printf("%s %s - ", SERVICE, state_text(result));
354
355 if(match == NP_MATCH_FAILURE && len && !(flags & FLAG_HIDE_OUTPUT))
356 printf("Unexpected response from host/socket: %s", status);
357 else {
358 if(match == NP_MATCH_FAILURE)
359 printf("Unexpected response from host/socket on ");
360 else
361 printf("%.3f second response time on ", elapsed_time);
362 if(server_address[0] != '/') {
363 if (host_specified)
364 printf("%s port %d",
365 server_address, server_port);
366 else
367 printf("port %d", server_port);
368 }
369 else
370 printf("socket %s", server_address);
371 }
372 495
373 if (match != NP_MATCH_FAILURE && !(flags & FLAG_HIDE_OUTPUT) && len) 496 mp_exit(overall);
374 printf (" [%s]", status);
375
376 /* perf-data doesn't apply when server doesn't talk properly,
377 * so print all zeroes on warn and crit. Use fperfdata since
378 * localisation settings can make different outputs */
379 if(match == NP_MATCH_FAILURE)
380 printf ("|%s",
381 fperfdata ("time", elapsed_time, "s",
382 (flags & FLAG_TIME_WARN ? true : false), 0,
383 (flags & FLAG_TIME_CRIT ? true : false), 0,
384 true, 0,
385 true, socket_timeout)
386 );
387 else
388 printf("|%s",
389 fperfdata ("time", elapsed_time, "s",
390 (flags & FLAG_TIME_WARN ? true : false), warning_time,
391 (flags & FLAG_TIME_CRIT ? true : false), critical_time,
392 true, 0,
393 true, socket_timeout)
394 );
395
396 putchar('\n');
397 return result;
398} 497}
399 498
400
401
402/* process command-line arguments */ 499/* process command-line arguments */
403static int process_arguments (int argc, char **argv) { 500static check_tcp_config_wrapper process_arguments(int argc, char **argv, check_tcp_config config) {
404 int c;
405 bool escape = false;
406 char *temp;
407
408 enum { 501 enum {
409 SNI_OPTION = CHAR_MAX + 1 502 SNI_OPTION = CHAR_MAX + 1,
503 output_format_index,
410 }; 504 };
411 505
412 int option = 0;
413 static struct option longopts[] = { 506 static struct option longopts[] = {
414 {"hostname", required_argument, 0, 'H'}, 507 {"hostname", required_argument, 0, 'H'},
415 {"critical", required_argument, 0, 'c'}, 508 {"critical", required_argument, 0, 'c'},
@@ -437,278 +530,307 @@ static int process_arguments (int argc, char **argv) {
437 {"ssl", no_argument, 0, 'S'}, 530 {"ssl", no_argument, 0, 'S'},
438 {"sni", required_argument, 0, SNI_OPTION}, 531 {"sni", required_argument, 0, SNI_OPTION},
439 {"certificate", required_argument, 0, 'D'}, 532 {"certificate", required_argument, 0, 'D'},
440 {0, 0, 0, 0} 533 {"output-format", required_argument, 0, output_format_index},
441 }; 534 {0, 0, 0, 0}};
442 535
443 if (argc < 2) 536 if (argc < 2) {
444 usage4 (_("No arguments found")); 537 usage4(_("No arguments found"));
538 }
445 539
446 /* backwards compatibility */ 540 /* backwards compatibility */
447 for (c = 1; c < argc; c++) { 541 for (int i = 1; i < argc; i++) {
448 if (strcmp ("-to", argv[c]) == 0) 542 if (strcmp("-to", argv[i]) == 0) {
449 strcpy (argv[c], "-t"); 543 strcpy(argv[i], "-t");
450 else if (strcmp ("-wt", argv[c]) == 0) 544 } else if (strcmp("-wt", argv[i]) == 0) {
451 strcpy (argv[c], "-w"); 545 strcpy(argv[i], "-w");
452 else if (strcmp ("-ct", argv[c]) == 0) 546 } else if (strcmp("-ct", argv[i]) == 0) {
453 strcpy (argv[c], "-c"); 547 strcpy(argv[i], "-c");
548 }
454 } 549 }
455 550
456 if (!is_option (argv[1])) { 551 if (!is_option(argv[1])) {
457 server_address = argv[1]; 552 config.server_address = argv[1];
458 argv[1] = argv[0]; 553 argv[1] = argv[0];
459 argv = &argv[1]; 554 argv = &argv[1];
460 argc--; 555 argc--;
461 } 556 }
462 557
463 while (1) { 558 bool escape = false;
464 c = getopt_long (argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", 559
465 longopts, &option); 560 while (true) {
561 int option = 0;
562 int option_index =
563 getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option);
466 564
467 if (c == -1 || c == EOF || c == 1) 565 if (CHECK_EOF(option_index) || option_index == 1) {
468 break; 566 break;
567 }
469 568
470 switch (c) { 569 switch (option_index) {
471 case '?': /* print short usage statement if args not parsable */ 570 case '?': /* print short usage statement if args not parsable */
472 usage5 (); 571 usage5();
473 case 'h': /* help */ 572 case 'h': /* help */
474 print_help (); 573 print_help(config.service);
475 exit (STATE_UNKNOWN); 574 exit(STATE_UNKNOWN);
476 case 'V': /* version */ 575 case 'V': /* version */
477 print_revision (progname, NP_VERSION); 576 print_revision(progname, NP_VERSION);
478 exit (STATE_UNKNOWN); 577 exit(STATE_UNKNOWN);
479 case 'v': /* verbose mode */ 578 case 'v': /* verbose mode */
480 flags |= FLAG_VERBOSE; 579 verbosity++;
481 match_flags |= NP_MATCH_VERBOSE; 580 config.match_flags |= NP_MATCH_VERBOSE;
482 break; 581 break;
483 case '4': 582 case '4': // Apparently unused TODO
484 address_family = AF_INET; 583 address_family = AF_INET;
485 break; 584 break;
486 case '6': 585 case '6': // Apparently unused TODO
487#ifdef USE_IPV6
488 address_family = AF_INET6; 586 address_family = AF_INET6;
489#else
490 usage4 (_("IPv6 support not available"));
491#endif
492 break;
493 case 'H': /* hostname */
494 host_specified = true;
495 server_address = optarg;
496 break; 587 break;
497 case 'c': /* critical */ 588 case 'H': /* hostname */
498 critical_time = strtod (optarg, NULL); 589 config.host_specified = true;
499 flags |= FLAG_TIME_CRIT; 590 config.server_address = optarg;
500 break; 591 break;
501 case 'j': /* hide output */ 592 case 'c': /* critical */
502 flags |= FLAG_HIDE_OUTPUT; 593 config.critical_time = strtod(optarg, NULL);
594 config.critical_time_set = true;
503 break; 595 break;
504 case 'w': /* warning */ 596 case 'j': /* hide output */
505 warning_time = strtod (optarg, NULL); 597 config.hide_output = true;
506 flags |= FLAG_TIME_WARN;
507 break; 598 break;
508 case 'C': 599 case 'w': /* warning */
509 crit_codes = realloc (crit_codes, ++crit_codes_count); 600 config.warning_time = strtod(optarg, NULL);
510 crit_codes[crit_codes_count - 1] = optarg; 601 config.warning_time_set = true;
511 break; 602 break;
512 case 'W': 603 case 't': /* timeout */
513 warn_codes = realloc (warn_codes, ++warn_codes_count); 604 if (!is_intpos(optarg)) {
514 warn_codes[warn_codes_count - 1] = optarg; 605 usage4(_("Timeout interval must be a positive integer"));
515 break; 606 } else {
516 case 't': /* timeout */ 607 socket_timeout = atoi(optarg);
517 if (!is_intpos (optarg)) 608 }
518 usage4 (_("Timeout interval must be a positive integer"));
519 else
520 socket_timeout = atoi (optarg);
521 break; 609 break;
522 case 'p': /* port */ 610 case 'p': /* port */
523 if (!is_intpos (optarg)) 611 if (!is_intpos(optarg)) {
524 usage4 (_("Port must be a positive integer")); 612 usage4(_("Port must be a positive integer"));
525 else 613 } else {
526 server_port = atoi (optarg); 614 config.server_port = atoi(optarg);
615 }
527 break; 616 break;
528 case 'E': 617 case 'E':
529 escape = true; 618 escape = true;
530 break; 619 break;
531 case 's': 620 case 's':
532 if (escape) 621 if (escape) {
533 server_send = np_escaped_string(optarg); 622 config.send = np_escaped_string(optarg);
534 else 623 } else {
535 xasprintf(&server_send, "%s", optarg); 624 xasprintf(&config.send, "%s", optarg);
625 }
536 break; 626 break;
537 case 'e': /* expect string (may be repeated) */ 627 case 'e': /* expect string (may be repeated) */
538 match_flags &= ~NP_MATCH_EXACT; 628 config.match_flags &= ~NP_MATCH_EXACT;
539 if (server_expect_count == 0) 629 if (config.server_expect_count == 0) {
540 server_expect = malloc (sizeof (char *) * (++server_expect_count)); 630 config.server_expect = malloc(sizeof(char *) * (++config.server_expect_count));
541 else 631 } else {
542 server_expect = realloc (server_expect, sizeof (char *) * (++server_expect_count)); 632 config.server_expect =
543 server_expect[server_expect_count - 1] = optarg; 633 realloc(config.server_expect, sizeof(char *) * (++config.server_expect_count));
634 }
635
636 if (config.server_expect == NULL) {
637 die(STATE_UNKNOWN, _("Allocation failed"));
638 }
639 config.server_expect[config.server_expect_count - 1] = optarg;
544 break; 640 break;
545 case 'm': 641 case 'm':
546 if (!is_intpos (optarg)) 642 if (!is_intpos(optarg)) {
547 usage4 (_("Maxbytes must be a positive integer")); 643 usage4(_("Maxbytes must be a positive integer"));
548 else 644 } else {
549 maxbytes = strtol (optarg, NULL, 0); 645 config.maxbytes = strtol(optarg, NULL, 0);
646 }
550 break; 647 break;
551 case 'q': 648 case 'q':
552 if (escape) 649 if (escape) {
553 server_quit = np_escaped_string(optarg); 650 config.quit = np_escaped_string(optarg);
554 else 651 } else {
555 xasprintf(&server_quit, "%s\r\n", optarg); 652 xasprintf(&config.quit, "%s\r\n", optarg);
653 }
556 break; 654 break;
557 case 'r': 655 case 'r':
558 if (!strncmp(optarg,"ok",2)) 656 if (!strncmp(optarg, "ok", 2)) {
559 econn_refuse_state = STATE_OK; 657 config.econn_refuse_state = STATE_OK;
560 else if (!strncmp(optarg,"warn",4)) 658 } else if (!strncmp(optarg, "warn", 4)) {
561 econn_refuse_state = STATE_WARNING; 659 config.econn_refuse_state = STATE_WARNING;
562 else if (!strncmp(optarg,"crit",4)) 660 } else if (!strncmp(optarg, "crit", 4)) {
563 econn_refuse_state = STATE_CRITICAL; 661 config.econn_refuse_state = STATE_CRITICAL;
564 else 662 } else {
565 usage4 (_("Refuse must be one of ok, warn, crit")); 663 usage4(_("Refuse must be one of ok, warn, crit"));
664 }
566 break; 665 break;
567 case 'M': 666 case 'M':
568 if (!strncmp(optarg,"ok",2)) 667 if (!strncmp(optarg, "ok", 2)) {
569 expect_mismatch_state = STATE_OK; 668 config.expect_mismatch_state = STATE_OK;
570 else if (!strncmp(optarg,"warn",4)) 669 } else if (!strncmp(optarg, "warn", 4)) {
571 expect_mismatch_state = STATE_WARNING; 670 config.expect_mismatch_state = STATE_WARNING;
572 else if (!strncmp(optarg,"crit",4)) 671 } else if (!strncmp(optarg, "crit", 4)) {
573 expect_mismatch_state = STATE_CRITICAL; 672 config.expect_mismatch_state = STATE_CRITICAL;
574 else 673 } else {
575 usage4 (_("Mismatch must be one of ok, warn, crit")); 674 usage4(_("Mismatch must be one of ok, warn, crit"));
675 }
576 break; 676 break;
577 case 'd': 677 case 'd':
578 if (is_intpos (optarg)) 678 if (is_intpos(optarg)) {
579 delay = atoi (optarg); 679 config.delay = atoi(optarg);
580 else 680 } else {
581 usage4 (_("Delay must be a positive integer")); 681 usage4(_("Delay must be a positive integer"));
682 }
582 break; 683 break;
583 case 'D': /* Check SSL cert validity - days 'til certificate expiration */ 684 case 'D': /* Check SSL cert validity - days 'til certificate expiration */
584#ifdef HAVE_SSL 685#ifdef HAVE_SSL
585# ifdef USE_OPENSSL /* XXX */ 686# ifdef MOPL_USE_OPENSSL /* XXX */
586 if ((temp=strchr(optarg,','))!=NULL) { 687 {
587 *temp='\0'; 688 char *temp;
588 if (!is_intnonneg (optarg)) 689 if ((temp = strchr(optarg, ',')) != NULL) {
589 usage2 (_("Invalid certificate expiration period"), optarg); 690 *temp = '\0';
590 days_till_exp_warn = atoi (optarg); 691 if (!is_intnonneg(optarg)) {
591 *temp=','; 692 usage2(_("Invalid certificate expiration period"), optarg);
592 temp++; 693 }
593 if (!is_intnonneg (temp)) 694 config.days_till_exp_warn = atoi(optarg);
594 usage2 (_("Invalid certificate expiration period"), temp); 695 *temp = ',';
595 days_till_exp_crit = atoi (temp); 696 temp++;
697 if (!is_intnonneg(temp)) {
698 usage2(_("Invalid certificate expiration period"), temp);
699 }
700 config.days_till_exp_crit = atoi(temp);
701 } else {
702 config.days_till_exp_crit = 0;
703 if (!is_intnonneg(optarg)) {
704 usage2(_("Invalid certificate expiration period"), optarg);
705 }
706 config.days_till_exp_warn = atoi(optarg);
596 } 707 }
597 else { 708 config.check_cert = true;
598 days_till_exp_crit=0; 709 config.use_tls = true;
599 if (!is_intnonneg (optarg)) 710 } break;
600 usage2 (_("Invalid certificate expiration period"), optarg); 711# endif /* MOPL_USE_OPENSSL */
601 days_till_exp_warn = atoi (optarg);
602 }
603 check_cert = true;
604 flags |= FLAG_SSL;
605 break;
606# endif /* USE_OPENSSL */
607#endif 712#endif
608 /* fallthrough if we don't have ssl */ 713 /* fallthrough if we don't have ssl */
609 case 'S': 714 case 'S':
610#ifdef HAVE_SSL 715#ifdef HAVE_SSL
611 flags |= FLAG_SSL; 716 config.use_tls = true;
612#else 717#else
613 die (STATE_UNKNOWN, _("Invalid option - SSL is not available")); 718 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
614#endif 719#endif
615 break; 720 break;
616 case SNI_OPTION: 721 case SNI_OPTION:
617#ifdef HAVE_SSL 722#ifdef HAVE_SSL
618 flags |= FLAG_SSL; 723 config.use_tls = true;
619 sni_specified = true; 724 config.sni_specified = true;
620 sni = optarg; 725 config.sni = optarg;
621#else 726#else
622 die (STATE_UNKNOWN, _("Invalid option - SSL is not available")); 727 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
623#endif 728#endif
624 break; 729 break;
625 case 'A': 730 case 'A':
626 match_flags |= NP_MATCH_ALL; 731 config.match_flags |= NP_MATCH_ALL;
732 break;
733 case output_format_index: {
734 parsed_output_format parser = mp_parse_output_format(optarg);
735 if (!parser.parsing_success) {
736 // TODO List all available formats here, maybe add anothoer usage function
737 printf("Invalid output format: %s\n", optarg);
738 exit(STATE_UNKNOWN);
739 }
740
741 config.output_format_set = true;
742 config.output_format = parser.output_format;
627 break; 743 break;
628 } 744 }
745 }
629 } 746 }
630 747
631 c = optind; 748 int index = optind;
632 if(!host_specified && c < argc) 749 if (!config.host_specified && index < argc) {
633 server_address = strdup (argv[c++]); 750 config.server_address = strdup(argv[index++]);
751 }
634 752
635 if (server_address == NULL) 753 if (config.server_address == NULL) {
636 usage4 (_("You must provide a server address")); 754 usage4(_("You must provide a server address"));
637 else if (server_address[0] != '/' && !is_host(server_address)) 755 } else if (config.server_address[0] != '/' && !is_host(config.server_address)) {
638 die (STATE_CRITICAL, "%s %s - %s: %s\n", SERVICE, state_text(STATE_CRITICAL), _("Invalid hostname, address or socket"), server_address); 756 die(STATE_CRITICAL, "%s %s - %s: %s\n", config.service, state_text(STATE_CRITICAL),
757 _("Invalid hostname, address or socket"), config.server_address);
758 }
639 759
640 return OK; 760 check_tcp_config_wrapper result = {
761 .config = config,
762 .errorcode = OK,
763 };
764 return result;
641} 765}
642 766
643 767void print_help(const char *service) {
644void 768 print_revision(progname, NP_VERSION);
645print_help (void) 769
646{ 770 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
647 print_revision (progname, NP_VERSION); 771 printf(COPYRIGHT, copyright, email);
648 772
649 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 773 printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"),
650 printf (COPYRIGHT, copyright, email); 774 service);
651 775
652 printf (_("This plugin tests %s connections with the specified host (or unix socket).\n\n"), 776 print_usage();
653 SERVICE); 777
654 778 printf(UT_HELP_VRSN);
655 print_usage (); 779 printf(UT_EXTRA_OPTS);
656 780
657 printf (UT_HELP_VRSN); 781 printf(UT_HOST_PORT, 'p', "none");
658 printf (UT_EXTRA_OPTS); 782
659 783 printf(UT_IPv46);
660 printf (UT_HOST_PORT, 'p', "none"); 784
661 785 printf(" %s\n", "-E, --escape");
662 printf (UT_IPv46); 786 printf(" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before "
663 787 "send or quit option"));
664 printf (" %s\n", "-E, --escape"); 788 printf(" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit"));
665 printf (" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before send or quit option")); 789 printf(" %s\n", "-s, --send=STRING");
666 printf (" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit")); 790 printf(" %s\n", _("String to send to the server"));
667 printf (" %s\n", "-s, --send=STRING"); 791 printf(" %s\n", "-e, --expect=STRING");
668 printf (" %s\n", _("String to send to the server")); 792 printf(" %s %s\n", _("String to expect in server response"), _("(may be repeated)"));
669 printf (" %s\n", "-e, --expect=STRING"); 793 printf(" %s\n", "-A, --all");
670 printf (" %s %s\n", _("String to expect in server response"), _("(may be repeated)")); 794 printf(" %s\n", _("All expect strings need to occur in server response. Default is any"));
671 printf (" %s\n", "-A, --all"); 795 printf(" %s\n", "-q, --quit=STRING");
672 printf (" %s\n", _("All expect strings need to occur in server response. Default is any")); 796 printf(" %s\n", _("String to send server to initiate a clean close of the connection"));
673 printf (" %s\n", "-q, --quit=STRING"); 797 printf(" %s\n", "-r, --refuse=ok|warn|crit");
674 printf (" %s\n", _("String to send server to initiate a clean close of the connection")); 798 printf(" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)"));
675 printf (" %s\n", "-r, --refuse=ok|warn|crit"); 799 printf(" %s\n", "-M, --mismatch=ok|warn|crit");
676 printf (" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)")); 800 printf(" %s\n",
677 printf (" %s\n", "-M, --mismatch=ok|warn|crit"); 801 _("Accept expected string mismatches with states ok, warn, crit (default: warn)"));
678 printf (" %s\n", _("Accept expected string mismatches with states ok, warn, crit (default: warn)")); 802 printf(" %s\n", "-j, --jail");
679 printf (" %s\n", "-j, --jail"); 803 printf(" %s\n", _("Hide output from TCP socket"));
680 printf (" %s\n", _("Hide output from TCP socket")); 804 printf(" %s\n", "-m, --maxbytes=INTEGER");
681 printf (" %s\n", "-m, --maxbytes=INTEGER"); 805 printf(" %s\n", _("Close connection once more than this number of bytes are received"));
682 printf (" %s\n", _("Close connection once more than this number of bytes are received")); 806 printf(" %s\n", "-d, --delay=INTEGER");
683 printf (" %s\n", "-d, --delay=INTEGER"); 807 printf(" %s\n", _("Seconds to wait between sending string and polling for response"));
684 printf (" %s\n", _("Seconds to wait between sending string and polling for response"));
685 808
686#ifdef HAVE_SSL 809#ifdef HAVE_SSL
687 printf (" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); 810 printf(" %s\n", "-D, --certificate=INTEGER[,INTEGER]");
688 printf (" %s\n", _("Minimum number of days a certificate has to be valid.")); 811 printf(" %s\n", _("Minimum number of days a certificate has to be valid."));
689 printf (" %s\n", _("1st is #days for warning, 2nd is critical (if not specified - 0).")); 812 printf(" %s\n", _("1st is #days for warning, 2nd is critical (if not specified - 0)."));
690 printf (" %s\n", "-S, --ssl"); 813 printf(" %s\n", "-S, --ssl");
691 printf (" %s\n", _("Use SSL for the connection.")); 814 printf(" %s\n", _("Use SSL for the connection."));
692 printf (" %s\n", "--sni=STRING"); 815 printf(" %s\n", "--sni=STRING");
693 printf (" %s\n", _("SSL server_name")); 816 printf(" %s\n", _("SSL server_name"));
694#endif 817#endif
695 818
696 printf (UT_WARN_CRIT); 819 printf(UT_WARN_CRIT);
697 820
698 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 821 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
699 822
700 printf (UT_VERBOSE); 823 printf(UT_OUTPUT_FORMAT);
824 printf(UT_VERBOSE);
701 825
702 printf (UT_SUPPORT); 826 printf(UT_SUPPORT);
703} 827}
704 828
705 829void print_usage(void) {
706void 830 printf("%s\n", _("Usage:"));
707print_usage (void) 831 printf("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n",
708{ 832 progname);
709 printf ("%s\n", _("Usage:")); 833 printf("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n");
710 printf ("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n",progname); 834 printf("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n");
711 printf ("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n"); 835 printf("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n");
712 printf ("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n");
713 printf ("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n");
714} 836}
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 f50ea427..aec995d4 100644
--- a/plugins/check_time.c
+++ b/plugins/check_time.c
@@ -1,374 +1,357 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_time plugin 3 * Monitoring check_time plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 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_time plugin 10 * This file contains the check_time plugin
11* 11 *
12* This plugin will check the time difference with the specified host. 12 * This plugin will check the time difference with the specified host.
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
31#include "states.h"
31const char *progname = "check_time"; 32const char *progname = "check_time";
32const char *copyright = "1999-2007"; 33const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
34 35
35#include "common.h" 36#include "common.h"
36#include "netutils.h" 37#include "netutils.h"
37#include "utils.h" 38#include "utils.h"
39#include "check_time.d/config.h"
40
41#define UNIX_EPOCH 2208988800UL
42
43typedef struct {
44 int errorcode;
45 check_time_config config;
46} check_time_config_wrapper;
47static check_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
48static void print_help(void);
49void print_usage(void);
38 50
39enum { 51int main(int argc, char **argv) {
40 TIME_PORT = 37 52 setlocale(LC_ALL, "");
41}; 53 bindtextdomain(PACKAGE, LOCALEDIR);
42 54 textdomain(PACKAGE);
43#define UNIX_EPOCH 2208988800UL
44
45uint32_t raw_server_time;
46unsigned long server_time, diff_time;
47int warning_time = 0;
48bool check_warning_time = false;
49int critical_time = 0;
50bool check_critical_time = false;
51unsigned long warning_diff = 0;
52bool check_warning_diff = false;
53unsigned long critical_diff = 0;
54bool check_critical_diff = false;
55int server_port = TIME_PORT;
56char *server_address = NULL;
57bool use_udp = false;
58
59int process_arguments (int, char **);
60void print_help (void);
61void print_usage (void);
62
63int
64main (int argc, char **argv)
65{
66 int sd;
67 int result = STATE_UNKNOWN;
68 time_t conntime;
69
70 setlocale (LC_ALL, "");
71 bindtextdomain (PACKAGE, LOCALEDIR);
72 textdomain (PACKAGE);
73 55
74 /* Parse extra opts if any */ 56 /* Parse extra opts if any */
75 argv=np_extra_opts (&argc, argv, progname); 57 argv = np_extra_opts(&argc, argv, progname);
58
59 check_time_config_wrapper tmp_config = process_arguments(argc, argv);
60 if (tmp_config.errorcode == ERROR) {
61 usage4(_("Could not parse arguments"));
62 }
76 63
77 if (process_arguments (argc, argv) == ERROR) 64 const check_time_config config = tmp_config.config;
78 usage4 (_("Could not parse arguments"));
79 65
80 /* initialize alarm signal handling */ 66 /* initialize alarm signal handling */
81 signal (SIGALRM, socket_timeout_alarm_handler); 67 signal(SIGALRM, socket_timeout_alarm_handler);
82 68
83 /* set socket timeout */ 69 /* set socket timeout */
84 alarm (socket_timeout); 70 alarm(socket_timeout);
85 time (&start_time); 71 time_t start_time;
72 time(&start_time);
86 73
74 int socket;
75 mp_state_enum result = STATE_UNKNOWN;
87 /* try to connect to the host at the given port number */ 76 /* try to connect to the host at the given port number */
88 if (use_udp) { 77 if (config.use_udp) {
89 result = my_udp_connect (server_address, server_port, &sd); 78 result = my_udp_connect(config.server_address, config.server_port, &socket);
90 } else { 79 } else {
91 result = my_tcp_connect (server_address, server_port, &sd); 80 result = my_tcp_connect(config.server_address, config.server_port, &socket);
92 } 81 }
93 82
94 if (result != STATE_OK) { 83 if (result != STATE_OK) {
95 if (check_critical_time) 84 if (config.check_critical_time) {
96 result = STATE_CRITICAL; 85 result = STATE_CRITICAL;
97 else if (check_warning_time) 86 } else if (config.check_warning_time) {
98 result = STATE_WARNING; 87 result = STATE_WARNING;
99 else 88 } else {
100 result = STATE_UNKNOWN; 89 result = STATE_UNKNOWN;
101 die (result, 90 }
102 _("TIME UNKNOWN - could not connect to server %s, port %d\n"), 91 die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"),
103 server_address, server_port); 92 config.server_address, config.server_port);
104 } 93 }
105 94
106 if (use_udp) { 95 if (config.use_udp) {
107 if (send (sd, "", 0, 0) < 0) { 96 if (send(socket, "", 0, 0) < 0) {
108 if (check_critical_time) 97 if (config.check_critical_time) {
109 result = STATE_CRITICAL; 98 result = STATE_CRITICAL;
110 else if (check_warning_time) 99 } else if (config.check_warning_time) {
111 result = STATE_WARNING; 100 result = STATE_WARNING;
112 else 101 } else {
113 result = STATE_UNKNOWN; 102 result = STATE_UNKNOWN;
114 die (result, 103 }
115 _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"), 104 die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"),
116 server_address, server_port); 105 config.server_address, config.server_port);
117 } 106 }
118 } 107 }
119 108
120 /* watch for the connection string */ 109 /* watch for the connection string */
121 result = recv (sd, (void *)&raw_server_time, sizeof (raw_server_time), 0); 110 uint32_t raw_server_time;
111 result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0);
122 112
123 /* close the connection */ 113 /* close the connection */
124 close (sd); 114 close(socket);
125 115
126 /* reset the alarm */ 116 /* reset the alarm */
127 time (&end_time); 117 time_t end_time;
128 alarm (0); 118 time(&end_time);
119 alarm(0);
129 120
130 /* return a WARNING status if we couldn't read any data */ 121 /* return a WARNING status if we couldn't read any data */
131 if (result <= 0) { 122 if (result <= 0) {
132 if (check_critical_time) 123 if (config.check_critical_time) {
133 result = STATE_CRITICAL; 124 result = STATE_CRITICAL;
134 else if (check_warning_time) 125 } else if (config.check_warning_time) {
135 result = STATE_WARNING; 126 result = STATE_WARNING;
136 else 127 } else {
137 result = STATE_UNKNOWN; 128 result = STATE_UNKNOWN;
138 die (result, 129 }
139 _("TIME UNKNOWN - no data received from server %s, port %d\n"), 130 die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"),
140 server_address, server_port); 131 config.server_address, config.server_port);
141 } 132 }
142 133
143 result = STATE_OK; 134 result = STATE_OK;
144 135
145 conntime = (end_time - start_time); 136 time_t conntime = (end_time - start_time);
146 if (check_critical_time&& conntime > critical_time) 137 if (config.check_critical_time && conntime > config.critical_time) {
147 result = STATE_CRITICAL; 138 result = STATE_CRITICAL;
148 else if (check_warning_time && conntime > warning_time) 139 } else if (config.check_warning_time && conntime > config.warning_time) {
149 result = STATE_WARNING; 140 result = STATE_WARNING;
141 }
150 142
151 if (result != STATE_OK) 143 if (result != STATE_OK) {
152 die (result, _("TIME %s - %d second response time|%s\n"), 144 die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime,
153 state_text (result), (int)conntime, 145 perfdata("time", (long)conntime, "s", config.check_warning_time,
154 perfdata ("time", (long)conntime, "s", 146 (long)config.warning_time, config.check_critical_time,
155 check_warning_time, (long)warning_time, 147 (long)config.critical_time, true, 0, false, 0));
156 check_critical_time, (long)critical_time, 148 }
157 true, 0, false, 0));
158 149
159 server_time = ntohl (raw_server_time) - UNIX_EPOCH; 150 unsigned long server_time;
160 if (server_time > (unsigned long)end_time) 151 unsigned long diff_time;
152 server_time = ntohl(raw_server_time) - UNIX_EPOCH;
153 if (server_time > (unsigned long)end_time) {
161 diff_time = server_time - (unsigned long)end_time; 154 diff_time = server_time - (unsigned long)end_time;
162 else 155 } else {
163 diff_time = (unsigned long)end_time - server_time; 156 diff_time = (unsigned long)end_time - server_time;
157 }
164 158
165 if (check_critical_diff&& diff_time > critical_diff) 159 if (config.check_critical_diff && diff_time > config.critical_diff) {
166 result = STATE_CRITICAL; 160 result = STATE_CRITICAL;
167 else if (check_warning_diff&& diff_time > warning_diff) 161 } else if (config.check_warning_diff && diff_time > config.warning_diff) {
168 result = STATE_WARNING; 162 result = STATE_WARNING;
163 }
169 164
170 printf (_("TIME %s - %lu second time difference|%s %s\n"), 165 printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time,
171 state_text (result), diff_time, 166 perfdata("time", (long)conntime, "s", config.check_warning_time,
172 perfdata ("time", (long)conntime, "s", 167 (long)config.warning_time, config.check_critical_time,
173 check_warning_time, (long)warning_time, 168 (long)config.critical_time, true, 0, false, 0),
174 check_critical_time, (long)critical_time, 169 perfdata("offset", diff_time, "s", config.check_warning_diff, config.warning_diff,
175 true, 0, false, 0), 170 config.check_critical_diff, config.critical_diff, true, 0, false, 0));
176 perfdata ("offset", diff_time, "s",
177 check_warning_diff, warning_diff,
178 check_critical_diff, critical_diff,
179 true, 0, false, 0));
180 return result; 171 return result;
181} 172}
182 173
183
184
185/* process command-line arguments */ 174/* process command-line arguments */
186int 175check_time_config_wrapper process_arguments(int argc, char **argv) {
187process_arguments (int argc, char **argv) 176 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
188{ 177 {"warning-variance", required_argument, 0, 'w'},
189 int c; 178 {"critical-variance", required_argument, 0, 'c'},
190 179 {"warning-connect", required_argument, 0, 'W'},
191 int option = 0; 180 {"critical-connect", required_argument, 0, 'C'},
192 static struct option longopts[] = { 181 {"port", required_argument, 0, 'p'},
193 {"hostname", required_argument, 0, 'H'}, 182 {"udp", no_argument, 0, 'u'},
194 {"warning-variance", required_argument, 0, 'w'}, 183 {"timeout", required_argument, 0, 't'},
195 {"critical-variance", required_argument, 0, 'c'}, 184 {"version", no_argument, 0, 'V'},
196 {"warning-connect", required_argument, 0, 'W'}, 185 {"help", no_argument, 0, 'h'},
197 {"critical-connect", required_argument, 0, 'C'}, 186 {0, 0, 0, 0}};
198 {"port", required_argument, 0, 'p'}, 187
199 {"udp", no_argument, 0, 'u'}, 188 if (argc < 2) {
200 {"timeout", required_argument, 0, 't'}, 189 usage("\n");
201 {"version", no_argument, 0, 'V'}, 190 }
202 {"help", no_argument, 0, 'h'},
203 {0, 0, 0, 0}
204 };
205 191
206 if (argc < 2) 192 for (int i = 1; i < argc; i++) {
207 usage ("\n"); 193 if (strcmp("-to", argv[i]) == 0) {
208 194 strcpy(argv[i], "-t");
209 for (c = 1; c < argc; c++) { 195 } else if (strcmp("-wd", argv[i]) == 0) {
210 if (strcmp ("-to", argv[c]) == 0) 196 strcpy(argv[i], "-w");
211 strcpy (argv[c], "-t"); 197 } else if (strcmp("-cd", argv[i]) == 0) {
212 else if (strcmp ("-wd", argv[c]) == 0) 198 strcpy(argv[i], "-c");
213 strcpy (argv[c], "-w"); 199 } else if (strcmp("-wt", argv[i]) == 0) {
214 else if (strcmp ("-cd", argv[c]) == 0) 200 strcpy(argv[i], "-W");
215 strcpy (argv[c], "-c"); 201 } else if (strcmp("-ct", argv[i]) == 0) {
216 else if (strcmp ("-wt", argv[c]) == 0) 202 strcpy(argv[i], "-C");
217 strcpy (argv[c], "-W"); 203 }
218 else if (strcmp ("-ct", argv[c]) == 0)
219 strcpy (argv[c], "-C");
220 } 204 }
221 205
206 check_time_config_wrapper result = {
207 .errorcode = OK,
208 .config = check_time_config_init(),
209 };
210
211 int option_char;
222 while (true) { 212 while (true) {
223 c = getopt_long (argc, argv, "hVH:w:c:W:C:p:t:u", longopts, 213 int option = 0;
224 &option); 214 option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option);
225 215
226 if (c == -1 || c == EOF) 216 if (CHECK_EOF(option_char)) {
227 break; 217 break;
218 }
228 219
229 switch (c) { 220 switch (option_char) {
230 case '?': /* print short usage statement if args not parsable */ 221 case '?': /* print short usage statement if args not parsable */
231 usage5 (); 222 usage5();
232 case 'h': /* help */ 223 case 'h': /* help */
233 print_help (); 224 print_help();
234 exit (STATE_UNKNOWN); 225 exit(STATE_UNKNOWN);
235 case 'V': /* version */ 226 case 'V': /* version */
236 print_revision (progname, NP_VERSION); 227 print_revision(progname, NP_VERSION);
237 exit (STATE_UNKNOWN); 228 exit(STATE_UNKNOWN);
238 case 'H': /* hostname */ 229 case 'H': /* hostname */
239 if (!is_host (optarg)) 230 if (!is_host(optarg)) {
240 usage2 (_("Invalid hostname/address"), optarg); 231 usage2(_("Invalid hostname/address"), optarg);
241 server_address = optarg;
242 break;
243 case 'w': /* warning-variance */
244 if (is_intnonneg (optarg)) {
245 warning_diff = strtoul (optarg, NULL, 10);
246 check_warning_diff = true;
247 } 232 }
248 else if (strspn (optarg, "0123456789:,") > 0) { 233 result.config.server_address = optarg;
249 if (sscanf (optarg, "%lu%*[:,]%d", &warning_diff, &warning_time) == 2) { 234 break;
250 check_warning_diff = true; 235 case 'w': /* warning-variance */
251 check_warning_time = true; 236 if (is_intnonneg(optarg)) {
252 } 237 result.config.warning_diff = strtoul(optarg, NULL, 10);
253 else { 238 result.config.check_warning_diff = true;
254 usage4 (_("Warning thresholds must be a positive integer")); 239 } else if (strspn(optarg, "0123456789:,") > 0) {
240 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.warning_diff,
241 &result.config.warning_time) == 2) {
242 result.config.check_warning_diff = true;
243 result.config.check_warning_time = true;
244 } else {
245 usage4(_("Warning thresholds must be a positive integer"));
255 } 246 }
256 } 247 } else {
257 else { 248 usage4(_("Warning threshold must be a positive integer"));
258 usage4 (_("Warning threshold must be a positive integer"));
259 } 249 }
260 break; 250 break;
261 case 'c': /* critical-variance */ 251 case 'c': /* critical-variance */
262 if (is_intnonneg (optarg)) { 252 if (is_intnonneg(optarg)) {
263 critical_diff = strtoul (optarg, NULL, 10); 253 result.config.critical_diff = strtoul(optarg, NULL, 10);
264 check_critical_diff = true; 254 result.config.check_critical_diff = true;
265 } 255 } else if (strspn(optarg, "0123456789:,") > 0) {
266 else if (strspn (optarg, "0123456789:,") > 0) { 256 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.critical_diff,
267 if (sscanf (optarg, "%lu%*[:,]%d", &critical_diff, &critical_time) == 257 &result.config.critical_time) == 2) {
268 2) { 258 result.config.check_critical_diff = true;
269 check_critical_diff = true; 259 result.config.check_critical_time = true;
270 check_critical_time = true; 260 } else {
271 } 261 usage4(_("Critical thresholds must be a positive integer"));
272 else {
273 usage4 (_("Critical thresholds must be a positive integer"));
274 } 262 }
275 } 263 } else {
276 else { 264 usage4(_("Critical threshold must be a positive integer"));
277 usage4 (_("Critical threshold must be a positive integer"));
278 } 265 }
279 break; 266 break;
280 case 'W': /* warning-connect */ 267 case 'W': /* warning-connect */
281 if (!is_intnonneg (optarg)) 268 if (!is_intnonneg(optarg)) {
282 usage4 (_("Warning threshold must be a positive integer")); 269 usage4(_("Warning threshold must be a positive integer"));
283 else 270 } else {
284 warning_time = atoi (optarg); 271 result.config.warning_time = atoi(optarg);
285 check_warning_time = true; 272 }
273 result.config.check_warning_time = true;
286 break; 274 break;
287 case 'C': /* critical-connect */ 275 case 'C': /* critical-connect */
288 if (!is_intnonneg (optarg)) 276 if (!is_intnonneg(optarg)) {
289 usage4 (_("Critical threshold must be a positive integer")); 277 usage4(_("Critical threshold must be a positive integer"));
290 else 278 } else {
291 critical_time = atoi (optarg); 279 result.config.critical_time = atoi(optarg);
292 check_critical_time = true; 280 }
281 result.config.check_critical_time = true;
293 break; 282 break;
294 case 'p': /* port */ 283 case 'p': /* port */
295 if (!is_intnonneg (optarg)) 284 if (!is_intnonneg(optarg)) {
296 usage4 (_("Port must be a positive integer")); 285 usage4(_("Port must be a positive integer"));
297 else 286 } else {
298 server_port = atoi (optarg); 287 result.config.server_port = atoi(optarg);
288 }
299 break; 289 break;
300 case 't': /* timeout */ 290 case 't': /* timeout */
301 if (!is_intnonneg (optarg)) 291 if (!is_intnonneg(optarg)) {
302 usage2 (_("Timeout interval must be a positive integer"), optarg); 292 usage2(_("Timeout interval must be a positive integer"), optarg);
303 else 293 } else {
304 socket_timeout = atoi (optarg); 294 socket_timeout = atoi(optarg);
295 }
305 break; 296 break;
306 case 'u': /* udp */ 297 case 'u': /* udp */
307 use_udp = true; 298 result.config.use_udp = true;
308 } 299 }
309 } 300 }
310 301
311 c = optind; 302 option_char = optind;
312 if (server_address == NULL) { 303 if (result.config.server_address == NULL) {
313 if (argc > c) { 304 if (argc > option_char) {
314 if (!is_host (argv[c])) 305 if (!is_host(argv[option_char])) {
315 usage2 (_("Invalid hostname/address"), optarg); 306 usage2(_("Invalid hostname/address"), optarg);
316 server_address = argv[c]; 307 }
317 } 308 result.config.server_address = argv[option_char];
318 else { 309 } else {
319 usage4 (_("Hostname was not supplied")); 310 usage4(_("Hostname was not supplied"));
320 } 311 }
321 } 312 }
322 313
323 return OK; 314 return result;
324} 315}
325 316
326 317void print_help(void) {
327
328void
329print_help (void)
330{
331 char *myport; 318 char *myport;
332 xasprintf (&myport, "%d", TIME_PORT); 319 xasprintf(&myport, "%d", TIME_PORT);
333 320
334 print_revision (progname, NP_VERSION); 321 print_revision(progname, NP_VERSION);
335 322
336 printf ("Copyright (c) 1999 Ethan Galstad\n"); 323 printf("Copyright (c) 1999 Ethan Galstad\n");
337 printf (COPYRIGHT, copyright, email); 324 printf(COPYRIGHT, copyright, email);
338 325
339 printf ("%s\n", _("This plugin will check the time on the specified host.")); 326 printf("%s\n", _("This plugin will check the time on the specified host."));
340 327
341 printf ("\n\n"); 328 printf("\n\n");
342 329
343 print_usage (); 330 print_usage();
344 331
345 printf (UT_HELP_VRSN); 332 printf(UT_HELP_VRSN);
346 printf (UT_EXTRA_OPTS); 333 printf(UT_EXTRA_OPTS);
347 334
348 printf (UT_HOST_PORT, 'p', myport); 335 printf(UT_HOST_PORT, 'p', myport);
349 336
350 printf (" %s\n", "-u, --udp"); 337 printf(" %s\n", "-u, --udp");
351 printf (" %s\n", _("Use UDP to connect, not TCP")); 338 printf(" %s\n", _("Use UDP to connect, not TCP"));
352 printf (" %s\n", "-w, --warning-variance=INTEGER"); 339 printf(" %s\n", "-w, --warning-variance=INTEGER");
353 printf (" %s\n", _("Time difference (sec.) necessary to result in a warning status")); 340 printf(" %s\n", _("Time difference (sec.) necessary to result in a warning status"));
354 printf (" %s\n", "-c, --critical-variance=INTEGER"); 341 printf(" %s\n", "-c, --critical-variance=INTEGER");
355 printf (" %s\n", _("Time difference (sec.) necessary to result in a critical status")); 342 printf(" %s\n", _("Time difference (sec.) necessary to result in a critical status"));
356 printf (" %s\n", "-W, --warning-connect=INTEGER"); 343 printf(" %s\n", "-W, --warning-connect=INTEGER");
357 printf (" %s\n", _("Response time (sec.) necessary to result in warning status")); 344 printf(" %s\n", _("Response time (sec.) necessary to result in warning status"));
358 printf (" %s\n", "-C, --critical-connect=INTEGER"); 345 printf(" %s\n", "-C, --critical-connect=INTEGER");
359 printf (" %s\n", _("Response time (sec.) necessary to result in critical status")); 346 printf(" %s\n", _("Response time (sec.) necessary to result in critical status"));
360 347
361 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 348 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
362 349
363 printf (UT_SUPPORT); 350 printf(UT_SUPPORT);
364} 351}
365 352
366 353void print_usage(void) {
367 354 printf("%s\n", _("Usage:"));
368void 355 printf("%s -H <host_address> [-p port] [-u] [-w variance] [-c variance]\n", progname);
369print_usage (void) 356 printf(" [-W connect_time] [-C connect_time] [-t timeout]\n");
370{
371 printf ("%s\n", _("Usage:"));
372 printf ("%s -H <host_address> [-p port] [-u] [-w variance] [-c variance]\n",progname);
373 printf (" [-W connect_time] [-C connect_time] [-t timeout]\n");
374} 357}
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 380ff3bc..54decce3 100644
--- a/plugins/check_ups.c
+++ b/plugins/check_ups.c
@@ -6,7 +6,7 @@
6 * Copyright (c) 2000 Tom Shields 6 * Copyright (c) 2000 Tom Shields
7 * 2004 Alain Richard <alain.richard@equation.fr> 7 * 2004 Alain Richard <alain.richard@equation.fr>
8 * 2004 Arnaud Quette <arnaud.quette@mgeups.com> 8 * 2004 Arnaud Quette <arnaud.quette@mgeups.com>
9 * Copyright (c) 2002-2023 Monitoring Plugins Development Team 9 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
10 * 10 *
11 * Description: 11 * Description:
12 * 12 *
@@ -33,100 +33,52 @@
33 *****************************************************************************/ 33 *****************************************************************************/
34 34
35const char *progname = "check_ups"; 35const char *progname = "check_ups";
36const char *copyright = "2000-2023"; 36const char *copyright = "2000-2024";
37const char *email = "devel@monitoring-plugins.org"; 37const char *email = "devel@monitoring-plugins.org";
38 38
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#include "check_ups.d/config.h"
43#include "states.h"
42 44
43enum { PORT = 3493 }; 45enum {
44 46 NOSUCHVAR = ERROR - 1
45#define UPS_NONE 0 /* no supported options */ 47};
46#define UPS_UTILITY 1 /* supports utility line */
47#define UPS_BATTPCT 2 /* supports percent battery remaining */
48#define UPS_STATUS 4 /* supports UPS status */
49#define UPS_TEMP 8 /* supports UPS temperature */
50#define UPS_LOADPCT 16 /* supports load percent */
51#define UPS_REALPOWER 32 /* supports real power */
52
53#define UPSSTATUS_NONE 0
54#define UPSSTATUS_OFF 1
55#define UPSSTATUS_OL 2
56#define UPSSTATUS_OB 4
57#define UPSSTATUS_LB 8
58#define UPSSTATUS_CAL 16
59#define UPSSTATUS_RB 32 /*Replace Battery */
60#define UPSSTATUS_BYPASS 64
61#define UPSSTATUS_OVER 128
62#define UPSSTATUS_TRIM 256
63#define UPSSTATUS_BOOST 512
64#define UPSSTATUS_CHRG 1024
65#define UPSSTATUS_DISCHRG 2048
66#define UPSSTATUS_UNKNOWN 4096
67#define UPSSTATUS_ALARM 8192
68
69enum { NOSUCHVAR = ERROR - 1 };
70
71typedef struct ups_config {
72 unsigned int server_port;
73 char *server_address;
74 char *ups_name;
75 double warning_value;
76 double critical_value;
77 bool check_warn;
78 bool check_crit;
79 int check_variable;
80 int status;
81 bool temp_output_c;
82} ups_config;
83
84ups_config ups_config_init(void) {
85 ups_config tmp = {0};
86 tmp.server_port = PORT;
87 tmp.server_address = NULL;
88 tmp.ups_name = NULL;
89 tmp.check_variable = UPS_NONE;
90 tmp.status = UPSSTATUS_NONE;
91
92 return tmp;
93}
94 48
95// Forward declarations 49// Forward declarations
96int determine_status(ups_config *, int *supported_options); 50typedef struct {
97int get_ups_variable(const char *, char *, const ups_config config); 51 int errorcode;
98 52 int ups_status;
99int process_arguments(int, char **, ups_config *); 53 int supported_options;
100int validate_arguments(ups_config); 54} determine_status_result;
101void print_help(void); 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*/);
64
65static void print_help(void);
102void print_usage(void); 66void print_usage(void);
103 67
104int main(int argc, char **argv) { 68int main(int argc, char **argv) {
105 setlocale(LC_ALL, ""); 69 setlocale(LC_ALL, "");
106 bindtextdomain(PACKAGE, LOCALEDIR); 70 bindtextdomain(PACKAGE, LOCALEDIR);
107 textdomain(PACKAGE); 71 textdomain(PACKAGE);
108
109 char *ups_status;
110 ups_status = strdup("N/A");
111
112 char *data;
113 data = strdup("");
114
115 char *message;
116 message = strdup("");
117
118 // Exit result
119 int result = STATE_UNKNOWN;
120
121 /* Parse extra opts if any */ 72 /* Parse extra opts if any */
122 argv = np_extra_opts(&argc, argv, progname); 73 argv = np_extra_opts(&argc, argv, progname);
123 74
124 // Config from commandline 75 check_ups_config_wrapper tmp_config = process_arguments(argc, argv);
125 ups_config config = ups_config_init();
126 76
127 if (process_arguments(argc, argv, &config) == ERROR) { 77 if (tmp_config.errorcode == ERROR) {
128 usage4(_("Could not parse arguments")); 78 usage4(_("Could not parse arguments"));
129 } 79 }
80 // Config from commandline
81 check_ups_config config = tmp_config.config;
130 82
131 /* initialize alarm signal handling */ 83 /* initialize alarm signal handling */
132 signal(SIGALRM, socket_timeout_alarm_handler); 84 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -134,74 +86,76 @@ int main(int argc, char **argv) {
134 /* set socket timeout */ 86 /* set socket timeout */
135 alarm(socket_timeout); 87 alarm(socket_timeout);
136 88
137 int supported_options = UPS_NONE;
138
139 /* get the ups status if possible */ 89 /* get the ups status if possible */
140 if (determine_status(&config, &supported_options) != OK) { 90 determine_status_result query_result = determine_status(config);
91 if (query_result.errorcode != OK) {
141 return STATE_CRITICAL; 92 return STATE_CRITICAL;
142 } 93 }
143 94
95 int ups_status_flags = query_result.ups_status;
96 int supported_options = query_result.supported_options;
144 97
145 if (supported_options & UPS_STATUS) { 98 // Exit result
146 99 mp_state_enum result = STATE_UNKNOWN;
147 ups_status = strdup(""); 100 char *message = NULL;
148 101
102 if (supported_options & UPS_STATUS) {
103 char *ups_status = strdup("");
149 result = STATE_OK; 104 result = STATE_OK;
150 105
151 if (config.status & UPSSTATUS_OFF) { 106 if (ups_status_flags & UPSSTATUS_OFF) {
152 xasprintf(&ups_status, "Off"); 107 xasprintf(&ups_status, "Off");
153 result = STATE_CRITICAL; 108 result = STATE_CRITICAL;
154 } else if ((config.status & (UPSSTATUS_OB | UPSSTATUS_LB)) == 109 } else if ((ups_status_flags & (UPSSTATUS_OB | UPSSTATUS_LB)) ==
155 (UPSSTATUS_OB | UPSSTATUS_LB)) { 110 (UPSSTATUS_OB | UPSSTATUS_LB)) {
156 xasprintf(&ups_status, _("On Battery, Low Battery")); 111 xasprintf(&ups_status, _("On Battery, Low Battery"));
157 result = STATE_CRITICAL; 112 result = STATE_CRITICAL;
158 } else { 113 } else {
159 if (config.status & UPSSTATUS_OL) { 114 if (ups_status_flags & UPSSTATUS_OL) {
160 xasprintf(&ups_status, "%s%s", ups_status, _("Online")); 115 xasprintf(&ups_status, "%s%s", ups_status, _("Online"));
161 } 116 }
162 if (config.status & UPSSTATUS_OB) { 117 if (ups_status_flags & UPSSTATUS_OB) {
163 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery")); 118 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery"));
164 result = max_state(result, STATE_WARNING); 119 result = max_state(result, STATE_WARNING);
165 } 120 }
166 if (config.status & UPSSTATUS_LB) { 121 if (ups_status_flags & UPSSTATUS_LB) {
167 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery")); 122 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery"));
168 result = max_state(result, STATE_WARNING); 123 result = max_state(result, STATE_WARNING);
169 } 124 }
170 if (config.status & UPSSTATUS_CAL) { 125 if (ups_status_flags & UPSSTATUS_CAL) {
171 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating")); 126 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating"));
172 } 127 }
173 if (config.status & UPSSTATUS_RB) { 128 if (ups_status_flags & UPSSTATUS_RB) {
174 xasprintf(&ups_status, "%s%s", ups_status, 129 xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery"));
175 _(", Replace Battery"));
176 result = max_state(result, STATE_WARNING); 130 result = max_state(result, STATE_WARNING);
177 } 131 }
178 if (config.status & UPSSTATUS_BYPASS) { 132 if (ups_status_flags & UPSSTATUS_BYPASS) {
179 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass")); 133 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass"));
180 // Bypassing the battery is likely a bad thing 134 // Bypassing the battery is likely a bad thing
181 result = STATE_CRITICAL; 135 result = STATE_CRITICAL;
182 } 136 }
183 if (config.status & UPSSTATUS_OVER) { 137 if (ups_status_flags & UPSSTATUS_OVER) {
184 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload")); 138 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload"));
185 result = max_state(result, STATE_WARNING); 139 result = max_state(result, STATE_WARNING);
186 } 140 }
187 if (config.status & UPSSTATUS_TRIM) { 141 if (ups_status_flags & UPSSTATUS_TRIM) {
188 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming")); 142 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming"));
189 } 143 }
190 if (config.status & UPSSTATUS_BOOST) { 144 if (ups_status_flags & UPSSTATUS_BOOST) {
191 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting")); 145 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting"));
192 } 146 }
193 if (config.status & UPSSTATUS_CHRG) { 147 if (ups_status_flags & UPSSTATUS_CHRG) {
194 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging")); 148 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging"));
195 } 149 }
196 if (config.status & UPSSTATUS_DISCHRG) { 150 if (ups_status_flags & UPSSTATUS_DISCHRG) {
197 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging")); 151 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging"));
198 result = max_state(result, STATE_WARNING); 152 result = max_state(result, STATE_WARNING);
199 } 153 }
200 if (config.status & UPSSTATUS_ALARM) { 154 if (ups_status_flags & UPSSTATUS_ALARM) {
201 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM")); 155 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM"));
202 result = STATE_CRITICAL; 156 result = STATE_CRITICAL;
203 } 157 }
204 if (config.status & UPSSTATUS_UNKNOWN) { 158 if (ups_status_flags & UPSSTATUS_UNKNOWN) {
205 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown")); 159 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown"));
206 } 160 }
207 } 161 }
@@ -210,7 +164,7 @@ int main(int argc, char **argv) {
210 164
211 int res; 165 int res;
212 char temp_buffer[MAX_INPUT_BUFFER]; 166 char temp_buffer[MAX_INPUT_BUFFER];
213 167 char *performance_data = strdup("");
214 /* get the ups utility voltage if possible */ 168 /* get the ups utility voltage if possible */
215 res = get_ups_variable("input.voltage", temp_buffer, config); 169 res = get_ups_variable("input.voltage", temp_buffer, config);
216 if (res == NOSUCHVAR) { 170 if (res == NOSUCHVAR) {
@@ -233,24 +187,20 @@ int main(int argc, char **argv) {
233 } 187 }
234 188
235 if (config.check_variable == UPS_UTILITY) { 189 if (config.check_variable == UPS_UTILITY) {
236 if (config.check_crit && 190 if (config.check_crit && ups_utility_deviation >= config.critical_value) {
237 ups_utility_deviation >= config.critical_value) {
238 result = STATE_CRITICAL; 191 result = STATE_CRITICAL;
239 } else if (config.check_warn && 192 } else if (config.check_warn && ups_utility_deviation >= config.warning_value) {
240 ups_utility_deviation >= config.warning_value) {
241 result = max_state(result, STATE_WARNING); 193 result = max_state(result, STATE_WARNING);
242 } 194 }
243 xasprintf(&data, "%s", 195 xasprintf(&performance_data, "%s",
244 perfdata("voltage", (long)(1000 * ups_utility_voltage), 196 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV",
245 "mV", config.check_warn, 197 config.check_warn, (long)(1000 * config.warning_value),
246 (long)(1000 * config.warning_value), 198 config.check_crit, (long)(1000 * config.critical_value), true, 0,
247 config.check_crit,
248 (long)(1000 * config.critical_value), true, 0,
249 false, 0)); 199 false, 0));
250 } else { 200 } else {
251 xasprintf(&data, "%s", 201 xasprintf(&performance_data, "%s",
252 perfdata("voltage", (long)(1000 * ups_utility_voltage), 202 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false,
253 "mV", false, 0, false, 0, true, 0, false, 0)); 203 0, true, 0, false, 0));
254 } 204 }
255 } 205 }
256 206
@@ -268,22 +218,19 @@ int main(int argc, char **argv) {
268 xasprintf(&message, "%sBatt=%3.1f%% ", message, ups_battery_percent); 218 xasprintf(&message, "%sBatt=%3.1f%% ", message, ups_battery_percent);
269 219
270 if (config.check_variable == UPS_BATTPCT) { 220 if (config.check_variable == UPS_BATTPCT) {
271 if (config.check_crit && 221 if (config.check_crit && ups_battery_percent <= config.critical_value) {
272 ups_battery_percent <= config.critical_value) {
273 result = STATE_CRITICAL; 222 result = STATE_CRITICAL;
274 } else if (config.check_warn && 223 } else if (config.check_warn && ups_battery_percent <= config.warning_value) {
275 ups_battery_percent <= config.warning_value) {
276 result = max_state(result, STATE_WARNING); 224 result = max_state(result, STATE_WARNING);
277 } 225 }
278 xasprintf(&data, "%s %s", data, 226 xasprintf(&performance_data, "%s %s", performance_data,
279 perfdata("battery", (long)ups_battery_percent, "%", 227 perfdata("battery", (long)ups_battery_percent, "%", config.check_warn,
280 config.check_warn, (long)(config.warning_value), 228 (long)(config.warning_value), config.check_crit,
281 config.check_crit, (long)(config.critical_value), 229 (long)(config.critical_value), true, 0, true, 100));
282 true, 0, true, 100));
283 } else { 230 } else {
284 xasprintf(&data, "%s %s", data, 231 xasprintf(&performance_data, "%s %s", performance_data,
285 perfdata("battery", (long)ups_battery_percent, "%", false, 232 perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true,
286 0, false, 0, true, 0, true, 100)); 233 0, true, 100));
287 } 234 }
288 } 235 }
289 236
@@ -301,22 +248,19 @@ int main(int argc, char **argv) {
301 xasprintf(&message, "%sLoad=%3.1f%% ", message, ups_load_percent); 248 xasprintf(&message, "%sLoad=%3.1f%% ", message, ups_load_percent);
302 249
303 if (config.check_variable == UPS_LOADPCT) { 250 if (config.check_variable == UPS_LOADPCT) {
304 if (config.check_crit && 251 if (config.check_crit && ups_load_percent >= config.critical_value) {
305 ups_load_percent >= config.critical_value) {
306 result = STATE_CRITICAL; 252 result = STATE_CRITICAL;
307 } else if (config.check_warn && 253 } else if (config.check_warn && ups_load_percent >= config.warning_value) {
308 ups_load_percent >= config.warning_value) {
309 result = max_state(result, STATE_WARNING); 254 result = max_state(result, STATE_WARNING);
310 } 255 }
311 xasprintf(&data, "%s %s", data, 256 xasprintf(&performance_data, "%s %s", performance_data,
312 perfdata("load", (long)ups_load_percent, "%", 257 perfdata("load", (long)ups_load_percent, "%", config.check_warn,
313 config.check_warn, (long)(config.warning_value), 258 (long)(config.warning_value), config.check_crit,
314 config.check_crit, (long)(config.critical_value), 259 (long)(config.critical_value), true, 0, true, 100));
315 true, 0, true, 100));
316 } else { 260 } else {
317 xasprintf(&data, "%s %s", data, 261 xasprintf(&performance_data, "%s %s", performance_data,
318 perfdata("load", (long)ups_load_percent, "%", false, 0, 262 perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0,
319 false, 0, true, 0, true, 100)); 263 true, 100));
320 } 264 }
321 } 265 }
322 266
@@ -345,19 +289,17 @@ int main(int argc, char **argv) {
345 if (config.check_variable == UPS_TEMP) { 289 if (config.check_variable == UPS_TEMP) {
346 if (config.check_crit && ups_temperature >= config.critical_value) { 290 if (config.check_crit && ups_temperature >= config.critical_value) {
347 result = STATE_CRITICAL; 291 result = STATE_CRITICAL;
348 } else if (config.check_warn && 292 } else if (config.check_warn && ups_temperature >= config.warning_value) {
349 ups_temperature >= config.warning_value) {
350 result = max_state(result, STATE_WARNING); 293 result = max_state(result, STATE_WARNING);
351 } 294 }
352 xasprintf(&data, "%s %s", data, 295 xasprintf(&performance_data, "%s %s", performance_data,
353 perfdata("temp", (long)ups_temperature, tunits, 296 perfdata("temp", (long)ups_temperature, tunits, config.check_warn,
354 config.check_warn, (long)(config.warning_value), 297 (long)(config.warning_value), config.check_crit,
355 config.check_crit, (long)(config.critical_value), 298 (long)(config.critical_value), true, 0, false, 0));
356 true, 0, false, 0));
357 } else { 299 } else {
358 xasprintf(&data, "%s %s", data, 300 xasprintf(&performance_data, "%s %s", performance_data,
359 perfdata("temp", (long)ups_temperature, tunits, false, 0, 301 perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0,
360 false, 0, true, 0, false, 0)); 302 false, 0));
361 } 303 }
362 } 304 }
363 305
@@ -376,19 +318,17 @@ int main(int argc, char **argv) {
376 if (config.check_variable == UPS_REALPOWER) { 318 if (config.check_variable == UPS_REALPOWER) {
377 if (config.check_crit && ups_realpower >= config.critical_value) { 319 if (config.check_crit && ups_realpower >= config.critical_value) {
378 result = STATE_CRITICAL; 320 result = STATE_CRITICAL;
379 } else if (config.check_warn && 321 } else if (config.check_warn && ups_realpower >= config.warning_value) {
380 ups_realpower >= config.warning_value) {
381 result = max_state(result, STATE_WARNING); 322 result = max_state(result, STATE_WARNING);
382 } 323 }
383 xasprintf(&data, "%s %s", data, 324 xasprintf(&performance_data, "%s %s", performance_data,
384 perfdata("realpower", (long)ups_realpower, "W", 325 perfdata("realpower", (long)ups_realpower, "W", config.check_warn,
385 config.check_warn, (long)(config.warning_value), 326 (long)(config.warning_value), config.check_crit,
386 config.check_crit, (long)(config.critical_value), 327 (long)(config.critical_value), true, 0, false, 0));
387 true, 0, false, 0));
388 } else { 328 } else {
389 xasprintf(&data, "%s %s", data, 329 xasprintf(&performance_data, "%s %s", performance_data,
390 perfdata("realpower", (long)ups_realpower, "W", false, 0, 330 perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0,
391 false, 0, true, 0, false, 0)); 331 false, 0));
392 } 332 }
393 } 333 }
394 334
@@ -402,73 +342,79 @@ int main(int argc, char **argv) {
402 /* reset timeout */ 342 /* reset timeout */
403 alarm(0); 343 alarm(0);
404 344
405 printf("UPS %s - %s|%s\n", state_text(result), message, data); 345 printf("UPS %s - %s|%s\n", state_text(result), message, performance_data);
406 return result; 346 exit(result);
407} 347}
408 348
409/* determines what options are supported by the UPS */ 349/* determines what options are supported by the UPS */
410int determine_status(ups_config *config, int *supported_options) { 350determine_status_result determine_status(const check_ups_config config) {
411 char recv_buffer[MAX_INPUT_BUFFER]; 351
352 determine_status_result result = {
353 .errorcode = OK,
354 .ups_status = UPSSTATUS_NONE,
355 .supported_options = 0,
356 };
412 357
413 int res = get_ups_variable("ups.status", recv_buffer, *config); 358 char recv_buffer[MAX_INPUT_BUFFER];
359 int res = get_ups_variable("ups.status", recv_buffer, config);
414 if (res == NOSUCHVAR) { 360 if (res == NOSUCHVAR) {
415 return OK; 361 return result;
416 } 362 }
417 363
418 if (res != STATE_OK) { 364 if (res != STATE_OK) {
419 printf("%s\n", _("Invalid response received from host")); 365 printf("%s\n", _("Invalid response received from host"));
420 return ERROR; 366 result.errorcode = ERROR;
367 return result;
421 } 368 }
422 369
423 *supported_options |= UPS_STATUS; 370 result.supported_options |= UPS_STATUS;
424 371
425 char temp_buffer[MAX_INPUT_BUFFER]; 372 char temp_buffer[MAX_INPUT_BUFFER];
426 373
427 strcpy(temp_buffer, recv_buffer); 374 strcpy(temp_buffer, recv_buffer);
428 for (char *ptr = (char *)strtok(temp_buffer, " "); ptr != NULL; 375 for (char *ptr = strtok(temp_buffer, " "); ptr != NULL; ptr = strtok(NULL, " ")) {
429 ptr = (char *)strtok(NULL, " ")) {
430 if (!strcmp(ptr, "OFF")) { 376 if (!strcmp(ptr, "OFF")) {
431 config->status |= UPSSTATUS_OFF; 377 result.ups_status |= UPSSTATUS_OFF;
432 } else if (!strcmp(ptr, "OL")) { 378 } else if (!strcmp(ptr, "OL")) {
433 config->status |= UPSSTATUS_OL; 379 result.ups_status |= UPSSTATUS_OL;
434 } else if (!strcmp(ptr, "OB")) { 380 } else if (!strcmp(ptr, "OB")) {
435 config->status |= UPSSTATUS_OB; 381 result.ups_status |= UPSSTATUS_OB;
436 } else if (!strcmp(ptr, "LB")) { 382 } else if (!strcmp(ptr, "LB")) {
437 config->status |= UPSSTATUS_LB; 383 result.ups_status |= UPSSTATUS_LB;
438 } else if (!strcmp(ptr, "CAL")) { 384 } else if (!strcmp(ptr, "CAL")) {
439 config->status |= UPSSTATUS_CAL; 385 result.ups_status |= UPSSTATUS_CAL;
440 } else if (!strcmp(ptr, "RB")) { 386 } else if (!strcmp(ptr, "RB")) {
441 config->status |= UPSSTATUS_RB; 387 result.ups_status |= UPSSTATUS_RB;
442 } else if (!strcmp(ptr, "BYPASS")) { 388 } else if (!strcmp(ptr, "BYPASS")) {
443 config->status |= UPSSTATUS_BYPASS; 389 result.ups_status |= UPSSTATUS_BYPASS;
444 } else if (!strcmp(ptr, "OVER")) { 390 } else if (!strcmp(ptr, "OVER")) {
445 config->status |= UPSSTATUS_OVER; 391 result.ups_status |= UPSSTATUS_OVER;
446 } else if (!strcmp(ptr, "TRIM")) { 392 } else if (!strcmp(ptr, "TRIM")) {
447 config->status |= UPSSTATUS_TRIM; 393 result.ups_status |= UPSSTATUS_TRIM;
448 } else if (!strcmp(ptr, "BOOST")) { 394 } else if (!strcmp(ptr, "BOOST")) {
449 config->status |= UPSSTATUS_BOOST; 395 result.ups_status |= UPSSTATUS_BOOST;
450 } else if (!strcmp(ptr, "CHRG")) { 396 } else if (!strcmp(ptr, "CHRG")) {
451 config->status |= UPSSTATUS_CHRG; 397 result.ups_status |= UPSSTATUS_CHRG;
452 } else if (!strcmp(ptr, "DISCHRG")) { 398 } else if (!strcmp(ptr, "DISCHRG")) {
453 config->status |= UPSSTATUS_DISCHRG; 399 result.ups_status |= UPSSTATUS_DISCHRG;
454 } else if (!strcmp(ptr, "ALARM")) { 400 } else if (!strcmp(ptr, "ALARM")) {
455 config->status |= UPSSTATUS_ALARM; 401 result.ups_status |= UPSSTATUS_ALARM;
456 } else { 402 } else {
457 config->status |= UPSSTATUS_UNKNOWN; 403 result.ups_status |= UPSSTATUS_UNKNOWN;
458 } 404 }
459 } 405 }
460 406
461 return OK; 407 return result;
462} 408}
463 409
464/* gets a variable value for a specific UPS */ 410/* gets a variable value for a specific UPS */
465int get_ups_variable(const char *varname, char *buf, const ups_config config) { 411int get_ups_variable(const char *varname, char *buf, const check_ups_config config) {
466 char send_buffer[MAX_INPUT_BUFFER]; 412 char send_buffer[MAX_INPUT_BUFFER];
467 413
468 /* create the command string to send to the UPS daemon */ 414 /* create the command string to send to the UPS daemon */
469 /* Add LOGOUT to avoid read failure logs */ 415 /* Add LOGOUT to avoid read failure logs */
470 int res = snprintf(send_buffer, sizeof(send_buffer), 416 int res = snprintf(send_buffer, sizeof(send_buffer), "GET VAR %s %s\nLOGOUT\n", config.ups_name,
471 "GET VAR %s %s\nLOGOUT\n", config.ups_name, varname); 417 varname);
472 if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) { 418 if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) {
473 printf("%s\n", _("UPS name to long for buffer")); 419 printf("%s\n", _("UPS name to long for buffer"));
474 return ERROR; 420 return ERROR;
@@ -477,8 +423,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
477 char temp_buffer[MAX_INPUT_BUFFER]; 423 char temp_buffer[MAX_INPUT_BUFFER];
478 424
479 /* send the command to the daemon and get a response back */ 425 /* send the command to the daemon and get a response back */
480 if (process_tcp_request(config.server_address, config.server_port, 426 if (process_tcp_request(config.server_address, config.server_port, send_buffer, temp_buffer,
481 send_buffer, temp_buffer,
482 sizeof(temp_buffer)) != STATE_OK) { 427 sizeof(temp_buffer)) != STATE_OK) {
483 printf("%s\n", _("Invalid response received from host")); 428 printf("%s\n", _("Invalid response received from host"));
484 return ERROR; 429 return ERROR;
@@ -496,8 +441,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
496 ptr[len - 1] = 0; 441 ptr[len - 1] = 0;
497 } 442 }
498 if (strcmp(ptr, "ERR UNKNOWN-UPS") == 0) { 443 if (strcmp(ptr, "ERR UNKNOWN-UPS") == 0) {
499 printf(_("CRITICAL - no such UPS '%s' on that host\n"), 444 printf(_("CRITICAL - no such UPS '%s' on that host\n"), config.ups_name);
500 config.ups_name);
501 return ERROR; 445 return ERROR;
502 } 446 }
503 447
@@ -534,7 +478,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
534 [-wv warn_value] [-cv crit_value] [-to to_sec] */ 478 [-wv warn_value] [-cv crit_value] [-to to_sec] */
535 479
536/* process command-line arguments */ 480/* process command-line arguments */
537int process_arguments(int argc, char **argv, ups_config *config) { 481check_ups_config_wrapper process_arguments(int argc, char **argv) {
538 482
539 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 483 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
540 {"ups", required_argument, 0, 'u'}, 484 {"ups", required_argument, 0, 'u'},
@@ -548,8 +492,14 @@ int process_arguments(int argc, char **argv, ups_config *config) {
548 {"help", no_argument, 0, 'h'}, 492 {"help", no_argument, 0, 'h'},
549 {0, 0, 0, 0}}; 493 {0, 0, 0, 0}};
550 494
495 check_ups_config_wrapper result = {
496 .errorcode = OK,
497 .config = check_ups_config_init(),
498 };
499
551 if (argc < 2) { 500 if (argc < 2) {
552 return ERROR; 501 result.errorcode = ERROR;
502 return result;
553 } 503 }
554 504
555 int c; 505 int c;
@@ -576,52 +526,52 @@ int process_arguments(int argc, char **argv, ups_config *config) {
576 usage5(); 526 usage5();
577 case 'H': /* hostname */ 527 case 'H': /* hostname */
578 if (is_host(optarg)) { 528 if (is_host(optarg)) {
579 config->server_address = optarg; 529 result.config.server_address = optarg;
580 } else { 530 } else {
581 usage2(_("Invalid hostname/address"), optarg); 531 usage2(_("Invalid hostname/address"), optarg);
582 } 532 }
583 break; 533 break;
584 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for 534 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for
585 Fahrenheit) */ 535 Fahrenheit) */
586 config->temp_output_c = true; 536 result.config.temp_output_c = true;
587 break; 537 break;
588 case 'u': /* ups name */ 538 case 'u': /* ups name */
589 config->ups_name = optarg; 539 result.config.ups_name = optarg;
590 break; 540 break;
591 case 'p': /* port */ 541 case 'p': /* port */
592 if (is_intpos(optarg)) { 542 if (is_intpos(optarg)) {
593 config->server_port = atoi(optarg); 543 result.config.server_port = atoi(optarg);
594 } else { 544 } else {
595 usage2(_("Port must be a positive integer"), optarg); 545 usage2(_("Port must be a positive integer"), optarg);
596 } 546 }
597 break; 547 break;
598 case 'c': /* critical time threshold */ 548 case 'c': /* critical time threshold */
599 if (is_intnonneg(optarg)) { 549 if (is_intnonneg(optarg)) {
600 config->critical_value = atoi(optarg); 550 result.config.critical_value = atoi(optarg);
601 config->check_crit = true; 551 result.config.check_crit = true;
602 } else { 552 } else {
603 usage2(_("Critical time must be a positive integer"), optarg); 553 usage2(_("Critical time must be a positive integer"), optarg);
604 } 554 }
605 break; 555 break;
606 case 'w': /* warning time threshold */ 556 case 'w': /* warning time threshold */
607 if (is_intnonneg(optarg)) { 557 if (is_intnonneg(optarg)) {
608 config->warning_value = atoi(optarg); 558 result.config.warning_value = atoi(optarg);
609 config->check_warn = true; 559 result.config.check_warn = true;
610 } else { 560 } else {
611 usage2(_("Warning time must be a positive integer"), optarg); 561 usage2(_("Warning time must be a positive integer"), optarg);
612 } 562 }
613 break; 563 break;
614 case 'v': /* variable */ 564 case 'v': /* variable */
615 if (!strcmp(optarg, "LINE")) { 565 if (!strcmp(optarg, "LINE")) {
616 config->check_variable = UPS_UTILITY; 566 result.config.check_variable = UPS_UTILITY;
617 } else if (!strcmp(optarg, "TEMP")) { 567 } else if (!strcmp(optarg, "TEMP")) {
618 config->check_variable = UPS_TEMP; 568 result.config.check_variable = UPS_TEMP;
619 } else if (!strcmp(optarg, "BATTPCT")) { 569 } else if (!strcmp(optarg, "BATTPCT")) {
620 config->check_variable = UPS_BATTPCT; 570 result.config.check_variable = UPS_BATTPCT;
621 } else if (!strcmp(optarg, "LOADPCT")) { 571 } else if (!strcmp(optarg, "LOADPCT")) {
622 config->check_variable = UPS_LOADPCT; 572 result.config.check_variable = UPS_LOADPCT;
623 } else if (!strcmp(optarg, "REALPOWER")) { 573 } else if (!strcmp(optarg, "REALPOWER")) {
624 config->check_variable = UPS_REALPOWER; 574 result.config.check_variable = UPS_REALPOWER;
625 } else { 575 } else {
626 usage2(_("Unrecognized UPS variable"), optarg); 576 usage2(_("Unrecognized UPS variable"), optarg);
627 } 577 }
@@ -642,27 +592,27 @@ int process_arguments(int argc, char **argv, ups_config *config) {
642 } 592 }
643 } 593 }
644 594
645 if (config->server_address == NULL && argc > optind) { 595 if (result.config.server_address == NULL && argc > optind) {
646 if (is_host(argv[optind])) { 596 if (is_host(argv[optind])) {
647 config->server_address = argv[optind++]; 597 result.config.server_address = argv[optind++];
648 } else { 598 } else {
649 usage2(_("Invalid hostname/address"), optarg); 599 usage2(_("Invalid hostname/address"), optarg);
650 } 600 }
651 } 601 }
652 602
653 if (config->server_address == NULL) { 603 if (result.config.server_address == NULL) {
654 config->server_address = strdup("127.0.0.1"); 604 result.config.server_address = strdup("127.0.0.1");
655 } 605 }
656 606
657 return validate_arguments(*config); 607 return validate_arguments(result);
658} 608}
659 609
660int validate_arguments(ups_config config) { 610check_ups_config_wrapper validate_arguments(check_ups_config_wrapper config_wrapper) {
661 if (!config.ups_name) { 611 if (config_wrapper.config.ups_name) {
662 printf("%s\n", _("Error : no UPS indicated")); 612 printf("%s\n", _("Error : no UPS indicated"));
663 return ERROR; 613 config_wrapper.errorcode = ERROR;
664 } 614 }
665 return OK; 615 return config_wrapper;
666} 616}
667 617
668void print_help(void) { 618void print_help(void) {
@@ -731,8 +681,7 @@ void print_help(void) {
731 "with Russell Kroll's")); 681 "with Russell Kroll's"));
732 printf(" %s\n", _("Network UPS Tools be installed on the remote host. If " 682 printf(" %s\n", _("Network UPS Tools be installed on the remote host. If "
733 "you do not have the")); 683 "you do not have the"));
734 printf(" %s\n", 684 printf(" %s\n", _("package installed on your system, you can download it from"));
735 _("package installed on your system, you can download it from"));
736 printf(" %s\n", _("http://www.networkupstools.org")); 685 printf(" %s\n", _("http://www.networkupstools.org"));
737 686
738 printf(UT_SUPPORT); 687 printf(UT_SUPPORT);
diff --git a/plugins/check_ups.d/config.h b/plugins/check_ups.d/config.h
new file mode 100644
index 00000000..e05edceb
--- /dev/null
+++ b/plugins/check_ups.d/config.h
@@ -0,0 +1,54 @@
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}
diff --git a/plugins/check_users.c b/plugins/check_users.c
index 89b95369..4027d21a 100644
--- a/plugins/check_users.c
+++ b/plugins/check_users.c
@@ -1,282 +1,283 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_users plugin 3 * Monitoring check_users plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2012 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_users plugin 10 * This file contains the check_users plugin
11* 11 *
12* This plugin checks the number of users currently logged in on the local 12 * This plugin checks the number of users currently logged in on the local
13* system and generates an error if the number exceeds the thresholds 13 * system and generates an error if the number exceeds the thresholds
14* specified. 14 * specified.
15* 15 *
16* 16 *
17* This program is free software: you can redistribute it and/or modify 17 * This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by 18 * it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or 19 * the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version. 20 * (at your option) any later version.
21* 21 *
22* This program is distributed in the hope that it will be useful, 22 * This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details. 25 * GNU General Public License for more details.
26* 26 *
27* You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>. 28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29* 29 *
30* 30 *
31*****************************************************************************/ 31 *****************************************************************************/
32 32
33const char *progname = "check_users"; 33const char *progname = "check_users";
34const char *copyright = "2000-2007"; 34const char *copyright = "2000-2024";
35const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
36 36
37#include "common.h" 37#include "check_users.d/users.h"
38#include "utils.h" 38#include "output.h"
39#include "perfdata.h"
40#include "states.h"
41#include "utils_base.h"
42#include "./common.h"
43#include "./utils.h"
44#include "check_users.d/config.h"
45#include "thresholds.h"
39 46
40#if HAVE_WTSAPI32_H 47#if HAVE_WTSAPI32_H
41# include <windows.h> 48# include <windows.h>
42# include <wtsapi32.h> 49# include <wtsapi32.h>
43# undef ERROR 50# undef ERROR
44# define ERROR -1 51# define ERROR -1
45#elif HAVE_UTMPX_H 52#elif HAVE_UTMPX_H
46# include <utmpx.h> 53# include <utmpx.h>
47#else
48# include "popen.h"
49#endif 54#endif
50 55
51#ifdef HAVE_LIBSYSTEMD 56#ifdef HAVE_LIBSYSTEMD
52#include <systemd/sd-daemon.h> 57# include <systemd/sd-daemon.h>
53#include <systemd/sd-login.h> 58# include <systemd/sd-login.h>
54#endif 59#endif
55 60
56#define possibly_set(a,b) ((a) == 0 ? (b) : 0) 61typedef struct process_argument_wrapper {
57 62 int errorcode;
58int process_arguments (int, char **); 63 check_users_config config;
59void print_help (void); 64} check_users_config_wrapper;
60void print_usage (void); 65check_users_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
61
62char *warning_range = NULL;
63char *critical_range = NULL;
64thresholds *thlds = NULL;
65 66
66int 67void print_help(void);
67main (int argc, char **argv) 68void print_usage(void);
68{
69 int users = -1;
70 int result = STATE_UNKNOWN;
71#if HAVE_WTSAPI32_H
72 WTS_SESSION_INFO *wtsinfo;
73 DWORD wtscount;
74 DWORD index;
75#elif HAVE_UTMPX_H
76 struct utmpx *putmpx;
77#else
78 char input_buffer[MAX_INPUT_BUFFER];
79#endif
80 69
81 setlocale (LC_ALL, ""); 70int main(int argc, char **argv) {
82 bindtextdomain (PACKAGE, LOCALEDIR); 71 setlocale(LC_ALL, "");
83 textdomain (PACKAGE); 72 bindtextdomain(PACKAGE, LOCALEDIR);
73 textdomain(PACKAGE);
84 74
85 /* Parse extra opts if any */ 75 /* Parse extra opts if any */
86 argv = np_extra_opts (&argc, argv, progname); 76 argv = np_extra_opts(&argc, argv, progname);
87
88 if (process_arguments (argc, argv) == ERROR)
89 usage4 (_("Could not parse arguments"));
90 77
91 users = 0; 78 check_users_config_wrapper tmp_config = process_arguments(argc, argv);
92 79
93#ifdef HAVE_LIBSYSTEMD 80 if (tmp_config.errorcode == ERROR) {
94 if (sd_booted () > 0) 81 usage4(_("Could not parse arguments"));
95 users = sd_get_sessions (NULL);
96 else {
97#endif
98#if HAVE_WTSAPI32_H
99 if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE,
100 0, 1, &wtsinfo, &wtscount)) {
101 printf(_("Could not enumerate RD sessions: %d\n"), GetLastError());
102 return STATE_UNKNOWN;
103 } 82 }
104 83
105 for (index = 0; index < wtscount; index++) { 84 check_users_config config = tmp_config.config;
106 LPTSTR username;
107 DWORD size;
108 int len;
109
110 if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
111 wtsinfo[index].SessionId, WTSUserName, &username, &size))
112 continue;
113 85
114 len = lstrlen(username); 86#ifdef _WIN32
115 87# if HAVE_WTSAPI32_H
116 WTSFreeMemory(username); 88 get_num_of_users_wrapper user_wrapper = get_num_of_users_windows();
117 89# else
118 if (len == 0) 90# error Did not find WTSAPI32
119 continue; 91# endif // HAVE_WTSAPI32_H
120
121 if (wtsinfo[index].State == WTSActive ||
122 wtsinfo[index].State == WTSDisconnected)
123 users++;
124 }
125
126 WTSFreeMemory(wtsinfo);
127#elif HAVE_UTMPX_H
128 /* get currently logged users from utmpx */
129 setutxent ();
130
131 while ((putmpx = getutxent ()) != NULL)
132 if (putmpx->ut_type == USER_PROCESS)
133 users++;
134
135 endutxent ();
136#else 92#else
137 /* run the command */ 93# ifdef HAVE_LIBSYSTEMD
138 child_process = spopen (WHO_COMMAND); 94 get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd();
139 if (child_process == NULL) { 95# elif HAVE_UTMPX_H
140 printf (_("Could not open pipe: %s\n"), WHO_COMMAND); 96 get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp();
141 return STATE_UNKNOWN; 97# else // !HAVE_LIBSYSTEMD && !HAVE_UTMPX_H
98 get_num_of_users_wrapper user_wrapper = get_num_of_users_who_command();
99# endif // HAVE_LIBSYSTEMD
100#endif // _WIN32
101
102 mp_check overall = mp_check_init();
103 if (config.output_format_is_set) {
104 mp_set_format(config.output_format);
142 } 105 }
106 mp_subcheck sc_users = mp_subcheck_init();
143 107
144 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 108 if (user_wrapper.errorcode != 0) {
145 if (child_stderr == NULL) 109 sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN);
146 printf (_("Could not open stderr for %s\n"), WHO_COMMAND); 110 sc_users.output = "Failed to retrieve number of users";
147 111 mp_add_subcheck_to_check(&overall, sc_users);
148 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 112 mp_exit(overall);
149 /* increment 'users' on all lines except total user count */
150 if (input_buffer[0] != '#') {
151 users++;
152 continue;
153 }
154
155 /* get total logged in users */
156 if (sscanf (input_buffer, _("# users=%d"), &users) == 1)
157 break;
158 } 113 }
114 /* check the user count against warning and critical thresholds */
159 115
160 /* check STDERR */ 116 mp_perfdata users_pd = {
161 if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) 117 .label = "users",
162 result = possibly_set (result, STATE_UNKNOWN); 118 .value = mp_create_pd_value(user_wrapper.users),
163 (void) fclose (child_stderr); 119 };
164
165 /* close the pipe */
166 if (spclose (child_process))
167 result = possibly_set (result, STATE_UNKNOWN);
168#endif
169#ifdef HAVE_LIBSYSTEMD
170 }
171#endif
172 120
173 /* check the user count against warning and critical thresholds */ 121 users_pd = mp_pd_set_thresholds(users_pd, config.thresholds);
174 result = get_status((double)users, thlds); 122 mp_add_perfdata_to_subcheck(&sc_users, users_pd);
175 123
176 if (result == STATE_UNKNOWN) 124 int tmp_status = mp_get_pd_status(users_pd);
177 printf ("%s\n", _("Unable to read output")); 125 sc_users = mp_set_subcheck_state(sc_users, tmp_status);
178 else { 126
179 printf (_("USERS %s - %d users currently logged in |%s\n"), 127 switch (tmp_status) {
180 state_text(result), users, 128 case STATE_WARNING:
181 sperfdata_int("users", users, "", warning_range, 129 xasprintf(&sc_users.output,
182 critical_range, true, 0, false, 0)); 130 "%d users currently logged in. This violates the warning threshold",
131 user_wrapper.users);
132 break;
133 case STATE_CRITICAL:
134 xasprintf(&sc_users.output,
135 "%d users currently logged in. This violates the critical threshold",
136 user_wrapper.users);
137 break;
138 default:
139 xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users);
183 } 140 }
184 141
185 return result; 142 mp_add_subcheck_to_check(&overall, sc_users);
143 mp_exit(overall);
186} 144}
187 145
146#define output_format_index CHAR_MAX + 1
147
188/* process command-line arguments */ 148/* process command-line arguments */
189int 149check_users_config_wrapper process_arguments(int argc, char **argv) {
190process_arguments (int argc, char **argv) 150 static struct option longopts[] = {{"critical", required_argument, 0, 'c'},
191{ 151 {"warning", required_argument, 0, 'w'},
192 int c; 152 {"version", no_argument, 0, 'V'},
193 int option = 0; 153 {"help", no_argument, 0, 'h'},
194 static struct option longopts[] = { 154 {"output-format", required_argument, 0, output_format_index},
195 {"critical", required_argument, 0, 'c'}, 155 {0, 0, 0, 0}};
196 {"warning", required_argument, 0, 'w'}, 156
197 {"version", no_argument, 0, 'V'}, 157 if (argc < 2) {
198 {"help", no_argument, 0, 'h'}, 158 usage(progname);
199 {0, 0, 0, 0} 159 }
200 };
201 160
202 if (argc < 2) 161 char *warning_range = NULL;
203 usage ("\n"); 162 char *critical_range = NULL;
163 check_users_config_wrapper result = {
164 .config = check_users_config_init(),
165 .errorcode = OK,
166 };
204 167
205 while (true) { 168 while (true) {
206 c = getopt_long (argc, argv, "+hVvc:w:", longopts, &option); 169 int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL);
207 170
208 if (c == -1 || c == EOF || c == 1) 171 if (counter == -1 || counter == EOF || counter == 1) {
209 break; 172 break;
173 }
210 174
211 switch (c) { 175 switch (counter) {
212 case '?': /* print short usage statement if args not parsable */ 176 case '?': /* print short usage statement if args not parsable */
213 usage5 (); 177 usage5();
214 case 'h': /* help */ 178 case 'h': /* help */
215 print_help (); 179 print_help();
216 exit (STATE_UNKNOWN); 180 exit(STATE_UNKNOWN);
217 case 'V': /* version */ 181 case 'V': /* version */
218 print_revision (progname, NP_VERSION); 182 print_revision(progname, NP_VERSION);
219 exit (STATE_UNKNOWN); 183 exit(STATE_UNKNOWN);
220 case 'c': /* critical */ 184 case 'c': /* critical */
221 critical_range = optarg; 185 critical_range = optarg;
222 break; 186 break;
223 case 'w': /* warning */ 187 case 'w': /* warning */
224 warning_range = optarg; 188 warning_range = optarg;
225 break; 189 break;
190 case output_format_index: {
191 parsed_output_format parser = mp_parse_output_format(optarg);
192 if (!parser.parsing_success) {
193 // TODO List all available formats here, maybe add anothoer usage function
194 printf("Invalid output format: %s\n", optarg);
195 exit(STATE_UNKNOWN);
196 }
197
198 result.config.output_format_is_set = true;
199 result.config.output_format = parser.output_format;
200 break;
201 }
226 } 202 }
227 } 203 }
228 204
229 c = optind; 205 int option_char = optind;
230 206
231 if (warning_range == NULL && argc > c) 207 if (warning_range == NULL && argc > option_char) {
232 warning_range = argv[c++]; 208 warning_range = argv[option_char++];
209 }
233 210
234 if (critical_range == NULL && argc > c) 211 if (critical_range == NULL && argc > option_char) {
235 critical_range = argv[c++]; 212 critical_range = argv[option_char++];
213 }
236 214
237 /* this will abort in case of invalid ranges */ 215 // TODO add proper verification for ranges here!
238 set_thresholds (&thlds, warning_range, critical_range); 216 mp_range_parsed tmp;
217 if (warning_range) {
218 tmp = mp_parse_range_string(warning_range);
219 } else {
220 printf("Warning threshold missing\n");
221 print_usage();
222 exit(STATE_UNKNOWN);
223 }
239 224
240 if (!thlds->warning) { 225 if (tmp.error == MP_PARSING_SUCCESS) {
241 usage4 (_("Warning threshold must be a valid range expression")); 226 result.config.thresholds.warning = tmp.range;
227 result.config.thresholds.warning_is_set = true;
228 } else {
229 printf("Failed to parse warning range: %s", warning_range);
230 exit(STATE_UNKNOWN);
242 } 231 }
243 232
244 if (!thlds->critical) { 233 if (critical_range) {
245 usage4 (_("Critical threshold must be a valid range expression")); 234 tmp = mp_parse_range_string(critical_range);
235 } else {
236 printf("Critical threshold missing\n");
237 print_usage();
238 exit(STATE_UNKNOWN);
246 } 239 }
247 240
248 return OK; 241 if (tmp.error == MP_PARSING_SUCCESS) {
242 result.config.thresholds.critical = tmp.range;
243 result.config.thresholds.critical_is_set = true;
244 } else {
245 printf("Failed to parse critical range: %s", critical_range);
246 exit(STATE_UNKNOWN);
247 }
248
249 return result;
249} 250}
250 251
251void 252void print_help(void) {
252print_help (void) 253 print_revision(progname, NP_VERSION);
253{
254 print_revision (progname, NP_VERSION);
255 254
256 printf ("Copyright (c) 1999 Ethan Galstad\n"); 255 printf("Copyright (c) 1999 Ethan Galstad\n");
257 printf (COPYRIGHT, copyright, email); 256 printf(COPYRIGHT, copyright, email);
258 257
259 printf ("%s\n", _("This plugin checks the number of users currently logged in on the local")); 258 printf("%s\n", _("This plugin checks the number of users currently logged in on the local"));
260 printf ("%s\n", _("system and generates an error if the number exceeds the thresholds specified.")); 259 printf("%s\n",
260 _("system and generates an error if the number exceeds the thresholds specified."));
261 261
262 printf ("\n\n"); 262 printf("\n\n");
263 263
264 print_usage (); 264 print_usage();
265 265
266 printf (UT_HELP_VRSN); 266 printf(UT_HELP_VRSN);
267 printf (UT_EXTRA_OPTS); 267 printf(UT_EXTRA_OPTS);
268 268
269 printf (" %s\n", "-w, --warning=RANGE_EXPRESSION"); 269 printf(" %s\n", "-w, --warning=RANGE_EXPRESSION");
270 printf (" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); 270 printf(" %s\n",
271 printf (" %s\n", "-c, --critical=RANGE_EXPRESSION"); 271 _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION"));
272 printf (" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); 272 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION");
273 printf(" %s\n",
274 _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION"));
275 printf(UT_OUTPUT_FORMAT);
273 276
274 printf (UT_SUPPORT); 277 printf(UT_SUPPORT);
275} 278}
276 279
277void 280void print_usage(void) {
278print_usage (void) 281 printf("%s\n", _("Usage:"));
279{ 282 printf("%s -w <users> -c <users>\n", progname);
280 printf ("%s\n", _("Usage:"));
281 printf ("%s -w <users> -c <users>\n", progname);
282} 283}
diff --git a/plugins/check_users.d/config.h b/plugins/check_users.d/config.h
new file mode 100644
index 00000000..26d3ee70
--- /dev/null
+++ b/plugins/check_users.d/config.h
@@ -0,0 +1,20 @@
1#pragma once
2
3#include "output.h"
4#include "thresholds.h"
5
6typedef struct check_users_config {
7 mp_thresholds thresholds;
8
9 bool output_format_is_set;
10 mp_output_format output_format;
11} check_users_config;
12
13check_users_config check_users_config_init() {
14 check_users_config tmp = {
15 .thresholds = mp_thresholds_init(),
16
17 .output_format_is_set = false,
18 };
19 return tmp;
20}
diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c
new file mode 100644
index 00000000..a08f79c5
--- /dev/null
+++ b/plugins/check_users.d/users.c
@@ -0,0 +1,169 @@
1#include "./users.h"
2
3#ifdef _WIN32
4# ifdef HAVE_WTSAPI32_H
5# include <windows.h>
6# include <wtsapi32.h>
7# undef ERROR
8# define ERROR -1
9
10get_num_of_users_wrapper get_num_of_users_windows() {
11 WTS_SESSION_INFO *wtsinfo;
12 DWORD wtscount;
13
14 get_num_of_users_wrapper result = {};
15
16 if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) {
17 // printf(_("Could not enumerate RD sessions: %d\n"), GetLastError());
18 result.error = WINDOWS_COULD_NOT_ENUMERATE_SESSIONS;
19 return result;
20 }
21
22 for (DWORD index = 0; index < wtscount; index++) {
23 LPTSTR username;
24 DWORD size;
25
26 if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId,
27 WTSUserName, &username, &size)) {
28 continue;
29 }
30
31 int len = lstrlen(username);
32
33 WTSFreeMemory(username);
34
35 if (len == 0) {
36 continue;
37 }
38
39 if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) {
40 result.users++;
41 }
42 }
43
44 WTSFreeMemory(wtsinfo);
45 return result;
46}
47# else // HAVE_WTSAPI32_H
48# error On windows but without the WTSAPI32 lib
49# endif // HAVE_WTSAPI32_H
50
51#else // _WIN32
52
53# include "../../config.h"
54# include <stddef.h>
55
56# ifdef HAVE_LIBSYSTEMD
57# include <systemd/sd-daemon.h>
58# include <systemd/sd-login.h>
59
60get_num_of_users_wrapper get_num_of_users_systemd() {
61 get_num_of_users_wrapper result = {};
62
63 // Test whether we booted with systemd
64 if (sd_booted() > 0) {
65 int users = sd_get_uids(NULL);
66 if (users >= 0) {
67 // Success
68 result.users = users;
69 return result;
70 }
71
72 // Failure! return the error code
73 result.errorcode = users;
74 return result;
75 }
76
77 // Looks like we are not running systemd,
78 // return with error here
79 result.errorcode = NO_SYSTEMD_ERROR;
80 return result;
81}
82# endif
83
84# ifdef HAVE_UTMPX_H
85# include <utmpx.h>
86
87get_num_of_users_wrapper get_num_of_users_utmp() {
88 int users = 0;
89
90 /* get currently logged users from utmpx */
91 setutxent();
92
93 struct utmpx *putmpx;
94 while ((putmpx = getutxent()) != NULL) {
95 if (putmpx->ut_type == USER_PROCESS) {
96 users++;
97 }
98 }
99
100 endutxent();
101
102 get_num_of_users_wrapper result = {
103 .errorcode = 0,
104 .users = users,
105 };
106
107 return result;
108}
109# endif
110
111# ifndef HAVE_WTSAPI32_H
112# ifndef HAVE_LIBSYSTEMD
113# ifndef HAVE_UTMPX_H
114// Fall back option here for the others (probably still not on windows)
115
116# include "../common.h"
117# include "../popen.h"
118# include "../utils.h"
119
120get_num_of_users_wrapper get_num_of_users_who_command() {
121 /* run the command */
122 child_process = spopen(WHO_COMMAND);
123 if (child_process == NULL) {
124 // printf(_("Could not open pipe: %s\n"), WHO_COMMAND);
125 get_num_of_users_wrapper result = {
126 .errorcode = COULD_NOT_OPEN_PIPE,
127 };
128 return result;
129 }
130
131 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
132 if (child_stderr == NULL) {
133 // printf(_("Could not open stderr for %s\n"), WHO_COMMAND);
134 // TODO this error should probably be reported
135 }
136
137 get_num_of_users_wrapper result = {};
138 char input_buffer[MAX_INPUT_BUFFER];
139 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
140 /* increment 'users' on all lines except total user count */
141 if (input_buffer[0] != '#') {
142 result.users++;
143 continue;
144 }
145
146 /* get total logged in users */
147 if (sscanf(input_buffer, _("# users=%d"), &result.users) == 1) {
148 break;
149 }
150 }
151
152 /* check STDERR */
153 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
154 // if this fails, something broke and the result can not be relied upon or so is the theorie
155 // here
156 result.errorcode = STDERR_COULD_NOT_BE_READ;
157 }
158 (void)fclose(child_stderr);
159
160 /* close the pipe */
161 spclose(child_process);
162
163 return result;
164}
165
166# endif
167# endif
168# endif
169#endif
diff --git a/plugins/check_users.d/users.h b/plugins/check_users.d/users.h
new file mode 100644
index 00000000..aacba775
--- /dev/null
+++ b/plugins/check_users.d/users.h
@@ -0,0 +1,18 @@
1#pragma once
2
3typedef struct get_num_of_users_wrapper {
4 int errorcode;
5 int users;
6} get_num_of_users_wrapper;
7
8enum {
9 NO_SYSTEMD_ERROR = 64,
10 WINDOWS_COULD_NOT_ENUMERATE_SESSIONS,
11 COULD_NOT_OPEN_PIPE,
12 STDERR_COULD_NOT_BE_READ,
13};
14
15get_num_of_users_wrapper get_num_of_users_systemd();
16get_num_of_users_wrapper get_num_of_users_utmp();
17get_num_of_users_wrapper get_num_of_users_windows();
18get_num_of_users_wrapper get_num_of_users_who_command();
diff --git a/plugins/common.h b/plugins/common.h
index 833479ce..9d1434a3 100644
--- a/plugins/common.h
+++ b/plugins/common.h
@@ -1,126 +1,122 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins common include file 3 * Monitoring Plugins common include file
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2003-2007 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2007 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains common include files and defines used in many of 11 * This file contains common include files and defines used in many of
12* the plugins. 12 * the plugins.
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
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 36
36#ifdef HAVE_FEATURES_H 37#ifdef HAVE_FEATURES_H
37#include <features.h> 38# include <features.h>
38#endif 39#endif
39 40
40#include <stdio.h> /* obligatory includes */ 41#include <stdio.h> /* obligatory includes */
41#include <stdlib.h> 42#include <stdlib.h>
42#include <errno.h> 43#include <errno.h>
43 44
44/* This block provides uintmax_t - should be reported to coreutils that this should be added to fsuage.h */ 45/* This block provides uintmax_t - should be reported to coreutils that this should be added to
46 * fsuage.h */
45#if HAVE_INTTYPES_H 47#if HAVE_INTTYPES_H
46# include <inttypes.h> 48# include <inttypes.h>
47#endif 49#endif
48#if HAVE_STDINT_H 50#if HAVE_STDINT_H
49# include <stdint.h> 51# include <stdint.h>
50#endif 52#endif
51#include <unistd.h> 53#include <unistd.h>
52#ifndef UINTMAX_MAX 54#ifndef UINTMAX_MAX
53# define UINTMAX_MAX ((uintmax_t) -1) 55# define UINTMAX_MAX ((uintmax_t) - 1)
54#endif 56#endif
55 57
56#include <limits.h> /* This is assumed true, because coreutils assume it too */ 58#include <limits.h> /* This is assumed true, because coreutils assume it too */
57 59
58#ifdef HAVE_MATH_H 60#ifdef HAVE_MATH_H
59#include <math.h> 61# include <math.h>
60#endif 62#endif
61 63
62#ifdef _AIX 64#ifdef _AIX
63#ifdef HAVE_MP_H 65# ifdef HAVE_MP_H
64#include <mp.h> 66# include <mp.h>
65#endif 67# endif
66#endif 68#endif
67 69
68#ifdef HAVE_STRINGS_H 70#ifdef HAVE_STRINGS_H
69#include <strings.h> 71# include <strings.h>
70#endif 72#endif
71#ifdef HAVE_STRING_H 73#ifdef HAVE_STRING_H
72#include <string.h> 74# include <string.h>
73#endif 75#endif
74 76
75#ifdef HAVE_UNISTD_H 77#ifdef HAVE_UNISTD_H
76#include <unistd.h> 78# include <unistd.h>
77#endif 79#endif
78 80
79/* GET_NUMBER_OF_CPUS is a macro to return 81/* GET_NUMBER_OF_CPUS is a macro to return
80 number of CPUs, if we can get that data. 82 number of CPUs, if we can get that data.
81 Use configure.in to test for various OS ways of 83 Use configure.in to test for various OS ways of
82 getting that data 84 getting that data
83 Will return -1 if cannot get data 85 Will return -1 if cannot get data
84*/ 86*/
85#if defined(HAVE_SYSCONF__SC_NPROCESSORS_ONLN) 87#if defined(HAVE_SYSCONF__SC_NPROCESSORS_ONLN)
86# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_ONLN) 88# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_ONLN)
87#elif defined (HAVE_SYSCONF__SC_NPROCESSORS_CONF) 89#elif defined(HAVE_SYSCONF__SC_NPROCESSORS_CONF)
88# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_CONF) 90# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_CONF)
89#else 91#else
90# define GET_NUMBER_OF_CPUS() -1 92# define GET_NUMBER_OF_CPUS() -1
91#endif 93#endif
92 94
93#ifdef TIME_WITH_SYS_TIME 95#ifdef HAVE_SYS_TIME_H
94# include <sys/time.h> 96# include <sys/time.h>
95# include <time.h>
96#else
97# ifdef HAVE_SYS_TIME_H
98# include <sys/time.h>
99# else
100# include <time.h>
101# endif
102#endif 97#endif
98#include <time.h>
103 99
104#ifdef HAVE_SYS_TYPES_H 100#ifdef HAVE_SYS_TYPES_H
105#include <sys/types.h> 101# include <sys/types.h>
106#endif 102#endif
107 103
108#ifdef HAVE_SYS_SOCKET_H 104#ifdef HAVE_SYS_SOCKET_H
109#include <sys/socket.h> 105# include <sys/socket.h>
110#endif 106#endif
111 107
112#ifdef HAVE_SIGNAL_H 108#ifdef HAVE_SIGNAL_H
113#include <signal.h> 109# include <signal.h>
114#endif 110#endif
115 111
116/* GNU Libraries */ 112/* GNU Libraries */
117#include <getopt.h> 113#include <getopt.h>
118#include "dirname.h" 114#include "../gl/dirname.h"
119 115
120#include <locale.h> 116#include <locale.h>
121 117
122#ifdef HAVE_SYS_POLL_H 118#ifdef HAVE_SYS_POLL_H
123# include "sys/poll.h" 119# include "sys/poll.h"
124#endif 120#endif
125 121
126/* 122/*
@@ -130,42 +126,42 @@
130 */ 126 */
131 127
132#ifndef HAVE_STRTOL 128#ifndef HAVE_STRTOL
133# define strtol(a,b,c) atol((a)) 129# define strtol(a, b, c) atol((a))
134#endif 130#endif
135 131
136#ifndef HAVE_STRTOUL 132#ifndef HAVE_STRTOUL
137# define strtoul(a,b,c) (unsigned long)atol((a)) 133# define strtoul(a, b, c) (unsigned long)atol((a))
138#endif 134#endif
139 135
140/* SSL implementations */ 136/* SSL implementations */
141#ifdef HAVE_GNUTLS_OPENSSL_H 137#ifdef HAVE_GNUTLS_OPENSSL_H
142# include <gnutls/openssl.h> 138# include <gnutls/openssl.h>
143#else 139#else
144# define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */ 140# define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */
145# ifdef HAVE_SSL_H 141# ifdef HAVE_SSL_H
146# include <rsa.h> 142# include <rsa.h>
147# include <crypto.h> 143# include <crypto.h>
148# include <x509.h> 144# include <x509.h>
149# include <pem.h> 145# include <pem.h>
150# include <ssl.h> 146# include <ssl.h>
151# include <err.h> 147# include <err.h>
152# else 148# else
153# ifdef HAVE_OPENSSL_SSL_H 149# ifdef HAVE_OPENSSL_SSL_H
154# include <openssl/rsa.h> 150# include <openssl/rsa.h>
155# include <openssl/crypto.h> 151# include <openssl/crypto.h>
156# include <openssl/x509.h> 152# include <openssl/x509.h>
157# include <openssl/pem.h> 153# include <openssl/pem.h>
158# include <openssl/ssl.h> 154# include <openssl/ssl.h>
159# include <openssl/err.h> 155# include <openssl/err.h>
160# endif 156# endif
161# endif 157# endif
162#endif 158#endif
163 159
164/* openssl 1.1 does not set OPENSSL_NO_SSL2 by default but ships without ssl2 */ 160/* openssl 1.1 does not set OPENSSL_NO_SSL2 by default but ships without ssl2 */
165#ifdef OPENSSL_VERSION_NUMBER 161#ifdef OPENSSL_VERSION_NUMBER
166# if OPENSSL_VERSION_NUMBER >= 0x10100000 162# if OPENSSL_VERSION_NUMBER >= 0x10100000
167# define OPENSSL_NO_SSL2 163# define OPENSSL_NO_SSL2
168# endif 164# endif
169#endif 165#endif
170 166
171/* 167/*
@@ -176,7 +172,7 @@
176 172
177/* MariaDB 10.2 client does not set MYSQL_PORT */ 173/* MariaDB 10.2 client does not set MYSQL_PORT */
178#ifndef MYSQL_PORT 174#ifndef MYSQL_PORT
179# define MYSQL_PORT 3306 175# define MYSQL_PORT 3306
180#endif 176#endif
181 177
182enum { 178enum {
@@ -185,17 +181,9 @@ enum {
185}; 181};
186 182
187enum { 183enum {
188 STATE_OK, 184 DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
189 STATE_WARNING, 185 MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */
190 STATE_CRITICAL, 186 MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */
191 STATE_UNKNOWN,
192 STATE_DEPENDENT
193};
194
195enum {
196 DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
197 MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */
198 MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */
199}; 187};
200 188
201/* 189/*
@@ -203,18 +191,25 @@ enum {
203 * Internationalization 191 * Internationalization
204 * 192 *
205 */ 193 */
206#include "gettext.h" 194#include "../gl/gettext.h"
207#define _(String) gettext (String) 195#define _(String) gettext(String)
208#if ! ENABLE_NLS 196#if !defined(ENABLE_NLS) || !ENABLE_NLS
209# undef textdomain 197# undef textdomain
210# define textdomain(Domainname) /* empty */ 198# define textdomain(Domainname) /* empty */
211# undef bindtextdomain 199# undef bindtextdomain
212# define bindtextdomain(Domainname, Dirname) /* empty */ 200# define bindtextdomain(Domainname, Dirname) /* empty */
213#endif 201#endif
214 202
215/* For non-GNU compilers to ignore __attribute__ */ 203/* For non-GNU compilers to ignore __attribute__ */
216#ifndef __GNUC__ 204#ifndef __GNUC__
217# define __attribute__(x) /* do nothing */ 205# define __attribute__(x) /* do nothing */
206#endif
207
208/* for checking the result of getopt_long */
209#if EOF == -1
210#define CHECK_EOF(c) ((c) == EOF)
211#else
212#define CHECK_EOF(c) ((c) == -1 || (c) == EOF)
218#endif 213#endif
219 214
220#endif /* _COMMON_H_ */ 215#endif /* _COMMON_H_ */
diff --git a/plugins/negate.c b/plugins/negate.c
index c5fe7e13..a42a6c59 100644
--- a/plugins/negate.c
+++ b/plugins/negate.c
@@ -1,36 +1,36 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring negate plugin 3 * Monitoring negate plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2002-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the negate plugin 10 * This file contains the negate plugin
11* 11 *
12* Negates the status of a plugin (returns OK for CRITICAL, and vice-versa). 12 * Negates the status of a plugin (returns OK for CRITICAL, and vice-versa).
13* Can also perform custom state switching. 13 * Can also perform custom state switching.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "negate"; 32const char *progname = "negate";
33const char *copyright = "2002-2008"; 33const char *copyright = "2002-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#define DEFAULT_TIMEOUT 11 36#define DEFAULT_TIMEOUT 11
@@ -38,203 +38,212 @@ 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
44/* char *command_line; */ 51static void print_help(void);
52void print_usage(void);
45 53
46static const char **process_arguments (int, char **); 54int main(int argc, char **argv) {
47void validate_arguments (char **); 55 setlocale(LC_ALL, "");
48void print_help (void); 56 bindtextdomain(PACKAGE, LOCALEDIR);
49void print_usage (void); 57 textdomain(PACKAGE);
50bool subst_text = false;
51 58
52static int state[4] = { 59 timeout_interval = DEFAULT_TIMEOUT;
53 STATE_OK,
54 STATE_WARNING,
55 STATE_CRITICAL,
56 STATE_UNKNOWN,
57};
58 60
59int 61 negate_config_wrapper tmp_config = process_arguments(argc, argv);
60main (int argc, char **argv)
61{
62 int result = STATE_UNKNOWN;
63 char *sub;
64 char **command_line;
65 output chld_out, chld_err;
66 62
67 setlocale (LC_ALL, ""); 63 if (tmp_config.errorcode == ERROR) {
68 bindtextdomain (PACKAGE, LOCALEDIR); 64 die(STATE_UNKNOWN, _("negate: Failed to parse input"));
69 textdomain (PACKAGE); 65 }
70 66
71 timeout_interval = DEFAULT_TIMEOUT; 67 negate_config config = tmp_config.config;
72 68
73 command_line = (char **) process_arguments (argc, argv); 69 char **command_line = config.command_line;
74 70
75 /* Set signal handling and alarm */ 71 /* Set signal handling and alarm */
76 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) 72 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
77 die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); 73 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
74 }
75
76 (void)alarm(timeout_interval);
78 77
79 (void) alarm ((unsigned) timeout_interval); 78 mp_state_enum result = STATE_UNKNOWN;
79 output chld_out;
80 output chld_err;
80 81
81 /* catch when the command is quoted */ 82 /* catch when the command is quoted */
82 if(command_line[1] == NULL) { 83 if (command_line[1] == NULL) {
83 result = cmd_run (command_line[0], &chld_out, &chld_err, 0); 84 result = cmd_run(command_line[0], &chld_out, &chld_err, 0);
84 } else { 85 } else {
85 result = cmd_run_array (command_line, &chld_out, &chld_err, 0); 86 result = cmd_run_array(command_line, &chld_out, &chld_err, 0);
86 } 87 }
87 if (chld_err.lines > 0) { 88 if (chld_err.lines > 0) {
88 for (size_t i = 0; i < chld_err.lines; i++) { 89 for (size_t i = 0; i < chld_err.lines; i++) {
89 fprintf (stderr, "%s\n", chld_err.line[i]); 90 fprintf(stderr, "%s\n", chld_err.line[i]);
90 } 91 }
91 } 92 }
92 93
93 /* Return UNKNOWN or worse if no output is returned */ 94 /* Return UNKNOWN or worse if no output is returned */
94 if (chld_out.lines == 0) 95 if (chld_out.lines == 0) {
95 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 }
96 98
99 char *sub;
97 for (size_t i = 0; i < chld_out.lines; i++) { 100 for (size_t i = 0; i < chld_out.lines; i++) {
98 if (subst_text && result >= 0 && result <= 4 && result != state[result]) { 101 if (config.subst_text && result >= 0 && result <= 4 && result != config.state[result]) {
99 /* Loop over each match found */ 102 /* Loop over each match found */
100 while ((sub = strstr (chld_out.line[i], state_text (result)))) { 103 while ((sub = strstr(chld_out.line[i], state_text(result)))) {
101 /* 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 */
102 *sub = '\0'; 105 *sub = '\0';
103 sub += strlen (state_text (result)); 106 sub += strlen(state_text(result));
104 /* then put everything back together */ 107 /* then put everything back together */
105 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],
109 state_text(config.state[result]), sub);
106 } 110 }
107 } 111 }
108 printf ("%s\n", chld_out.line[i]); 112 printf("%s\n", chld_out.line[i]);
109 } 113 }
110 114
111 if (result >= 0 && result <= 4) { 115 if (result >= 0 && result <= 4) {
112 exit (state[result]); 116 exit(config.state[result]);
113 } else { 117 } else {
114 exit (result); 118 exit(result);
115 } 119 }
116} 120}
117 121
118
119/* process command-line arguments */ 122/* process command-line arguments */
120static const char ** 123static negate_config_wrapper process_arguments(int argc, char **argv) {
121process_arguments (int argc, char **argv)
122{
123 int c;
124 bool permute = true;
125
126 int option = 0;
127 static struct option longopts[] = { 124 static struct option longopts[] = {
128 {"help", no_argument, 0, 'h'}, 125 {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'},
129 {"version", no_argument, 0, 'V'}, 126 {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'},
130 {"timeout", required_argument, 0, 't'}, 127 {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'},
131 {"timeout-result", required_argument, 0, 'T'}, 128 {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'},
132 {"ok", required_argument, 0, 'o'}, 129 {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}};
133 {"warning", required_argument, 0, 'w'}, 130
134 {"critical", required_argument, 0, 'c'}, 131 negate_config_wrapper result = {
135 {"unknown", required_argument, 0, 'u'}, 132 .errorcode = OK,
136 {"substitute", no_argument, 0, 's'}, 133 .config = negate_config_init(),
137 {0, 0, 0, 0}
138 }; 134 };
135 bool permute = true;
136 while (true) {
137 int option = 0;
138 int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option);
139 139
140 while (1) { 140 if (option_char == -1 || option_char == EOF) {
141 c = getopt_long (argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option);
142
143 if (c == -1 || c == EOF)
144 break; 141 break;
142 }
145 143
146 switch (c) { 144 switch (option_char) {
147 case '?': /* help */ 145 case '?': /* help */
148 usage5 (); 146 usage5();
149 break; 147 break;
150 case 'h': /* help */ 148 case 'h': /* help */
151 print_help (); 149 print_help();
152 exit (EXIT_SUCCESS); 150 exit(STATE_UNKNOWN);
153 break; 151 break;
154 case 'V': /* version */ 152 case 'V': /* version */
155 print_revision (progname, NP_VERSION); 153 print_revision(progname, NP_VERSION);
156 exit (EXIT_SUCCESS); 154 exit(STATE_UNKNOWN);
157 case 't': /* timeout period */ 155 case 't': /* timeout period */
158 if (!is_integer (optarg)) 156 if (!is_integer(optarg)) {
159 usage2 (_("Timeout interval must be a positive integer"), optarg); 157 usage2(_("Timeout interval must be a positive integer"), optarg);
160 else 158 } else {
161 timeout_interval = atoi (optarg); 159 timeout_interval = atoi(optarg);
160 }
162 break; 161 break;
163 case 'T': /* Result to return on timeouts */ 162 case 'T': /* Result to return on timeouts */
164 if ((timeout_state = mp_translate_state(optarg)) == ERROR) 163 if ((timeout_state = mp_translate_state(optarg)) == ERROR) {
165 usage4 (_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 164 usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, "
165 "UNKNOWN) or integer (0-3)."));
166 }
166 break; 167 break;
167 case 'o': /* replacement for OK */ 168 case 'o': /* replacement for OK */
168 if ((state[STATE_OK] = mp_translate_state(optarg)) == ERROR) 169 if ((result.config.state[STATE_OK] = mp_translate_state(optarg)) == ERROR) {
169 usage4 (_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 170 usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
171 "integer (0-3)."));
172 }
170 permute = false; 173 permute = false;
171 break; 174 break;
172 175
173 case 'w': /* replacement for WARNING */ 176 case 'w': /* replacement for WARNING */
174 if ((state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) 177 if ((result.config.state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) {
175 usage4 (_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 178 usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
179 "integer (0-3)."));
180 }
176 permute = false; 181 permute = false;
177 break; 182 break;
178 case 'c': /* replacement for CRITICAL */ 183 case 'c': /* replacement for CRITICAL */
179 if ((state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) 184 if ((result.config.state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) {
180 usage4 (_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 185 usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
186 "integer (0-3)."));
187 }
181 permute = false; 188 permute = false;
182 break; 189 break;
183 case 'u': /* replacement for UNKNOWN */ 190 case 'u': /* replacement for UNKNOWN */
184 if ((state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) 191 if ((result.config.state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) {
185 usage4 (_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 192 usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
193 "integer (0-3)."));
194 }
186 permute = false; 195 permute = false;
187 break; 196 break;
188 case 's': /* Substitute status text */ 197 case 's': /* Substitute status text */
189 subst_text = true; 198 result.config.subst_text = true;
190 break; 199 break;
191 } 200 }
192 } 201 }
193 202
194 validate_arguments (&argv[optind]);
195
196 if (permute) { /* No [owcu] switch specified, default to this */ 203 if (permute) { /* No [owcu] switch specified, default to this */
197 state[STATE_OK] = STATE_CRITICAL; 204 result.config.state[STATE_OK] = STATE_CRITICAL;
198 state[STATE_CRITICAL] = STATE_OK; 205 result.config.state[STATE_CRITICAL] = STATE_OK;
199 } 206 }
200 207
201 return (const char **) &argv[optind]; 208 result.config.command_line = &argv[optind];
209
210 return validate_arguments(result);
202} 211}
203 212
213negate_config_wrapper validate_arguments(negate_config_wrapper config_wrapper) {
214 if (config_wrapper.config.command_line[0] == NULL) {
215 usage4(_("Could not parse arguments"));
216 }
204 217
205void 218 if (strncmp(config_wrapper.config.command_line[0], "/", 1) != 0 &&
206validate_arguments (char **command_line) 219 strncmp(config_wrapper.config.command_line[0], "./", 2) != 0) {
207{ 220 usage4(_("Require path to command"));
208 if (command_line[0] == NULL) 221 }
209 usage4 (_("Could not parse arguments"));
210 222
211 if (strncmp(command_line[0],"/",1) != 0 && strncmp(command_line[0],"./",2) != 0) 223 return config_wrapper;
212 usage4 (_("Require path to command"));
213} 224}
214 225
226void print_help(void) {
227 print_revision(progname, NP_VERSION);
215 228
216void 229 printf(COPYRIGHT, copyright, email);
217print_help (void)
218{
219 print_revision (progname, NP_VERSION);
220 230
221 printf (COPYRIGHT, copyright, email); 231 printf("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and "
232 "vice-versa) by default."));
233 printf("%s\n", _("Additional switches can be used to control:\n"));
234 printf("\t - which state becomes what\n");
235 printf("\t - changing the plugin output text to match the return code");
222 236
223 printf ("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and vice-versa) by default.")); 237 printf("\n\n");
224 printf ("%s\n", _("Additional switches can be used to control:\n"));
225 printf ("\t - which state becomes what\n");
226 printf ("\t - changing the plugin output text to match the return code");
227 238
228 printf ("\n\n"); 239 print_usage();
229 240
230 print_usage (); 241 printf(UT_HELP_VRSN);
231 242
232 printf (UT_HELP_VRSN); 243 printf(UT_PLUG_TIMEOUT, timeout_interval);
233 244 printf(" %s\n", _("Keep timeout longer than the plugin timeout to retain CRITICAL status."));
234 printf (UT_PLUG_TIMEOUT, timeout_interval); 245 printf(" -T, --timeout-result=STATUS\n");
235 printf (" %s\n", _("Keep timeout longer than the plugin timeout to retain CRITICAL status.")); 246 printf(" %s\n", _("Custom result on Negate timeouts; see below for STATUS definition\n"));
236 printf (" -T, --timeout-result=STATUS\n");
237 printf (" %s\n", _("Custom result on Negate timeouts; see below for STATUS definition\n"));
238 247
239 printf(" -o, --ok=STATUS\n"); 248 printf(" -o, --ok=STATUS\n");
240 printf(" -w, --warning=STATUS\n"); 249 printf(" -w, --warning=STATUS\n");
@@ -246,31 +255,30 @@ print_help (void)
246 printf(" -s, --substitute\n"); 255 printf(" -s, --substitute\n");
247 printf(_(" Substitute output text as well. Will only substitute text in CAPITALS\n")); 256 printf(_(" Substitute output text as well. Will only substitute text in CAPITALS\n"));
248 257
249 printf ("\n"); 258 printf("\n");
250 printf ("%s\n", _("Examples:")); 259 printf("%s\n", _("Examples:"));
251 printf (" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host"); 260 printf(" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host");
252 printf (" %s\n", _("Run check_ping and invert result. Must use full path to plugin")); 261 printf(" %s\n", _("Run check_ping and invert result. Must use full path to plugin"));
253 printf (" %s\n", "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'"); 262 printf(" %s\n",
254 printf (" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL")); 263 "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'");
255 printf ("\n"); 264 printf(" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL"));
256 printf ("%s\n", _("Notes:")); 265 printf("\n");
257 printf (" %s\n", _("This plugin is a wrapper to take the output of another plugin and invert it.")); 266 printf("%s\n", _("Notes:"));
258 printf (" %s\n", _("The full path of the plugin must be provided.")); 267 printf(" %s\n",
259 printf (" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL.")); 268 _("This plugin is a wrapper to take the output of another plugin and invert it."));
260 printf (" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK.")); 269 printf(" %s\n", _("The full path of the plugin must be provided."));
261 printf (" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged.")); 270 printf(" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL."));
262 printf ("\n"); 271 printf(" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK."));
263 printf (" %s\n", _("Using timeout-result, it is possible to override the timeout behaviour or a")); 272 printf(" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged."));
264 printf (" %s\n", _("plugin by setting the negate timeout a bit lower.")); 273 printf("\n");
265 274 printf(" %s\n",
266 printf (UT_SUPPORT); 275 _("Using timeout-result, it is possible to override the timeout behaviour or a"));
276 printf(" %s\n", _("plugin by setting the negate timeout a bit lower."));
277
278 printf(UT_SUPPORT);
267} 279}
268 280
269 281void print_usage(void) {
270 282 printf("%s\n", _("Usage:"));
271void 283 printf("%s [-t timeout] [-Towcu STATE] [-s] <definition of wrapped plugin>\n", progname);
272print_usage (void)
273{
274 printf ("%s\n", _("Usage:"));
275 printf ("%s [-t timeout] [-Towcu STATE] [-s] <definition of wrapped plugin>\n", progname);
276} 284}
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/netutils.c b/plugins/netutils.c
index c6af248e..f9933ebd 100644
--- a/plugins/netutils.c
+++ b/plugins/netutils.c
@@ -1,218 +1,217 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins network utilities 3 * Monitoring Plugins network utilities
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2003-2008 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains commons functions used in many of the plugins. 11 * This file contains commons functions used in many of the plugins.
12* 12 *
13* 13 *
14* This program is free software: you can redistribute it and/or modify 14 * This program is free software: you can redistribute it and/or modify
15* it under the terms of the GNU General Public License as published by 15 * it under the terms of the GNU General Public License as published by
16* the Free Software Foundation, either version 3 of the License, or 16 * the Free Software Foundation, either version 3 of the License, or
17* (at your option) any later version. 17 * (at your option) any later version.
18* 18 *
19* This program is distributed in the hope that it will be useful, 19 * This program is distributed in the hope that it will be useful,
20* but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22* GNU General Public License for more details. 22 * GNU General Public License for more details.
23* 23 *
24* You should have received a copy of the GNU General Public License 24 * You should have received a copy of the GNU General Public License
25* along with this program. If not, see <http://www.gnu.org/licenses/>. 25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26* 26 *
27* 27 *
28*****************************************************************************/ 28 *****************************************************************************/
29 29
30#include "common.h" 30#include "common.h"
31#include "output.h"
32#include "states.h"
33#include <sys/types.h>
31#include "netutils.h" 34#include "netutils.h"
32 35
33unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT; 36unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT;
34unsigned int socket_timeout_state = STATE_CRITICAL; 37mp_state_enum socket_timeout_state = STATE_CRITICAL;
35 38mp_state_enum econn_refuse_state = STATE_CRITICAL;
36int econn_refuse_state = STATE_CRITICAL;
37bool was_refused = false; 39bool was_refused = false;
38#if USE_IPV6 40
39int address_family = AF_UNSPEC; 41int address_family = AF_UNSPEC;
40#else
41int address_family = AF_INET;
42#endif
43 42
44/* handles socket timeouts */ 43/* handles socket timeouts */
45void 44void socket_timeout_alarm_handler(int sig) {
46socket_timeout_alarm_handler (int sig) 45 mp_subcheck timeout_sc = mp_subcheck_init();
47{ 46 timeout_sc = mp_set_subcheck_state(timeout_sc, socket_timeout_state);
48 if (sig == SIGALRM) 47
49 printf (_("%s - Socket timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); 48 if (sig == SIGALRM) {
50 else 49 xasprintf(&timeout_sc.output, _("Socket timeout after %d seconds\n"), socket_timeout);
51 printf (_("%s - Abnormal timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); 50 } else {
52 51 xasprintf(&timeout_sc.output, _("Abnormal timeout after %d seconds\n"), socket_timeout);
53 exit (socket_timeout_state); 52 }
54} 53
54 mp_check overall = mp_check_init();
55 mp_add_subcheck_to_check(&overall, timeout_sc);
55 56
57 mp_exit(overall);
58}
56 59
57/* connects to a host on a specified tcp port, sends a string, and gets a 60/* connects to a host on a specified tcp port, sends a string, and gets a
58 response. loops on select-recv until timeout or eof to get all of a 61 response. loops on select-recv until timeout or eof to get all of a
59 multi-packet answer */ 62 multi-packet answer */
60int 63mp_state_enum process_tcp_request2(const char *server_address, const int server_port,
61process_tcp_request2 (const char *server_address, int server_port, 64 const char *send_buffer, char *recv_buffer,
62 const char *send_buffer, char *recv_buffer, int recv_size) 65 const int recv_size) {
63{
64 66
65 int result; 67 int socket;
66 int send_result;
67 int recv_result;
68 int sd;
69 struct timeval tv;
70 fd_set readfds;
71 int recv_length = 0;
72 68
73 result = np_net_connect (server_address, server_port, &sd, IPPROTO_TCP); 69 mp_state_enum connect_result =
74 if (result != STATE_OK) 70 np_net_connect(server_address, server_port, &socket, IPPROTO_TCP);
71 if (connect_result != STATE_OK) {
75 return STATE_CRITICAL; 72 return STATE_CRITICAL;
73 }
76 74
77 send_result = send (sd, send_buffer, strlen (send_buffer), 0); 75 mp_state_enum result;
78 if (send_result<0 || (size_t)send_result!=strlen(send_buffer)) { 76 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
79 printf ("%s\n", _("Send failed")); 77 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
78 // printf("%s\n", _("Send failed"));
80 result = STATE_WARNING; 79 result = STATE_WARNING;
81 } 80 }
82 81
83 while (1) { 82 fd_set readfds;
83 ssize_t recv_length = 0;
84 while (true) {
84 /* wait up to the number of seconds for socket timeout 85 /* wait up to the number of seconds for socket timeout
85 minus one for data from the host */ 86 minus one for data from the host */
86 tv.tv_sec = socket_timeout - 1; 87 struct timeval timeout = {
87 tv.tv_usec = 0; 88 .tv_sec = socket_timeout - 1,
88 FD_ZERO (&readfds); 89 .tv_usec = 0,
89 FD_SET (sd, &readfds); 90 };
90 select (sd + 1, &readfds, NULL, NULL, &tv); 91 FD_ZERO(&readfds);
92 FD_SET(socket, &readfds);
93 select(socket + 1, &readfds, NULL, NULL, &timeout);
91 94
92 /* make sure some data has arrived */ 95 /* make sure some data has arrived */
93 if (!FD_ISSET (sd, &readfds)) { /* it hasn't */ 96 if (!FD_ISSET(socket, &readfds)) { /* it hasn't */
94 if (!recv_length) { 97 if (!recv_length) {
95 strcpy (recv_buffer, ""); 98 strcpy(recv_buffer, "");
96 printf ("%s\n", _("No data was received from host!")); 99 // printf("%s\n", _("No data was received from host!"));
97 result = STATE_WARNING; 100 result = STATE_WARNING;
98 } 101 } else { /* this one failed, but previous ones worked */
99 else { /* this one failed, but previous ones worked */
100 recv_buffer[recv_length] = 0; 102 recv_buffer[recv_length] = 0;
101 } 103 }
102 break; 104 break;
105 } /* it has */
106
107 ssize_t recv_result =
108 recv(socket, recv_buffer + recv_length, (size_t)(recv_size - recv_length - 1), 0);
109 if (recv_result == -1) {
110 /* recv failed, bail out */
111 strcpy(recv_buffer + recv_length, "");
112 result = STATE_WARNING;
113 break;
103 } 114 }
104 else { /* it has */ 115
105 recv_result = 116 if (recv_result == 0) {
106 recv (sd, recv_buffer + recv_length, 117 /* end of file ? */
107 (size_t)recv_size - recv_length - 1, 0); 118 recv_buffer[recv_length] = 0;
108 if (recv_result == -1) { 119 break;
109 /* recv failed, bail out */ 120 }
110 strcpy (recv_buffer + recv_length, ""); 121
111 result = STATE_WARNING; 122 /* we got data! */
112 break; 123 recv_length += recv_result;
113 } 124 if (recv_length >= recv_size - 1) {
114 else if (recv_result == 0) { 125 /* buffer full, we're done */
115 /* end of file ? */ 126 recv_buffer[recv_size - 1] = 0;
116 recv_buffer[recv_length] = 0; 127 break;
117 break;
118 }
119 else { /* we got data! */
120 recv_length += recv_result;
121 if (recv_length >= recv_size - 1) {
122 /* buffer full, we're done */
123 recv_buffer[recv_size - 1] = 0;
124 break;
125 }
126 }
127 } 128 }
128 /* end if(!FD_ISSET(sd,&readfds)) */ 129 /* end if(!FD_ISSET(sd,&readfds)) */
129 } 130 }
130 /* end while(1) */
131 131
132 close (sd); 132 close(socket);
133 return result; 133 return result;
134} 134}
135 135
136
137/* connects to a host on a specified port, sends a string, and gets a 136/* connects to a host on a specified port, sends a string, and gets a
138 response */ 137 response */
139int 138mp_state_enum process_request(const char *server_address, const int server_port, const int proto,
140process_request (const char *server_address, int server_port, int proto, 139 const char *send_buffer, char *recv_buffer, const int recv_size) {
141 const char *send_buffer, char *recv_buffer, int recv_size)
142{
143 int result;
144 int sd;
145
146 result = STATE_OK;
147 140
148 result = np_net_connect (server_address, server_port, &sd, proto); 141 mp_state_enum result = STATE_OK;
149 if (result != STATE_OK) 142 int socket;
143 result = np_net_connect(server_address, server_port, &socket, proto);
144 if (result != STATE_OK) {
150 return STATE_CRITICAL; 145 return STATE_CRITICAL;
146 }
151 147
152 result = send_request (sd, proto, send_buffer, recv_buffer, recv_size); 148 result = send_request(socket, proto, send_buffer, recv_buffer, recv_size);
153 149
154 close (sd); 150 close(socket);
155 151
156 return result; 152 return result;
157} 153}
158 154
159
160/* opens a tcp or udp connection to a remote host or local socket */ 155/* opens a tcp or udp connection to a remote host or local socket */
161int 156mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor,
162np_net_connect (const char *host_name, int port, int *sd, int proto) 157 const int proto) {
163{ 158 /* send back STATE_UNKOWN if there's an error
164 /* send back STATE_UNKOWN if there's an error 159 send back STATE_OK if we connect
165 send back STATE_OK if we connect 160 send back STATE_CRITICAL if we can't connect.
166 send back STATE_CRITICAL if we can't connect. 161 Let upstream figure out what to send to the user. */
167 Let upstream figure out what to send to the user. */ 162 bool is_socket = (host_name[0] == '/');
168 struct addrinfo hints; 163 int socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
169 struct addrinfo *r, *res; 164
170 struct sockaddr_un su; 165 struct addrinfo hints = {};
171 char port_str[6], host[MAX_HOST_ADDRESS_LENGTH]; 166 struct addrinfo *res = NULL;
172 size_t len; 167 int result;
173 int socktype, result;
174 short is_socket = (host_name[0] == '/');
175
176 socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
177
178 /* as long as it doesn't start with a '/', it's assumed a host or ip */ 168 /* as long as it doesn't start with a '/', it's assumed a host or ip */
179 if (!is_socket){ 169 if (!is_socket) {
180 memset (&hints, 0, sizeof (hints)); 170 memset(&hints, 0, sizeof(hints));
181 hints.ai_family = address_family; 171 hints.ai_family = address_family;
182 hints.ai_protocol = proto; 172 hints.ai_protocol = proto;
183 hints.ai_socktype = socktype; 173 hints.ai_socktype = socktype;
184 174
185 len = strlen (host_name); 175 size_t len = strlen(host_name);
186 /* check for an [IPv6] address (and strip the brackets) */ 176 /* check for an [IPv6] address (and strip the brackets) */
187 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') { 177 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') {
188 host_name++; 178 host_name++;
189 len -= 2; 179 len -= 2;
190 } 180 }
191 if (len >= sizeof(host)) 181
182 char host[MAX_HOST_ADDRESS_LENGTH];
183
184 if (len >= sizeof(host)) {
192 return STATE_UNKNOWN; 185 return STATE_UNKNOWN;
193 memcpy (host, host_name, len); 186 }
187
188 memcpy(host, host_name, len);
194 host[len] = '\0'; 189 host[len] = '\0';
195 snprintf (port_str, sizeof (port_str), "%d", port);
196 result = getaddrinfo (host, port_str, &hints, &res);
197 190
198 if (result != 0) { 191 char port_str[6];
199 printf ("%s\n", gai_strerror (result)); 192 snprintf(port_str, sizeof(port_str), "%d", port);
193 int getaddrinfo_err = getaddrinfo(host, port_str, &hints, &res);
194
195 if (getaddrinfo_err != 0) {
196 // printf("%s\n", gai_strerror(result));
200 return STATE_UNKNOWN; 197 return STATE_UNKNOWN;
201 } 198 }
202 199
203 r = res; 200 struct addrinfo *addressPointer = res;
204 while (r) { 201 while (addressPointer) {
205 /* attempt to create a socket */ 202 /* attempt to create a socket */
206 *sd = socket (r->ai_family, socktype, r->ai_protocol); 203 *socketDescriptor =
204 socket(addressPointer->ai_family, socktype, addressPointer->ai_protocol);
207 205
208 if (*sd < 0) { 206 if (*socketDescriptor < 0) {
209 printf ("%s\n", _("Socket creation failed")); 207 // printf("%s\n", _("Socket creation failed"));
210 freeaddrinfo (r); 208 freeaddrinfo(addressPointer);
211 return STATE_UNKNOWN; 209 return STATE_UNKNOWN;
212 } 210 }
213 211
214 /* attempt to open a connection */ 212 /* attempt to open a connection */
215 result = connect (*sd, r->ai_addr, r->ai_addrlen); 213 result =
214 connect(*socketDescriptor, addressPointer->ai_addr, addressPointer->ai_addrlen);
216 215
217 if (result == 0) { 216 if (result == 0) {
218 was_refused = false; 217 was_refused = false;
@@ -227,149 +226,151 @@ np_net_connect (const char *host_name, int port, int *sd, int proto)
227 } 226 }
228 } 227 }
229 228
230 close (*sd); 229 close(*socketDescriptor);
231 r = r->ai_next; 230 addressPointer = addressPointer->ai_next;
232 } 231 }
233 freeaddrinfo (res); 232
234 } 233 freeaddrinfo(res);
235 /* else the hostname is interpreted as a path to a unix socket */ 234
236 else { 235 } else {
237 if(strlen(host_name) >= UNIX_PATH_MAX){ 236 /* else the hostname is interpreted as a path to a unix socket */
237 if (strlen(host_name) >= UNIX_PATH_MAX) {
238 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket")); 238 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket"));
239 } 239 }
240 memset(&su, 0, sizeof(su)); 240
241 struct sockaddr_un su = {};
241 su.sun_family = AF_UNIX; 242 su.sun_family = AF_UNIX;
242 strncpy(su.sun_path, host_name, UNIX_PATH_MAX); 243 strncpy(su.sun_path, host_name, UNIX_PATH_MAX);
243 *sd = socket(PF_UNIX, SOCK_STREAM, 0); 244 *socketDescriptor = socket(PF_UNIX, SOCK_STREAM, 0);
244 if(*sd < 0){ 245
246 if (*socketDescriptor < 0) {
245 die(STATE_UNKNOWN, _("Socket creation failed")); 247 die(STATE_UNKNOWN, _("Socket creation failed"));
246 } 248 }
247 result = connect(*sd, (struct sockaddr *)&su, sizeof(su)); 249
248 if (result < 0 && errno == ECONNREFUSED) 250 result = connect(*socketDescriptor, (struct sockaddr *)&su, sizeof(su));
251 if (result < 0 && errno == ECONNREFUSED) {
249 was_refused = true; 252 was_refused = true;
253 }
250 } 254 }
251 255
252 if (result == 0) 256 if (result == 0) {
253 return STATE_OK; 257 return STATE_OK;
254 else if (was_refused) { 258 }
259
260 if (was_refused) {
255 switch (econn_refuse_state) { /* a user-defined expected outcome */ 261 switch (econn_refuse_state) { /* a user-defined expected outcome */
256 case STATE_OK: 262 case STATE_OK:
257 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */ 263 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */
258 case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */ 264 case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */
259 if (is_socket) 265 if (is_socket) {
260 printf("connect to file socket %s: %s\n", host_name, strerror(errno)); 266 // printf("connect to file socket %s: %s\n", host_name, strerror(errno));
261 else 267 } else {
262 printf("connect to address %s and port %d: %s\n", 268 // printf("connect to address %s and port %d: %s\n", host_name, port,
263 host_name, port, strerror(errno)); 269 // strerror(errno));
270 }
264 return STATE_CRITICAL; 271 return STATE_CRITICAL;
265 break; 272 break;
266 default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */ 273 default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */
267 return STATE_UNKNOWN; 274 return STATE_UNKNOWN;
268 break; 275 break;
269 } 276 }
270 } 277 } else {
271 else { 278 if (is_socket) {
272 if (is_socket) 279 // printf("connect to file socket %s: %s\n", host_name, strerror(errno));
273 printf("connect to file socket %s: %s\n", host_name, strerror(errno)); 280 } else {
274 else 281 // printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno));
275 printf("connect to address %s and port %d: %s\n", 282 }
276 host_name, port, strerror(errno));
277 return STATE_CRITICAL; 283 return STATE_CRITICAL;
278 } 284 }
279} 285}
280 286
281int 287mp_state_enum send_request(const int socket, const int proto, const char *send_buffer,
282send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size) 288 char *recv_buffer, const int recv_size) {
283{ 289 mp_state_enum result = STATE_OK;
284 int result = STATE_OK;
285 int send_result;
286 int recv_result;
287 struct timeval tv;
288 fd_set readfds;
289 290
290 send_result = send (sd, send_buffer, strlen (send_buffer), 0); 291 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
291 if (send_result<0 || (size_t)send_result!=strlen(send_buffer)) { 292 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
292 printf ("%s\n", _("Send failed")); 293 // printf("%s\n", _("Send failed"));
293 result = STATE_WARNING; 294 result = STATE_WARNING;
294 } 295 }
295 296
296 /* wait up to the number of seconds for socket timeout minus one 297 /* wait up to the number of seconds for socket timeout minus one
297 for data from the host */ 298 for data from the host */
298 tv.tv_sec = socket_timeout - 1; 299 struct timeval timestamp = {
299 tv.tv_usec = 0; 300 .tv_sec = socket_timeout - 1,
300 FD_ZERO (&readfds); 301 .tv_usec = 0,
301 FD_SET (sd, &readfds); 302 };
302 select (sd + 1, &readfds, NULL, NULL, &tv); 303 fd_set readfds;
304 FD_ZERO(&readfds);
305 FD_SET(socket, &readfds);
306 select(socket + 1, &readfds, NULL, NULL, &timestamp);
303 307
304 /* make sure some data has arrived */ 308 /* make sure some data has arrived */
305 if (!FD_ISSET (sd, &readfds)) { 309 if (!FD_ISSET(socket, &readfds)) {
306 strcpy (recv_buffer, ""); 310 strcpy(recv_buffer, "");
307 printf ("%s\n", _("No data was received from host!")); 311 // printf("%s\n", _("No data was received from host!"));
308 result = STATE_WARNING; 312 result = STATE_WARNING;
309 } 313 } else {
310 314 ssize_t recv_result = recv(socket, recv_buffer, (size_t)(recv_size - 1), 0);
311 else {
312 recv_result = recv (sd, recv_buffer, (size_t)recv_size - 1, 0);
313 if (recv_result == -1) { 315 if (recv_result == -1) {
314 strcpy (recv_buffer, ""); 316 strcpy(recv_buffer, "");
315 if (proto != IPPROTO_TCP) 317 if (proto != IPPROTO_TCP) {
316 printf ("%s\n", _("Receive failed")); 318 // printf("%s\n", _("Receive failed"));
319 }
317 result = STATE_WARNING; 320 result = STATE_WARNING;
318 } 321 } else {
319 else
320 recv_buffer[recv_result] = 0; 322 recv_buffer[recv_result] = 0;
323 }
321 324
322 /* die returned string */ 325 /* die returned string */
323 recv_buffer[recv_size - 1] = 0; 326 recv_buffer[recv_size - 1] = 0;
324 } 327 }
328
325 return result; 329 return result;
326} 330}
327 331
328 332bool is_host(const char *address) {
329bool is_host (const char *address) { 333 if (is_addr(address) || is_hostname(address)) {
330 if (is_addr (address) || is_hostname (address))
331 return (true); 334 return (true);
335 }
332 336
333 return (false); 337 return (false);
334} 338}
335 339
336void 340void host_or_die(const char *str) {
337host_or_die(const char *str) 341 if (!str || (!is_addr(str) && !is_hostname(str))) {
338{
339 if(!str || (!is_addr(str) && !is_hostname(str)))
340 usage_va(_("Invalid hostname/address - %s"), str); 342 usage_va(_("Invalid hostname/address - %s"), str);
343 }
341} 344}
342 345
343bool is_addr (const char *address) { 346bool is_addr(const char *address) {
344#ifdef USE_IPV6 347 if (address_family == AF_INET && is_inet_addr(address)) {
345 if (address_family == AF_INET && is_inet_addr (address))
346 return true; 348 return true;
347 else if (address_family == AF_INET6 && is_inet6_addr (address)) 349 }
350
351 if (address_family == AF_INET6 && is_inet6_addr(address)) {
348 return true; 352 return true;
349#else 353 }
350 if (is_inet_addr (address))
351 return (true);
352#endif
353 354
354 return (false); 355 return false;
355} 356}
356 357
357int 358bool dns_lookup(const char *node_string, struct sockaddr_storage *ss, const int family) {
358dns_lookup (const char *in, struct sockaddr_storage *ss, int family)
359{
360 struct addrinfo hints; 359 struct addrinfo hints;
361 struct addrinfo *res; 360 memset(&hints, 0, sizeof(struct addrinfo));
362 int retval;
363
364 memset (&hints, 0, sizeof(struct addrinfo));
365 hints.ai_family = family; 361 hints.ai_family = family;
366 362
367 retval = getaddrinfo (in, NULL, &hints, &res); 363 struct addrinfo *res;
368 if (retval != 0) 364 int retval = getaddrinfo(node_string, NULL, &hints, &res);
365 if (retval != 0) {
369 return false; 366 return false;
367 }
368
369 if (ss != NULL) {
370 memcpy(ss, res->ai_addr, res->ai_addrlen);
371 }
372
373 freeaddrinfo(res);
370 374
371 if (ss != NULL)
372 memcpy (ss, res->ai_addr, res->ai_addrlen);
373 freeaddrinfo (res);
374 return true; 375 return true;
375} 376}
diff --git a/plugins/netutils.h b/plugins/netutils.h
index a95057e0..16c2d31f 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -1,120 +1,136 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins net utilities include file 3 * Monitoring Plugins net utilities include file
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2003-2007 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2007 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains common include files and function definitions 11 * This file contains common include files and function definitions
12* used in many of the plugins. 12 * used in many of the plugins.
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
31#ifndef _NETUTILS_H_ 31#ifndef _NETUTILS_H_
32#define _NETUTILS_H_ 32#define _NETUTILS_H_
33 33
34#include "common.h" 34#include "output.h"
35#include "states.h"
35#include "utils.h" 36#include "utils.h"
36#include <netinet/in.h> 37#include <netinet/in.h>
37#include <arpa/inet.h> 38#include <arpa/inet.h>
38#include <netdb.h> 39#include <netdb.h>
39 40
40#ifdef HAVE_SYS_UN_H 41#ifdef HAVE_SYS_UN_H
41# include <sys/un.h> 42# include <sys/un.h>
42# ifndef UNIX_PATH_MAX 43# ifndef UNIX_PATH_MAX
43 /* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */ 44/* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */
44# define UNIX_PATH_MAX 104 45# define UNIX_PATH_MAX 104
45# endif /* UNIX_PATH_MAX */ 46# endif /* UNIX_PATH_MAX */
46#endif /* HAVE_SYS_UN_H */ 47#endif /* HAVE_SYS_UN_H */
47 48
48#ifndef HOST_MAX_BYTES 49#ifndef HOST_MAX_BYTES
49# define HOST_MAX_BYTES 255 50# define HOST_MAX_BYTES 255
50#endif 51#endif
51 52
52/* process_request and wrapper macros */ 53/* process_request and wrapper macros */
53#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \ 54#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \
54 process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize) 55 process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize)
55#define process_udp_request(addr, port, sbuf, rbuf, rsize) \ 56#define process_udp_request(addr, port, sbuf, rbuf, rsize) \
56 process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize) 57 process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize)
57int process_tcp_request2 (const char *address, int port, 58mp_state_enum process_tcp_request2(const char *server_address, int server_port,
58 const char *sbuffer, char *rbuffer, int rsize); 59 const char *send_buffer, char *recv_buffer, int recv_size);
59int process_request (const char *address, int port, int proto, 60mp_state_enum process_request(const char *server_address, int server_port, int proto,
60 const char *sbuffer, char *rbuffer, int rsize); 61 const char *send_buffer, char *recv_buffer, int recv_size);
61 62
62/* my_connect and wrapper macros */ 63/* my_connect and wrapper macros */
63#define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP) 64#define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP)
64#define my_udp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_UDP) 65#define my_udp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_UDP)
65int np_net_connect(const char *address, int port, int *sd, int proto); 66mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor, int proto);
66 67
67/* send_request and wrapper macros */ 68/* send_request and wrapper macros */
68#define send_tcp_request(s, sbuf, rbuf, rsize) \ 69#define send_tcp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize)
69 send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize) 70#define send_udp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize)
70#define send_udp_request(s, sbuf, rbuf, rsize) \ 71mp_state_enum send_request(int socket, int proto, const char *send_buffer, char *recv_buffer,
71 send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize) 72 int recv_size);
72int send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size);
73
74 73
75/* "is_*" wrapper macros and functions */ 74/* "is_*" wrapper macros and functions */
76bool is_host (const char *); 75bool is_host(const char *);
77bool is_addr (const char *); 76bool is_addr(const char *);
78int dns_lookup (const char *, struct sockaddr_storage *, int); 77bool dns_lookup(const char *, struct sockaddr_storage *, int);
79void host_or_die(const char *str); 78void host_or_die(const char *str);
80#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family) 79#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family)
81#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET) 80#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET)
82#ifdef USE_IPV6 81# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6)
83# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) 82# define is_hostname(addr) resolve_host_or_addr(addr, address_family)
84# define is_hostname(addr) resolve_host_or_addr(addr, address_family)
85#else
86# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET)
87#endif
88 83
89extern unsigned int socket_timeout; 84extern unsigned int socket_timeout;
90extern unsigned int socket_timeout_state; 85extern mp_state_enum socket_timeout_state;
91extern int econn_refuse_state; 86extern mp_state_enum econn_refuse_state;
92extern bool was_refused; 87extern bool was_refused;
93extern int address_family; 88extern int address_family;
94 89
95void socket_timeout_alarm_handler (int) __attribute__((noreturn)); 90void socket_timeout_alarm_handler(int) __attribute__((noreturn));
96 91
97/* SSL-Related functionality */ 92/* SSL-Related functionality */
98#ifdef HAVE_SSL 93#ifdef HAVE_SSL
99# define MP_SSLv2 1 94# define MP_SSLv2 1
100# define MP_SSLv3 2 95# define MP_SSLv3 2
101# define MP_TLSv1 3 96# define MP_TLSv1 3
102# define MP_TLSv1_1 4 97# define MP_TLSv1_1 4
103# define MP_TLSv1_2 5 98# define MP_TLSv1_2 5
104# define MP_SSLv2_OR_NEWER 6 99# define MP_SSLv2_OR_NEWER 6
105# define MP_SSLv3_OR_NEWER 7 100# define MP_SSLv3_OR_NEWER 7
106# define MP_TLSv1_OR_NEWER 8 101# define MP_TLSv1_OR_NEWER 8
107# define MP_TLSv1_1_OR_NEWER 9 102# define MP_TLSv1_1_OR_NEWER 9
108# define MP_TLSv1_2_OR_NEWER 10 103# define MP_TLSv1_2_OR_NEWER 10
109/* maybe this could be merged with the above np_net_connect, via some flags */ 104/* maybe this could be merged with the above np_net_connect, via some flags */
110int np_net_ssl_init(int sd); 105int np_net_ssl_init(int socket);
111int np_net_ssl_init_with_hostname(int sd, char *host_name); 106int np_net_ssl_init_with_hostname(int socket, char *host_name);
112int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version); 107int np_net_ssl_init_with_hostname_and_version(int socket, char *host_name, int version);
113int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey); 108int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name, int version,
114void np_net_ssl_cleanup(); 109 char *cert, char *privkey);
110void np_net_ssl_cleanup(void);
115int np_net_ssl_write(const void *buf, int num); 111int np_net_ssl_write(const void *buf, int num);
116int np_net_ssl_read(void *buf, int num); 112int np_net_ssl_read(void *buf, int num);
117int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118#endif /* HAVE_SSL */
119 113
114typedef enum {
115 ALL_OK,
116 NO_SERVER_CERTIFICATE_PRESENT,
117 UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT,
118 WRONG_TIME_FORMAT_IN_CERTIFICATE,
119} retrieve_expiration_date_errors;
120
121typedef struct {
122 double remaining_seconds;
123 retrieve_expiration_date_errors errors;
124} retrieve_expiration_time_result;
125
126typedef struct {
127 mp_state_enum result_state;
128 double remaining_seconds;
129 retrieve_expiration_date_errors errors;
130} net_ssl_check_cert_result;
131net_ssl_check_cert_result np_net_ssl_check_cert2(unsigned int days_till_exp_warn, unsigned int days_till_exp_crit);
132
133mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
134mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
135#endif /* HAVE_SSL */
120#endif /* _NETUTILS_H_ */ 136#endif /* _NETUTILS_H_ */
diff --git a/plugins/picohttpparser/picohttpparser.c b/plugins/picohttpparser/picohttpparser.c
index d0bfac62..e87388b0 100644
--- a/plugins/picohttpparser/picohttpparser.c
+++ b/plugins/picohttpparser/picohttpparser.c
@@ -28,622 +28,640 @@
28#include <stddef.h> 28#include <stddef.h>
29#include <string.h> 29#include <string.h>
30#ifdef __SSE4_2__ 30#ifdef __SSE4_2__
31#ifdef _MSC_VER 31# ifdef _MSC_VER
32#include <nmmintrin.h> 32# include <nmmintrin.h>
33#else 33# else
34#include <x86intrin.h> 34# include <x86intrin.h>
35#endif 35# endif
36#endif 36#endif
37#include "picohttpparser.h" 37#include "picohttpparser.h"
38 38
39#if __GNUC__ >= 3 39#if __GNUC__ >= 3
40#define likely(x) __builtin_expect(!!(x), 1) 40# define likely(x) __builtin_expect(!!(x), 1)
41#define unlikely(x) __builtin_expect(!!(x), 0) 41# define unlikely(x) __builtin_expect(!!(x), 0)
42#else 42#else
43#define likely(x) (x) 43# define likely(x) (x)
44#define unlikely(x) (x) 44# define unlikely(x) (x)
45#endif 45#endif
46 46
47#ifdef _MSC_VER 47#ifdef _MSC_VER
48#define ALIGNED(n) _declspec(align(n)) 48# define ALIGNED(n) _declspec(align(n))
49#else 49#else
50#define ALIGNED(n) __attribute__((aligned(n))) 50# define ALIGNED(n) __attribute__((aligned(n)))
51#endif 51#endif
52 52
53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) 53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c) - 040u < 0137u)
54 54
55#define CHECK_EOF() \ 55#define CHECK_EOF() \
56 if (buf == buf_end) { \ 56 if (buf == buf_end) { \
57 *ret = -2; \ 57 *ret = -2; \
58 return NULL; \ 58 return NULL; \
59 } 59 }
60 60
61#define EXPECT_CHAR_NO_CHECK(ch) \ 61#define EXPECT_CHAR_NO_CHECK(ch) \
62 if (*buf++ != ch) { \ 62 if (*buf++ != ch) { \
63 *ret = -1; \ 63 *ret = -1; \
64 return NULL; \ 64 return NULL; \
65 } 65 }
66 66
67#define EXPECT_CHAR(ch) \ 67#define EXPECT_CHAR(ch) \
68 CHECK_EOF(); \ 68 CHECK_EOF(); \
69 EXPECT_CHAR_NO_CHECK(ch); 69 EXPECT_CHAR_NO_CHECK(ch);
70 70
71#define ADVANCE_TOKEN(tok, toklen) \ 71#define ADVANCE_TOKEN(tok, toklen) \
72 do { \ 72 do { \
73 const char *tok_start = buf; \ 73 const char *tok_start = buf; \
74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ 74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
75 int found2; \ 75 int found2; \
76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ 76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
77 if (!found2) { \ 77 if (!found2) { \
78 CHECK_EOF(); \ 78 CHECK_EOF(); \
79 } \ 79 } \
80 while (1) { \ 80 while (1) { \
81 if (*buf == ' ') { \ 81 if (*buf == ' ') { \
82 break; \ 82 break; \
83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ 83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \ 84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \
85 *ret = -1; \ 85 *ret = -1; \
86 return NULL; \ 86 return NULL; \
87 } \ 87 } \
88 } \ 88 } \
89 ++buf; \ 89 ++buf; \
90 CHECK_EOF(); \ 90 CHECK_EOF(); \
91 } \ 91 } \
92 tok = tok_start; \ 92 tok = tok_start; \
93 toklen = buf - tok_start; \ 93 toklen = buf - tok_start; \
94 } while (0) 94 } while (0)
95 95
96static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 96static const char *token_char_map =
97 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" 97 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
98 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" 98 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
99 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" 99 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
100 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 100 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
101 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 101 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
104 104 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
105static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) 105
106{ 106static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges,
107 *found = 0; 107 size_t ranges_size, int *found) {
108 *found = 0;
108#if __SSE4_2__ 109#if __SSE4_2__
109 if (likely(buf_end - buf >= 16)) { 110 if (likely(buf_end - buf >= 16)) {
110 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); 111 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
111 112
112 size_t left = (buf_end - buf) & ~15; 113 size_t left = (buf_end - buf) & ~15;
113 do { 114 do {
114 __m128i b16 = _mm_loadu_si128((const __m128i *)buf); 115 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
115 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); 116 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16,
116 if (unlikely(r != 16)) { 117 _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
117 buf += r; 118 if (unlikely(r != 16)) {
118 *found = 1; 119 buf += r;
119 break; 120 *found = 1;
120 } 121 break;
121 buf += 16; 122 }
122 left -= 16; 123 buf += 16;
123 } while (likely(left != 0)); 124 left -= 16;
124 } 125 } while (likely(left != 0));
126 }
125#else 127#else
126 /* suppress unused parameter warning */ 128 /* suppress unused parameter warning */
127 (void)buf_end; 129 (void)buf_end;
128 (void)ranges; 130 (void)ranges;
129 (void)ranges_size; 131 (void)ranges_size;
130#endif 132#endif
131 return buf; 133 return buf;
132} 134}
133 135
134static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) 136static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token,
135{ 137 size_t *token_len, int *ret) {
136 const char *token_start = buf; 138 const char *token_start = buf;
137 139
138#ifdef __SSE4_2__ 140#ifdef __SSE4_2__
139 static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ 141 static const char ALIGNED(16) ranges1[16] =
140 "\012\037" /* allow SP and up to but not including DEL */ 142 "\0\010" /* allow HT */
141 "\177\177"; /* allow chars w. MSB set */ 143 "\012\037" /* allow SP and up to but not including DEL */
142 int found; 144 "\177\177"; /* allow chars w. MSB set */
143 buf = findchar_fast(buf, buf_end, ranges1, 6, &found); 145 int found;
144 if (found) 146 buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
145 goto FOUND_CTL; 147 if (found) {
148 goto FOUND_CTL;
149 }
146#else 150#else
147 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ 151 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined
148 while (likely(buf_end - buf >= 8)) { 152 */
149#define DOIT() \ 153 while (likely(buf_end - buf >= 8)) {
150 do { \ 154# define DOIT() \
151 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ 155 do { \
152 goto NonPrintable; \ 156 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
153 ++buf; \ 157 goto NonPrintable; \
154 } while (0) 158 ++buf; \
155 DOIT(); 159 } while (0)
156 DOIT(); 160 DOIT();
157 DOIT(); 161 DOIT();
158 DOIT(); 162 DOIT();
159 DOIT(); 163 DOIT();
160 DOIT(); 164 DOIT();
161 DOIT(); 165 DOIT();
162 DOIT(); 166 DOIT();
163#undef DOIT 167 DOIT();
164 continue; 168# undef DOIT
165 NonPrintable: 169 continue;
166 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 170 NonPrintable:
167 goto FOUND_CTL; 171 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
168 } 172 unlikely(*buf == '\177')) {
169 ++buf; 173 goto FOUND_CTL;
170 } 174 }
175 ++buf;
176 }
171#endif 177#endif
172 for (;; ++buf) { 178 for (;; ++buf) {
173 CHECK_EOF(); 179 CHECK_EOF();
174 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { 180 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
175 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 181 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
176 goto FOUND_CTL; 182 unlikely(*buf == '\177')) {
177 } 183 goto FOUND_CTL;
178 } 184 }
179 } 185 }
186 }
180FOUND_CTL: 187FOUND_CTL:
181 if (likely(*buf == '\015')) { 188 if (likely(*buf == '\015')) {
182 ++buf; 189 ++buf;
183 EXPECT_CHAR('\012'); 190 EXPECT_CHAR('\012');
184 *token_len = buf - 2 - token_start; 191 *token_len = buf - 2 - token_start;
185 } else if (*buf == '\012') { 192 } else if (*buf == '\012') {
186 *token_len = buf - token_start; 193 *token_len = buf - token_start;
187 ++buf; 194 ++buf;
188 } else { 195 } else {
189 *ret = -1; 196 *ret = -1;
190 return NULL; 197 return NULL;
191 } 198 }
192 *token = token_start; 199 *token = token_start;
193 200
194 return buf; 201 return buf;
195} 202}
196 203
197static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) 204static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) {
198{ 205 int ret_cnt = 0;
199 int ret_cnt = 0; 206 buf = last_len < 3 ? buf : buf + last_len - 3;
200 buf = last_len < 3 ? buf : buf + last_len - 3; 207
201 208 while (1) {
202 while (1) { 209 CHECK_EOF();
203 CHECK_EOF(); 210 if (*buf == '\015') {
204 if (*buf == '\015') { 211 ++buf;
205 ++buf; 212 CHECK_EOF();
206 CHECK_EOF(); 213 EXPECT_CHAR('\012');
207 EXPECT_CHAR('\012'); 214 ++ret_cnt;
208 ++ret_cnt; 215 } else if (*buf == '\012') {
209 } else if (*buf == '\012') { 216 ++buf;
210 ++buf; 217 ++ret_cnt;
211 ++ret_cnt; 218 } else {
212 } else { 219 ++buf;
213 ++buf; 220 ret_cnt = 0;
214 ret_cnt = 0; 221 }
215 } 222 if (ret_cnt == 2) {
216 if (ret_cnt == 2) { 223 return buf;
217 return buf; 224 }
218 } 225 }
219 } 226
220 227 *ret = -2;
221 *ret = -2; 228 return NULL;
222 return NULL;
223} 229}
224 230
225#define PARSE_INT(valp_, mul_) \ 231#define PARSE_INT(valp_, mul_) \
226 if (*buf < '0' || '9' < *buf) { \ 232 if (*buf < '0' || '9' < *buf) { \
227 buf++; \ 233 buf++; \
228 *ret = -1; \ 234 *ret = -1; \
229 return NULL; \ 235 return NULL; \
230 } \ 236 } \
231 *(valp_) = (mul_) * (*buf++ - '0'); 237 *(valp_) = (mul_) * (*buf++ - '0');
232 238
233#define PARSE_INT_3(valp_) \ 239#define PARSE_INT_3(valp_) \
234 do { \ 240 do { \
235 int res_ = 0; \ 241 int res_ = 0; \
236 PARSE_INT(&res_, 100) \ 242 PARSE_INT(&res_, 100) \
237 *valp_ = res_; \ 243 *valp_ = res_; \
238 PARSE_INT(&res_, 10) \ 244 PARSE_INT(&res_, 10) \
239 *valp_ += res_; \ 245 *valp_ += res_; \
240 PARSE_INT(&res_, 1) \ 246 PARSE_INT(&res_, 1) \
241 *valp_ += res_; \ 247 *valp_ += res_; \
242 } while (0) 248 } while (0)
243 249
244/* returned pointer is always within [buf, buf_end), or null */ 250/* returned pointer is always within [buf, buf_end), or null */
245static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *ret) 251static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version,
246{ 252 int *minor_version, int *ret) {
247 /* we want at least [HTTP/1.<two chars>] to try to parse */ 253 /* we want at least [HTTP/1.<two chars>] to try to parse */
248 if (buf_end - buf < 9) { 254 if (buf_end - buf < 9) {
249 *ret = -2; 255 *ret = -2;
250 return NULL; 256 return NULL;
251 } 257 }
252 EXPECT_CHAR_NO_CHECK('H'); 258 EXPECT_CHAR_NO_CHECK('H');
253 EXPECT_CHAR_NO_CHECK('T'); 259 EXPECT_CHAR_NO_CHECK('T');
254 EXPECT_CHAR_NO_CHECK('T'); 260 EXPECT_CHAR_NO_CHECK('T');
255 EXPECT_CHAR_NO_CHECK('P'); 261 EXPECT_CHAR_NO_CHECK('P');
256 EXPECT_CHAR_NO_CHECK('/'); 262 EXPECT_CHAR_NO_CHECK('/');
257 PARSE_INT(major_version, 1); 263 PARSE_INT(major_version, 1);
258 if (*major_version == 1) { 264 if (*major_version == 1) {
259 EXPECT_CHAR_NO_CHECK('.'); 265 EXPECT_CHAR_NO_CHECK('.');
260 PARSE_INT(minor_version, 1); 266 PARSE_INT(minor_version, 1);
261 } else { 267 } else {
262 *minor_version = 0; 268 *minor_version = 0;
263 } 269 }
264 return buf; 270 return buf;
265} 271}
266 272
267static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, 273static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers,
268 size_t max_headers, int *ret) 274 size_t *num_headers, size_t max_headers, int *ret) {
269{ 275 for (;; ++*num_headers) {
270 for (;; ++*num_headers) { 276 CHECK_EOF();
271 CHECK_EOF(); 277 if (*buf == '\015') {
272 if (*buf == '\015') { 278 ++buf;
273 ++buf; 279 EXPECT_CHAR('\012');
274 EXPECT_CHAR('\012'); 280 break;
275 break; 281 } else if (*buf == '\012') {
276 } else if (*buf == '\012') { 282 ++buf;
277 ++buf; 283 break;
278 break; 284 }
279 } 285 if (*num_headers == max_headers) {
280 if (*num_headers == max_headers) { 286 *ret = -1;
281 *ret = -1; 287 return NULL;
282 return NULL; 288 }
283 } 289 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
284 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { 290 /* parsing name, but do not discard SP before colon, see
285 /* parsing name, but do not discard SP before colon, see 291 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
286 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ 292 headers[*num_headers].name = buf;
287 headers[*num_headers].name = buf; 293 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
288 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */ 294 "\"\"" /* 0x22 */
289 "\"\"" /* 0x22 */ 295 "()" /* 0x28,0x29 */
290 "()" /* 0x28,0x29 */ 296 ",," /* 0x2c */
291 ",," /* 0x2c */ 297 "//" /* 0x2f */
292 "//" /* 0x2f */ 298 ":@" /* 0x3a-0x40 */
293 ":@" /* 0x3a-0x40 */ 299 "[]" /* 0x5b-0x5d */
294 "[]" /* 0x5b-0x5d */ 300 "{\377"; /* 0x7b-0xff */
295 "{\377"; /* 0x7b-0xff */ 301 int found;
296 int found; 302 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
297 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found); 303 if (!found) {
298 if (!found) { 304 CHECK_EOF();
299 CHECK_EOF(); 305 }
300 } 306 while (1) {
301 while (1) { 307 if (*buf == ':') {
302 if (*buf == ':') { 308 break;
303 break; 309 } else if (!token_char_map[(unsigned char)*buf]) {
304 } else if (!token_char_map[(unsigned char)*buf]) { 310 *ret = -1;
305 *ret = -1; 311 return NULL;
306 return NULL; 312 }
307 } 313 ++buf;
308 ++buf; 314 CHECK_EOF();
309 CHECK_EOF(); 315 }
310 } 316 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
311 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) { 317 *ret = -1;
312 *ret = -1; 318 return NULL;
313 return NULL; 319 }
314 } 320 ++buf;
315 ++buf; 321 for (;; ++buf) {
316 for (;; ++buf) { 322 CHECK_EOF();
317 CHECK_EOF(); 323 if (!(*buf == ' ' || *buf == '\t')) {
318 if (!(*buf == ' ' || *buf == '\t')) { 324 break;
319 break; 325 }
320 } 326 }
321 } 327 } else {
322 } else { 328 headers[*num_headers].name = NULL;
323 headers[*num_headers].name = NULL; 329 headers[*num_headers].name_len = 0;
324 headers[*num_headers].name_len = 0; 330 }
325 } 331 const char *value;
326 const char *value; 332 size_t value_len;
327 size_t value_len; 333 if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
328 if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) { 334 return NULL;
329 return NULL; 335 }
330 } 336 /* remove trailing SPs and HTABs */
331 /* remove trailing SPs and HTABs */ 337 const char *value_end = value + value_len;
332 const char *value_end = value + value_len; 338 for (; value_end != value; --value_end) {
333 for (; value_end != value; --value_end) { 339 const char c = *(value_end - 1);
334 const char c = *(value_end - 1); 340 if (!(c == ' ' || c == '\t')) {
335 if (!(c == ' ' || c == '\t')) { 341 break;
336 break; 342 }
337 } 343 }
338 } 344 headers[*num_headers].value = value;
339 headers[*num_headers].value = value; 345 headers[*num_headers].value_len = value_end - value;
340 headers[*num_headers].value_len = value_end - value; 346 }
341 } 347 return buf;
342 return buf;
343} 348}
344 349
345static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, 350static const char *parse_request(const char *buf, const char *buf_end, const char **method,
346 size_t *path_len, int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, 351 size_t *method_len, const char **path, size_t *path_len,
347 size_t max_headers, int *ret) 352 int *major_version, int *minor_version, struct phr_header *headers,
348{ 353 size_t *num_headers, size_t max_headers, int *ret) {
349 /* skip first empty line (some clients add CRLF after POST content) */ 354 /* skip first empty line (some clients add CRLF after POST content) */
350 CHECK_EOF(); 355 CHECK_EOF();
351 if (*buf == '\015') { 356 if (*buf == '\015') {
352 ++buf; 357 ++buf;
353 EXPECT_CHAR('\012'); 358 EXPECT_CHAR('\012');
354 } else if (*buf == '\012') { 359 } else if (*buf == '\012') {
355 ++buf; 360 ++buf;
356 } 361 }
357 362
358 /* parse request line */ 363 /* parse request line */
359 ADVANCE_TOKEN(*method, *method_len); 364 ADVANCE_TOKEN(*method, *method_len);
360 do { 365 do {
361 ++buf; 366 ++buf;
362 } while (*buf == ' '); 367 } while (*buf == ' ');
363 ADVANCE_TOKEN(*path, *path_len); 368 ADVANCE_TOKEN(*path, *path_len);
364 do { 369 do {
365 ++buf; 370 ++buf;
366 } while (*buf == ' '); 371 } while (*buf == ' ');
367 if (*method_len == 0 || *path_len == 0) { 372 if (*method_len == 0 || *path_len == 0) {
368 *ret = -1; 373 *ret = -1;
369 return NULL; 374 return NULL;
370 } 375 }
371 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { 376 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) {
372 return NULL; 377 return NULL;
373 } 378 }
374 if (*buf == '\015') { 379 if (*buf == '\015') {
375 ++buf; 380 ++buf;
376 EXPECT_CHAR('\012'); 381 EXPECT_CHAR('\012');
377 } else if (*buf == '\012') { 382 } else if (*buf == '\012') {
378 ++buf; 383 ++buf;
379 } else { 384 } else {
380 *ret = -1; 385 *ret = -1;
381 return NULL; 386 return NULL;
382 } 387 }
383 388
384 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 389 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
385} 390}
386 391
387int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, 392int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len,
388 size_t *path_len, int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) 393 const char **path, size_t *path_len, int *major_version, int *minor_version,
389{ 394 struct phr_header *headers, size_t *num_headers, size_t last_len) {
390 const char *buf = buf_start, *buf_end = buf_start + len; 395 const char *buf = buf_start, *buf_end = buf_start + len;
391 size_t max_headers = *num_headers; 396 size_t max_headers = *num_headers;
392 int r; 397 int r;
393 398
394 *method = NULL; 399 *method = NULL;
395 *method_len = 0; 400 *method_len = 0;
396 *path = NULL; 401 *path = NULL;
397 *path_len = 0; 402 *path_len = 0;
398 *major_version = -1; 403 *major_version = -1;
399 *minor_version = -1; 404 *minor_version = -1;
400 *num_headers = 0; 405 *num_headers = 0;
401 406
402 /* if last_len != 0, check if the request is complete (a fast countermeasure 407 /* if last_len != 0, check if the request is complete (a fast countermeasure
403 against slowloris */ 408 against slowloris */
404 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 409 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
405 return r; 410 return r;
406 } 411 }
407 412
408 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version, minor_version, headers, num_headers, max_headers, 413 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version,
409 &r)) == NULL) { 414 minor_version, headers, num_headers, max_headers, &r)) == NULL) {
410 return r; 415 return r;
411 } 416 }
412 417
413 return (int)(buf - buf_start); 418 return (int)(buf - buf_start);
414} 419}
415 420
416static const char *parse_response(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *status, const char **msg, 421static const char *parse_response(const char *buf, const char *buf_end, int *major_version,
417 size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret) 422 int *minor_version, int *status, const char **msg,
418{ 423 size_t *msg_len, struct phr_header *headers, size_t *num_headers,
419 /* parse "HTTP/1.x" */ 424 size_t max_headers, int *ret) {
420 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { 425 /* parse "HTTP/1.x" */
421 return NULL; 426 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) {
422 } 427 return NULL;
423 /* skip space */ 428 }
424 if (*buf != ' ') { 429 /* skip space */
425 *ret = -1; 430 if (*buf != ' ') {
426 return NULL; 431 *ret = -1;
427 } 432 return NULL;
428 do { 433 }
429 ++buf; 434 do {
430 } while (*buf == ' '); 435 ++buf;
431 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */ 436 } while (*buf == ' ');
432 if (buf_end - buf < 4) { 437 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse
433 *ret = -2; 438 */
434 return NULL; 439 if (buf_end - buf < 4) {
435 } 440 *ret = -2;
436 PARSE_INT_3(status); 441 return NULL;
437 442 }
438 /* get message including preceding space */ 443 PARSE_INT_3(status);
439 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { 444
440 return NULL; 445 /* get message including preceding space */
441 } 446 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
442 if (*msg_len == 0) { 447 return NULL;
443 /* ok */ 448 }
444 } else if (**msg == ' ') { 449 if (*msg_len == 0) {
445 /* remove preceding space */ 450 /* ok */
446 do { 451 } else if (**msg == ' ') {
447 ++*msg; 452 /* remove preceding space */
448 --*msg_len; 453 do {
449 } while (**msg == ' '); 454 ++*msg;
450 } else { 455 --*msg_len;
451 /* garbage found after status code */ 456 } while (**msg == ' ');
452 *ret = -1; 457 } else {
453 return NULL; 458 /* garbage found after status code */
454 } 459 *ret = -1;
455 460 return NULL;
456 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 461 }
462
463 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
457} 464}
458 465
459int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version, int *status, const char **msg, size_t *msg_len, 466int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version,
460 struct phr_header *headers, size_t *num_headers, size_t last_len) 467 int *status, const char **msg, size_t *msg_len, struct phr_header *headers,
461{ 468 size_t *num_headers, size_t last_len) {
462 const char *buf = buf_start, *buf_end = buf + len; 469 const char *buf = buf_start, *buf_end = buf + len;
463 size_t max_headers = *num_headers; 470 size_t max_headers = *num_headers;
464 int r; 471 int r;
465 472
466 *major_version = -1; 473 *major_version = -1;
467 *minor_version = -1; 474 *minor_version = -1;
468 *status = 0; 475 *status = 0;
469 *msg = NULL; 476 *msg = NULL;
470 *msg_len = 0; 477 *msg_len = 0;
471 *num_headers = 0; 478 *num_headers = 0;
472 479
473 /* if last_len != 0, check if the response is complete (a fast countermeasure 480 /* if last_len != 0, check if the response is complete (a fast countermeasure
474 against slowloris */ 481 against slowloris */
475 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 482 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
476 return r; 483 return r;
477 } 484 }
478 485
479 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) { 486 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len,
480 return r; 487 headers, num_headers, max_headers, &r)) == NULL) {
481 } 488 return r;
482 489 }
483 return (int)(buf - buf_start); 490
491 return (int)(buf - buf_start);
484} 492}
485 493
486int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) 494int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers,
487{ 495 size_t *num_headers, size_t last_len) {
488 const char *buf = buf_start, *buf_end = buf + len; 496 const char *buf = buf_start, *buf_end = buf + len;
489 size_t max_headers = *num_headers; 497 size_t max_headers = *num_headers;
490 int r; 498 int r;
491 499
492 *num_headers = 0; 500 *num_headers = 0;
493 501
494 /* if last_len != 0, check if the response is complete (a fast countermeasure 502 /* if last_len != 0, check if the response is complete (a fast countermeasure
495 against slowloris */ 503 against slowloris */
496 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 504 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
497 return r; 505 return r;
498 } 506 }
499 507
500 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) { 508 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
501 return r; 509 return r;
502 } 510 }
503 511
504 return (int)(buf - buf_start); 512 return (int)(buf - buf_start);
505} 513}
506 514
507enum { 515enum {
508 CHUNKED_IN_CHUNK_SIZE, 516 CHUNKED_IN_CHUNK_SIZE,
509 CHUNKED_IN_CHUNK_EXT, 517 CHUNKED_IN_CHUNK_EXT,
510 CHUNKED_IN_CHUNK_DATA, 518 CHUNKED_IN_CHUNK_DATA,
511 CHUNKED_IN_CHUNK_CRLF, 519 CHUNKED_IN_CHUNK_CRLF,
512 CHUNKED_IN_TRAILERS_LINE_HEAD, 520 CHUNKED_IN_TRAILERS_LINE_HEAD,
513 CHUNKED_IN_TRAILERS_LINE_MIDDLE 521 CHUNKED_IN_TRAILERS_LINE_MIDDLE
514}; 522};
515 523
516static int decode_hex(int ch) 524static int decode_hex(int ch) {
517{ 525 if ('0' <= ch && ch <= '9') {
518 if ('0' <= ch && ch <= '9') { 526 return ch - '0';
519 return ch - '0'; 527 } else if ('A' <= ch && ch <= 'F') {
520 } else if ('A' <= ch && ch <= 'F') { 528 return ch - 'A' + 0xa;
521 return ch - 'A' + 0xa; 529 } else if ('a' <= ch && ch <= 'f') {
522 } else if ('a' <= ch && ch <= 'f') { 530 return ch - 'a' + 0xa;
523 return ch - 'a' + 0xa; 531 } else {
524 } else { 532 return -1;
525 return -1; 533 }
526 }
527} 534}
528 535
529ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) 536ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) {
530{ 537 size_t dst = 0, src = 0, bufsz = *_bufsz;
531 size_t dst = 0, src = 0, bufsz = *_bufsz; 538 ssize_t ret = -2; /* incomplete */
532 ssize_t ret = -2; /* incomplete */ 539
533 540 while (1) {
534 while (1) { 541 switch (decoder->_state) {
535 switch (decoder->_state) { 542 case CHUNKED_IN_CHUNK_SIZE:
536 case CHUNKED_IN_CHUNK_SIZE: 543 for (;; ++src) {
537 for (;; ++src) { 544 int v;
538 int v; 545 if (src == bufsz) {
539 if (src == bufsz) 546 goto Exit;
540 goto Exit; 547 }
541 if ((v = decode_hex(buf[src])) == -1) { 548 if ((v = decode_hex(buf[src])) == -1) {
542 if (decoder->_hex_count == 0) { 549 if (decoder->_hex_count == 0) {
543 ret = -1; 550 ret = -1;
544 goto Exit; 551 goto Exit;
545 } 552 }
546 break; 553 break;
547 } 554 }
548 if (decoder->_hex_count == sizeof(size_t) * 2) { 555 if (decoder->_hex_count == sizeof(size_t) * 2) {
549 ret = -1; 556 ret = -1;
550 goto Exit; 557 goto Exit;
551 } 558 }
552 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v; 559 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
553 ++decoder->_hex_count; 560 ++decoder->_hex_count;
554 } 561 }
555 decoder->_hex_count = 0; 562 decoder->_hex_count = 0;
556 decoder->_state = CHUNKED_IN_CHUNK_EXT; 563 decoder->_state = CHUNKED_IN_CHUNK_EXT;
557 /* fallthru */ 564 /* fallthru */
558 case CHUNKED_IN_CHUNK_EXT: 565 case CHUNKED_IN_CHUNK_EXT:
559 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ 566 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
560 for (;; ++src) { 567 for (;; ++src) {
561 if (src == bufsz) 568 if (src == bufsz) {
562 goto Exit; 569 goto Exit;
563 if (buf[src] == '\012') 570 }
564 break; 571 if (buf[src] == '\012') {
565 } 572 break;
566 ++src; 573 }
567 if (decoder->bytes_left_in_chunk == 0) { 574 }
568 if (decoder->consume_trailer) { 575 ++src;
569 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; 576 if (decoder->bytes_left_in_chunk == 0) {
570 break; 577 if (decoder->consume_trailer) {
571 } else { 578 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
572 goto Complete; 579 break;
573 } 580 } else {
574 } 581 goto Complete;
575 decoder->_state = CHUNKED_IN_CHUNK_DATA; 582 }
576 /* fallthru */ 583 }
577 case CHUNKED_IN_CHUNK_DATA: { 584 decoder->_state = CHUNKED_IN_CHUNK_DATA;
578 size_t avail = bufsz - src; 585 /* fallthru */
579 if (avail < decoder->bytes_left_in_chunk) { 586 case CHUNKED_IN_CHUNK_DATA: {
580 if (dst != src) 587 size_t avail = bufsz - src;
581 memmove(buf + dst, buf + src, avail); 588 if (avail < decoder->bytes_left_in_chunk) {
582 src += avail; 589 if (dst != src) {
583 dst += avail; 590 memmove(buf + dst, buf + src, avail);
584 decoder->bytes_left_in_chunk -= avail; 591 }
585 goto Exit; 592 src += avail;
586 } 593 dst += avail;
587 if (dst != src) 594 decoder->bytes_left_in_chunk -= avail;
588 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); 595 goto Exit;
589 src += decoder->bytes_left_in_chunk; 596 }
590 dst += decoder->bytes_left_in_chunk; 597 if (dst != src) {
591 decoder->bytes_left_in_chunk = 0; 598 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
592 decoder->_state = CHUNKED_IN_CHUNK_CRLF; 599 }
593 } 600 src += decoder->bytes_left_in_chunk;
594 /* fallthru */ 601 dst += decoder->bytes_left_in_chunk;
595 case CHUNKED_IN_CHUNK_CRLF: 602 decoder->bytes_left_in_chunk = 0;
596 for (;; ++src) { 603 decoder->_state = CHUNKED_IN_CHUNK_CRLF;
597 if (src == bufsz) 604 }
598 goto Exit; 605 /* fallthru */
599 if (buf[src] != '\015') 606 case CHUNKED_IN_CHUNK_CRLF:
600 break; 607 for (;; ++src) {
601 } 608 if (src == bufsz) {
602 if (buf[src] != '\012') { 609 goto Exit;
603 ret = -1; 610 }
604 goto Exit; 611 if (buf[src] != '\015') {
605 } 612 break;
606 ++src; 613 }
607 decoder->_state = CHUNKED_IN_CHUNK_SIZE; 614 }
608 break; 615 if (buf[src] != '\012') {
609 case CHUNKED_IN_TRAILERS_LINE_HEAD: 616 ret = -1;
610 for (;; ++src) { 617 goto Exit;
611 if (src == bufsz) 618 }
612 goto Exit; 619 ++src;
613 if (buf[src] != '\015') 620 decoder->_state = CHUNKED_IN_CHUNK_SIZE;
614 break; 621 break;
615 } 622 case CHUNKED_IN_TRAILERS_LINE_HEAD:
616 if (buf[src++] == '\012') 623 for (;; ++src) {
617 goto Complete; 624 if (src == bufsz) {
618 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; 625 goto Exit;
619 /* fallthru */ 626 }
620 case CHUNKED_IN_TRAILERS_LINE_MIDDLE: 627 if (buf[src] != '\015') {
621 for (;; ++src) { 628 break;
622 if (src == bufsz) 629 }
623 goto Exit; 630 }
624 if (buf[src] == '\012') 631 if (buf[src++] == '\012') {
625 break; 632 goto Complete;
626 } 633 }
627 ++src; 634 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
628 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; 635 /* fallthru */
629 break; 636 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
630 default: 637 for (;; ++src) {
631 assert(!"decoder is corrupt"); 638 if (src == bufsz) {
632 } 639 goto Exit;
633 } 640 }
641 if (buf[src] == '\012') {
642 break;
643 }
644 }
645 ++src;
646 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
647 break;
648 default:
649 assert(!"decoder is corrupt");
650 }
651 }
634 652
635Complete: 653Complete:
636 ret = bufsz - src; 654 ret = bufsz - src;
637Exit: 655Exit:
638 if (dst != src) 656 if (dst != src) {
639 memmove(buf + dst, buf + src, bufsz - src); 657 memmove(buf + dst, buf + src, bufsz - src);
640 *_bufsz = dst; 658 }
641 return ret; 659 *_bufsz = dst;
660 return ret;
642} 661}
643 662
644int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) 663int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) {
645{ 664 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
646 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
647} 665}
648 666
649#undef CHECK_EOF 667#undef CHECK_EOF
diff --git a/plugins/picohttpparser/picohttpparser.h b/plugins/picohttpparser/picohttpparser.h
index 8f13b36f..054c2812 100644
--- a/plugins/picohttpparser/picohttpparser.h
+++ b/plugins/picohttpparser/picohttpparser.h
@@ -30,7 +30,7 @@
30#include <sys/types.h> 30#include <sys/types.h>
31 31
32#ifdef _MSC_VER 32#ifdef _MSC_VER
33#define ssize_t intptr_t 33# define ssize_t intptr_t
34#endif 34#endif
35 35
36#ifdef __cplusplus 36#ifdef __cplusplus
@@ -40,30 +40,33 @@ extern "C" {
40/* contains name and value of a header (name == NULL if is a continuing line 40/* contains name and value of a header (name == NULL if is a continuing line
41 * of a multiline header */ 41 * of a multiline header */
42struct phr_header { 42struct phr_header {
43 const char *name; 43 const char *name;
44 size_t name_len; 44 size_t name_len;
45 const char *value; 45 const char *value;
46 size_t value_len; 46 size_t value_len;
47}; 47};
48 48
49/* returns number of bytes consumed if successful, -2 if request is partial, 49/* returns number of bytes consumed if successful, -2 if request is partial,
50 * -1 if failed */ 50 * -1 if failed */
51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len,
52 int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); 52 const char **path, size_t *path_len, int *major_version, int *minor_version,
53 struct phr_header *headers, size_t *num_headers, size_t last_len);
53 54
54/* ditto */ 55/* ditto */
55int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version, int *status, const char **msg, size_t *msg_len, 56int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version,
56 struct phr_header *headers, size_t *num_headers, size_t last_len); 57 int *status, const char **msg, size_t *msg_len, struct phr_header *headers,
58 size_t *num_headers, size_t last_len);
57 59
58/* ditto */ 60/* ditto */
59int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); 61int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers,
62 size_t last_len);
60 63
61/* should be zero-filled before start */ 64/* should be zero-filled before start */
62struct phr_chunked_decoder { 65struct phr_chunked_decoder {
63 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ 66 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
64 char consume_trailer; /* if trailing headers should be consumed */ 67 char consume_trailer; /* if trailing headers should be consumed */
65 char _hex_count; 68 char _hex_count;
66 char _state; 69 char _state;
67}; 70};
68 71
69/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- 72/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
diff --git a/plugins/popen.c b/plugins/popen.c
index 54e63bc5..c596d1e0 100644
--- a/plugins/popen.c
+++ b/plugins/popen.c
@@ -1,294 +1,302 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins popen 3 * Monitoring Plugins popen
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2005-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2005-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* A safe alternative to popen 10 * A safe alternative to popen
11* 11 *
12* Provides spopen and spclose 12 * Provides spopen and spclose
13* 13 *
14* FILE * spopen(const char *); 14 * FILE * spopen(const char *);
15* int spclose(FILE *); 15 * int spclose(FILE *);
16* 16 *
17* Code taken with little modification from "Advanced Programming for the Unix 17 * Code taken with little modification from "Advanced Programming for the Unix
18* Environment" by W. Richard Stevens 18 * Environment" by W. Richard Stevens
19* 19 *
20* This is considered safe in that no shell is spawned, and the environment 20 * This is considered safe in that no shell is spawned, and the environment
21* and path passed to the exec'd program are essentially empty. (popen create 21 * and path passed to the exec'd program are essentially empty. (popen create
22* a shell and passes the environment to it). 22 * a shell and passes the environment to it).
23* 23 *
24* 24 *
25* This program is free software: you can redistribute it and/or modify 25 * This program is free software: you can redistribute it and/or modify
26* it under the terms of the GNU General Public License as published by 26 * it under the terms of the GNU General Public License as published by
27* the Free Software Foundation, either version 3 of the License, or 27 * the Free Software Foundation, either version 3 of the License, or
28* (at your option) any later version. 28 * (at your option) any later version.
29* 29 *
30* This program is distributed in the hope that it will be useful, 30 * This program is distributed in the hope that it will be useful,
31* but WITHOUT ANY WARRANTY; without even the implied warranty of 31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33* GNU General Public License for more details. 33 * GNU General Public License for more details.
34* 34 *
35* You should have received a copy of the GNU General Public License 35 * You should have received a copy of the GNU General Public License
36* along with this program. If not, see <http://www.gnu.org/licenses/>. 36 * along with this program. If not, see <http://www.gnu.org/licenses/>.
37* 37 *
38* 38 *
39*****************************************************************************/ 39 *****************************************************************************/
40 40
41#include "./common.h" 41#include "./common.h"
42#include "./utils.h" 42#include "./utils.h"
43#include "../lib/maxfd.h"
44 43
45/* extern so plugin has pid to kill exec'd process on timeouts */ 44/* extern so plugin has pid to kill exec'd process on timeouts */
46extern pid_t *childpid; 45extern pid_t *childpid;
47extern int *child_stderr_array; 46extern int *child_stderr_array;
48extern FILE *child_process; 47extern FILE *child_process;
49 48
50FILE *spopen (const char *); 49FILE *spopen(const char * /*cmdstring*/);
51int spclose (FILE *); 50int spclose(FILE * /*fp*/);
52#ifdef REDHAT_SPOPEN_ERROR 51#ifdef REDHAT_SPOPEN_ERROR
53void popen_sigchld_handler (int); 52void popen_sigchld_handler(int);
54#endif 53#endif
55void popen_timeout_alarm_handler (int); 54void popen_timeout_alarm_handler(int /*signo*/);
56 55
57#include <stdarg.h> /* ANSI C header file */ 56#include <stdarg.h> /* ANSI C header file */
58#include <fcntl.h> 57#include <fcntl.h>
59 58
60#include <limits.h> 59#include <limits.h>
61#include <sys/resource.h> 60#include <sys/resource.h>
62 61
63#ifdef HAVE_SYS_WAIT_H 62#ifdef HAVE_SYS_WAIT_H
64#include <sys/wait.h> 63# include <sys/wait.h>
65#endif 64#endif
66 65
67#ifndef WEXITSTATUS 66#ifndef WEXITSTATUS
68# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 67# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
69#endif 68#endif
70 69
71#ifndef WIFEXITED 70#ifndef WIFEXITED
72# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 71# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
73#endif 72#endif
74 73
75/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ 74/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
76#if defined(SIG_IGN) && !defined(SIG_ERR) 75#if defined(SIG_IGN) && !defined(SIG_ERR)
77#define SIG_ERR ((Sigfunc *)-1) 76# define SIG_ERR ((Sigfunc *)-1)
78#endif 77#endif
79 78
80 79char *pname = NULL; /* caller can set this from argv[0] */
81char *pname = NULL; /* caller can set this from argv[0] */
82 80
83#ifdef REDHAT_SPOPEN_ERROR 81#ifdef REDHAT_SPOPEN_ERROR
84static volatile int childtermd = 0; 82static volatile int childtermd = 0;
85#endif 83#endif
86 84
87FILE * 85FILE *spopen(const char *cmdstring) {
88spopen (const char *cmdstring) 86#ifdef RLIMIT_CORE
89{
90 char *env[2];
91 char *cmd = NULL;
92 char **argv = NULL;
93 char *str, *tmp;
94 int argc;
95
96 int i = 0, pfd[2], pfderr[2];
97 pid_t pid;
98
99#ifdef RLIMIT_CORE
100 /* do not leave core files */ 87 /* do not leave core files */
101 struct rlimit limit; 88 struct rlimit limit;
102 getrlimit (RLIMIT_CORE, &limit); 89 getrlimit(RLIMIT_CORE, &limit);
103 limit.rlim_cur = 0; 90 limit.rlim_cur = 0;
104 setrlimit (RLIMIT_CORE, &limit); 91 setrlimit(RLIMIT_CORE, &limit);
105#endif 92#endif
106 93
94 char *env[2];
107 env[0] = strdup("LC_ALL=C"); 95 env[0] = strdup("LC_ALL=C");
108 env[1] = NULL; 96 env[1] = NULL;
109 97
110 /* if no command was passed, return with no error */ 98 /* if no command was passed, return with no error */
111 if (cmdstring == NULL) 99 if (cmdstring == NULL) {
112 return (NULL); 100 return (NULL);
101 }
113 102
103 char *cmd = NULL;
114 /* make copy of command string so strtok() doesn't silently modify it */ 104 /* make copy of command string so strtok() doesn't silently modify it */
115 /* (the calling program may want to access it later) */ 105 /* (the calling program may want to access it later) */
116 cmd = malloc (strlen (cmdstring) + 1); 106 cmd = malloc(strlen(cmdstring) + 1);
117 if (cmd == NULL) 107 if (cmd == NULL) {
118 return NULL; 108 return NULL;
119 strcpy (cmd, cmdstring); 109 }
110 strcpy(cmd, cmdstring);
120 111
121 /* This is not a shell, so we don't handle "???" */ 112 /* This is not a shell, so we don't handle "???" */
122 if (strstr (cmdstring, "\"")) 113 if (strstr(cmdstring, "\"")) {
123 return NULL; 114 return NULL;
115 }
124 116
125 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ 117 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
126 if (strstr (cmdstring, " ' ") || strstr (cmdstring, "'''")) 118 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
127 return NULL; 119 return NULL;
120 }
128 121
122 int argc;
123 char **argv = NULL;
129 /* there cannot be more args than characters */ 124 /* there cannot be more args than characters */
130 argc = strlen (cmdstring) + 1; /* add 1 for NULL termination */ 125 argc = strlen(cmdstring) + 1; /* add 1 for NULL termination */
131 argv = malloc (sizeof(char*)*argc); 126 argv = malloc(sizeof(char *) * argc);
132 127
133 if (argv == NULL) { 128 if (argv == NULL) {
134 printf ("%s\n", _("Could not malloc argv array in popen()")); 129 printf("%s\n", _("Could not malloc argv array in popen()"));
135 return NULL; 130 return NULL;
136 } 131 }
137 132
133 int i = 0;
134 char *str;
138 /* loop to get arguments to command */ 135 /* loop to get arguments to command */
139 while (cmd) { 136 while (cmd) {
140 str = cmd; 137 str = cmd;
141 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */ 138 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
142 139
143 if (i >= argc - 2) { 140 if (i >= argc - 2) {
144 printf ("%s\n",_("CRITICAL - You need more args!!!")); 141 printf("%s\n", _("CRITICAL - You need more args!!!"));
145 return (NULL); 142 return (NULL);
146 } 143 }
147 144
148 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */ 145 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
149 str++; 146 str++;
150 if (!strstr (str, "'")) 147 if (!strstr(str, "'")) {
151 return NULL; /* balanced? */ 148 return NULL; /* balanced? */
152 cmd = 1 + strstr (str, "'"); 149 }
153 str[strcspn (str, "'")] = 0; 150 cmd = 1 + strstr(str, "'");
154 } 151 str[strcspn(str, "'")] = 0;
155 else if (strcspn(str,"'") < strcspn (str, " \t\r\n")) { 152 } else if (strcspn(str, "'") < strcspn(str, " \t\r\n")) {
156 /* handle --option='foo bar' strings */ 153 /* handle --option='foo bar' strings */
157 tmp = str + strcspn(str, "'") + 1; 154 char *tmp = str + strcspn(str, "'") + 1;
158 if (!strstr (tmp, "'")) 155 if (!strstr(tmp, "'")) {
159 return NULL; /* balanced? */ 156 return NULL; /* balanced? */
160 tmp += strcspn(tmp,"'") + 1; 157 }
158 tmp += strcspn(tmp, "'") + 1;
161 *tmp = 0; 159 *tmp = 0;
162 cmd = tmp + 1; 160 cmd = tmp + 1;
163 } else { 161 } else {
164 if (strpbrk (str, " \t\r\n")) { 162 if (strpbrk(str, " \t\r\n")) {
165 cmd = 1 + strpbrk (str, " \t\r\n"); 163 cmd = 1 + strpbrk(str, " \t\r\n");
166 str[strcspn (str, " \t\r\n")] = 0; 164 str[strcspn(str, " \t\r\n")] = 0;
167 } 165 } else {
168 else {
169 cmd = NULL; 166 cmd = NULL;
170 } 167 }
171 } 168 }
172 169
173 if (cmd && strlen (cmd) == strspn (cmd, " \t\r\n")) 170 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
174 cmd = NULL; 171 cmd = NULL;
172 }
175 173
176 argv[i++] = str; 174 argv[i++] = str;
177
178 } 175 }
179 argv[i] = NULL; 176 argv[i] = NULL;
180 177
181 long maxfd = mp_open_max(); 178 long maxfd = mp_open_max();
182 179
183 if (childpid == NULL) { /* first time through */ 180 if (childpid == NULL) { /* first time through */
184 if ((childpid = calloc ((size_t)maxfd, sizeof (pid_t))) == NULL) 181 if ((childpid = calloc((size_t)maxfd, sizeof(pid_t))) == NULL) {
185 return (NULL); 182 return (NULL);
183 }
186 } 184 }
187 185
188 if (child_stderr_array == NULL) { /* first time through */ 186 if (child_stderr_array == NULL) { /* first time through */
189 if ((child_stderr_array = calloc ((size_t)maxfd, sizeof (int))) == NULL) 187 if ((child_stderr_array = calloc((size_t)maxfd, sizeof(int))) == NULL) {
190 return (NULL); 188 return (NULL);
189 }
191 } 190 }
192 191
193 if (pipe (pfd) < 0) 192 int pfd[2];
194 return (NULL); /* errno set by pipe() */ 193 if (pipe(pfd) < 0) {
194 return (NULL); /* errno set by pipe() */
195 }
195 196
196 if (pipe (pfderr) < 0) 197 int pfderr[2];
197 return (NULL); /* errno set by pipe() */ 198 if (pipe(pfderr) < 0) {
199 return (NULL); /* errno set by pipe() */
200 }
198 201
199#ifdef REDHAT_SPOPEN_ERROR 202#ifdef REDHAT_SPOPEN_ERROR
200 if (signal (SIGCHLD, popen_sigchld_handler) == SIG_ERR) { 203 if (signal(SIGCHLD, popen_sigchld_handler) == SIG_ERR) {
201 usage4 (_("Cannot catch SIGCHLD")); 204 usage4(_("Cannot catch SIGCHLD"));
202 } 205 }
203#endif 206#endif
204 207
205 if ((pid = fork ()) < 0) 208 pid_t pid;
206 return (NULL); /* errno set by fork() */ 209 if ((pid = fork()) < 0) {
207 else if (pid == 0) { /* child */ 210 return (NULL); /* errno set by fork() */
208 close (pfd[0]); 211 }
212
213 if (pid == 0) { /* child */
214 close(pfd[0]);
209 if (pfd[1] != STDOUT_FILENO) { 215 if (pfd[1] != STDOUT_FILENO) {
210 dup2 (pfd[1], STDOUT_FILENO); 216 dup2(pfd[1], STDOUT_FILENO);
211 close (pfd[1]); 217 close(pfd[1]);
212 } 218 }
213 close (pfderr[0]); 219 close(pfderr[0]);
214 if (pfderr[1] != STDERR_FILENO) { 220 if (pfderr[1] != STDERR_FILENO) {
215 dup2 (pfderr[1], STDERR_FILENO); 221 dup2(pfderr[1], STDERR_FILENO);
216 close (pfderr[1]); 222 close(pfderr[1]);
217 } 223 }
218 /* close all descriptors in childpid[] */ 224 /* close all descriptors in childpid[] */
219 for (i = 0; i < maxfd; i++) 225 for (i = 0; i < maxfd; i++) {
220 if (childpid[i] > 0) 226 if (childpid[i] > 0) {
221 close (i); 227 close(i);
228 }
229 }
222 230
223 execve (argv[0], argv, env); 231 execve(argv[0], argv, env);
224 _exit (0); 232 _exit(0);
225 } 233 }
226 234
227 close (pfd[1]); /* parent */ 235 close(pfd[1]); /* parent */
228 if ((child_process = fdopen (pfd[0], "r")) == NULL) 236 if ((child_process = fdopen(pfd[0], "r")) == NULL) {
229 return (NULL); 237 return (NULL);
230 close (pfderr[1]); 238 }
239 close(pfderr[1]);
231 240
232 childpid[fileno (child_process)] = pid; /* remember child pid for this fd */ 241 childpid[fileno(child_process)] = pid; /* remember child pid for this fd */
233 child_stderr_array[fileno (child_process)] = pfderr[0]; /* remember STDERR */ 242 child_stderr_array[fileno(child_process)] = pfderr[0]; /* remember STDERR */
234 return (child_process); 243 return (child_process);
235} 244}
236 245
237int 246int spclose(FILE *fp) {
238spclose (FILE * fp) 247 if (childpid == NULL) {
239{ 248 return (1); /* popen() has never been called */
240 int fd, status; 249 }
241 pid_t pid;
242
243 if (childpid == NULL)
244 return (1); /* popen() has never been called */
245 250
246 fd = fileno (fp); 251 pid_t pid;
247 if ((pid = childpid[fd]) == 0) 252 int fd = fileno(fp);
248 return (1); /* fp wasn't opened by popen() */ 253 if ((pid = childpid[fd]) == 0) {
254 return (1); /* fp wasn't opened by popen() */
255 }
249 256
250 childpid[fd] = 0; 257 childpid[fd] = 0;
251 if (fclose (fp) == EOF) 258 if (fclose(fp) == EOF) {
252 return (1); 259 return (1);
260 }
253 261
254#ifdef REDHAT_SPOPEN_ERROR 262#ifdef REDHAT_SPOPEN_ERROR
255 while (!childtermd); /* wait until SIGCHLD */ 263 while (!childtermd)
264 ; /* wait until SIGCHLD */
256#endif 265#endif
257 266
258 while (waitpid (pid, &status, 0) < 0) 267 int status;
259 if (errno != EINTR) 268 while (waitpid(pid, &status, 0) < 0) {
260 return (1); /* error other than EINTR from waitpid() */ 269 if (errno != EINTR) {
270 return (1); /* error other than EINTR from waitpid() */
271 }
272 }
261 273
262 if (WIFEXITED (status)) 274 if (WIFEXITED(status)) {
263 return (WEXITSTATUS (status)); /* return child's termination status */ 275 return (WEXITSTATUS(status)); /* return child's termination status */
276 }
264 277
265 return (1); 278 return (1);
266} 279}
267 280
268#ifdef REDHAT_SPOPEN_ERROR 281#ifdef REDHAT_SPOPEN_ERROR
269void 282void popen_sigchld_handler(int signo) {
270popen_sigchld_handler (int signo) 283 if (signo == SIGCHLD) {
271{
272 if (signo == SIGCHLD)
273 childtermd = 1; 284 childtermd = 1;
285 }
274} 286}
275#endif 287#endif
276 288
277void 289void popen_timeout_alarm_handler(int signo) {
278popen_timeout_alarm_handler (int signo)
279{
280 int fh;
281 if (signo == SIGALRM) { 290 if (signo == SIGALRM) {
282 if (child_process != NULL) { 291 if (child_process != NULL) {
283 fh=fileno (child_process); 292 int fh = fileno(child_process);
284 if(fh >= 0){ 293 if (fh >= 0) {
285 kill (childpid[fh], SIGKILL); 294 kill(childpid[fh], SIGKILL);
286 } 295 }
287 printf (_("CRITICAL - Plugin timed out after %d seconds\n"), 296 printf(_("CRITICAL - Plugin timed out after %d seconds\n"), timeout_interval);
288 timeout_interval);
289 } else { 297 } else {
290 printf ("%s\n", _("CRITICAL - popen timeout received, but no child process")); 298 printf("%s\n", _("CRITICAL - popen timeout received, but no child process"));
291 } 299 }
292 exit (STATE_CRITICAL); 300 exit(STATE_CRITICAL);
293 } 301 }
294} 302}
diff --git a/plugins/popen.h b/plugins/popen.h
index 1ea69632..e318ce25 100644
--- a/plugins/popen.h
+++ b/plugins/popen.h
@@ -1,13 +1,13 @@
1/****************************************************************************** 1/******************************************************************************
2* 2 *
3* 3 *
4*****************************************************************************/ 4 *****************************************************************************/
5 5
6FILE *spopen (const char *); 6FILE *spopen(const char *);
7int spclose (FILE *); 7int spclose(FILE *);
8void popen_timeout_alarm_handler (int); 8void popen_timeout_alarm_handler(int);
9 9
10pid_t *childpid=NULL; 10pid_t *childpid = NULL;
11int *child_stderr_array=NULL; 11int *child_stderr_array = NULL;
12FILE *child_process=NULL; 12FILE *child_process = NULL;
13FILE *child_stderr=NULL; 13FILE *child_stderr = NULL;
diff --git a/plugins/runcmd.c b/plugins/runcmd.c
index ed49bb99..be6691d2 100644
--- a/plugins/runcmd.c
+++ b/plugins/runcmd.c
@@ -1,63 +1,64 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring run command utilities 3 * Monitoring run command utilities
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2005-2006 Monitoring Plugins Development Team 6 * Copyright (c) 2005-2024 Monitoring Plugins Development Team
7* 7 *
8* Description : 8 * Description :
9* 9 *
10* A simple interface to executing programs from other programs, using an 10 * A simple interface to executing programs from other programs, using an
11* optimized and safe popen()-like implementation. It is considered safe 11 * optimized and safe popen()-like implementation. It is considered safe
12* in that no shell needs to be spawned and the environment passed to the 12 * in that no shell needs to be spawned and the environment passed to the
13* execve()'d program is essentially empty. 13 * execve()'d program is essentially empty.
14* 14 *
15* The code in this file is a derivative of popen.c which in turn was taken 15 * The code in this file is a derivative of popen.c which in turn was taken
16* from "Advanced Programming for the Unix Environment" by W. Richard Stevens. 16 * from "Advanced Programming for the Unix Environment" by W. Richard Stevens.
17* 17 *
18* Care has been taken to make sure the functions are async-safe. The one 18 * Care has been taken to make sure the functions are async-safe. The one
19* function which isn't is np_runcmd_init() which it doesn't make sense to 19 * function which isn't is np_runcmd_init() which it doesn't make sense to
20* call twice anyway, so the api as a whole should be considered async-safe. 20 * call twice anyway, so the api as a whole should be considered async-safe.
21* 21 *
22* 22 *
23* This program is free software: you can redistribute it and/or modify 23 * This program is free software: you can redistribute it and/or modify
24* it under the terms of the GNU General Public License as published by 24 * it under the terms of the GNU General Public License as published by
25* the Free Software Foundation, either version 3 of the License, or 25 * the Free Software Foundation, either version 3 of the License, or
26* (at your option) any later version. 26 * (at your option) any later version.
27* 27 *
28* This program is distributed in the hope that it will be useful, 28 * This program is distributed in the hope that it will be useful,
29* but WITHOUT ANY WARRANTY; without even the implied warranty of 29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31* GNU General Public License for more details. 31 * GNU General Public License for more details.
32* 32 *
33* You should have received a copy of the GNU General Public License 33 * You should have received a copy of the GNU General Public License
34* along with this program. If not, see <http://www.gnu.org/licenses/>. 34 * along with this program. If not, see <http://www.gnu.org/licenses/>.
35* 35 *
36* 36 *
37*****************************************************************************/ 37 *****************************************************************************/
38 38
39#define NAGIOSPLUG_API_C 1 39#define NAGIOSPLUG_API_C 1
40 40
41/** includes **/ 41/** includes **/
42#include "runcmd.h" 42#include "runcmd.h"
43#include "../lib/monitoringplug.h"
43#ifdef HAVE_SYS_WAIT_H 44#ifdef HAVE_SYS_WAIT_H
44# include <sys/wait.h> 45# include <sys/wait.h>
45#endif 46#endif
46 47
47#include "./utils.h" 48#include "./utils.h"
48 49
49/** macros **/ 50/** macros **/
50#ifndef WEXITSTATUS 51#ifndef WEXITSTATUS
51# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 52# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
52#endif 53#endif
53 54
54#ifndef WIFEXITED 55#ifndef WIFEXITED
55# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 56# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
56#endif 57#endif
57 58
58/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ 59/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
59#if defined(SIG_IGN) && !defined(SIG_ERR) 60#if defined(SIG_IGN) && !defined(SIG_ERR)
60# define SIG_ERR ((Sigfunc *)-1) 61# define SIG_ERR ((Sigfunc *)-1)
61#endif 62#endif
62 63
63#include "../lib/maxfd.h" 64#include "../lib/maxfd.h"
@@ -72,33 +73,27 @@
72static pid_t *np_pids = NULL; 73static pid_t *np_pids = NULL;
73 74
74/** prototypes **/ 75/** prototypes **/
75static int np_runcmd_open(const char *, int *, int *) 76static int np_runcmd_open(const char *, int *, int *) __attribute__((__nonnull__(1, 2, 3)));
76 __attribute__((__nonnull__(1, 2, 3)));
77 77
78static int np_fetch_output(int, output *, int) 78static int np_fetch_output(int, output *, int) __attribute__((__nonnull__(2)));
79 __attribute__((__nonnull__(2)));
80 79
81static int np_runcmd_close(int); 80static int np_runcmd_close(int);
82 81
83/* prototype imported from utils.h */ 82/* prototype imported from utils.h */
84extern void die (int, const char *, ...) 83extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
85 __attribute__((__noreturn__,__format__(__printf__, 2, 3)));
86
87 84
88/* this function is NOT async-safe. It is exported so multithreaded 85/* this function is NOT async-safe. It is exported so multithreaded
89 * plugins (or other apps) can call it prior to running any commands 86 * plugins (or other apps) can call it prior to running any commands
90 * through this api and thus achieve async-safeness throughout the api */ 87 * through this api and thus achieve async-safeness throughout the api */
91void np_runcmd_init(void) 88void np_runcmd_init(void) {
92{ 89 long maxfd = mp_open_max();
93 long maxfd = mp_open_max(); 90 if (!np_pids) {
94 if(!np_pids) np_pids = calloc(maxfd, sizeof(pid_t)); 91 np_pids = calloc(maxfd, sizeof(pid_t));
92 }
95} 93}
96 94
97
98/* Start running a command */ 95/* Start running a command */
99static int 96static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
100np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
101{
102 char *env[2]; 97 char *env[2];
103 char *cmd = NULL; 98 char *cmd = NULL;
104 char **argv = NULL; 99 char **argv = NULL;
@@ -112,7 +107,9 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
112 107
113 int i = 0; 108 int i = 0;
114 109
115 if(!np_pids) NP_RUNCMD_INIT; 110 if (!np_pids) {
111 NP_RUNCMD_INIT;
112 }
116 113
117 env[0] = strdup("LC_ALL=C"); 114 env[0] = strdup("LC_ALL=C");
118 env[1] = NULL; 115 env[1] = NULL;
@@ -120,86 +117,95 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
120 /* make copy of command string so strtok() doesn't silently modify it */ 117 /* make copy of command string so strtok() doesn't silently modify it */
121 /* (the calling program may want to access it later) */ 118 /* (the calling program may want to access it later) */
122 cmdlen = strlen(cmdstring); 119 cmdlen = strlen(cmdstring);
123 if((cmd = malloc(cmdlen + 1)) == NULL) return -1; 120 if ((cmd = malloc(cmdlen + 1)) == NULL) {
121 return -1;
122 }
124 memcpy(cmd, cmdstring, cmdlen); 123 memcpy(cmd, cmdstring, cmdlen);
125 cmd[cmdlen] = '\0'; 124 cmd[cmdlen] = '\0';
126 125
127 /* This is not a shell, so we don't handle "???" */ 126 /* This is not a shell, so we don't handle "???" */
128 if (strstr (cmdstring, "\"")) return -1; 127 if (strstr(cmdstring, "\"")) {
128 return -1;
129 }
129 130
130 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ 131 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
131 if (strstr (cmdstring, " ' ") || strstr (cmdstring, "'''")) 132 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
132 return -1; 133 return -1;
134 }
133 135
134 /* each arg must be whitespace-separated, so args can be a maximum 136 /* each arg must be whitespace-separated, so args can be a maximum
135 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ 137 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */
136 argc = (cmdlen >> 1) + 2; 138 argc = (cmdlen >> 1) + 2;
137 argv = calloc(sizeof(char *), argc); 139 argv = calloc(argc, sizeof(char *));
138 140
139 if (argv == NULL) { 141 if (argv == NULL) {
140 printf ("%s\n", _("Could not malloc argv array in popen()")); 142 printf("%s\n", _("Could not malloc argv array in popen()"));
141 return -1; 143 return -1;
142 } 144 }
143 145
144 /* get command arguments (stupidly, but fairly quickly) */ 146 /* get command arguments (stupidly, but fairly quickly) */
145 while (cmd) { 147 while (cmd) {
146 str = cmd; 148 str = cmd;
147 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */ 149 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
148 150
149 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */ 151 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
150 str++; 152 str++;
151 if (!strstr (str, "'")) return -1; /* balanced? */ 153 if (!strstr(str, "'")) {
152 cmd = 1 + strstr (str, "'"); 154 return -1; /* balanced? */
153 str[strcspn (str, "'")] = 0;
154 }
155 else {
156 if (strpbrk (str, " \t\r\n")) {
157 cmd = 1 + strpbrk (str, " \t\r\n");
158 str[strcspn (str, " \t\r\n")] = 0;
159 } 155 }
160 else { 156 cmd = 1 + strstr(str, "'");
157 str[strcspn(str, "'")] = 0;
158 } else {
159 if (strpbrk(str, " \t\r\n")) {
160 cmd = 1 + strpbrk(str, " \t\r\n");
161 str[strcspn(str, " \t\r\n")] = 0;
162 } else {
161 cmd = NULL; 163 cmd = NULL;
162 } 164 }
163 } 165 }
164 166
165 if (cmd && strlen (cmd) == strspn (cmd, " \t\r\n")) 167 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
166 cmd = NULL; 168 cmd = NULL;
169 }
167 170
168 argv[i++] = str; 171 argv[i++] = str;
169 } 172 }
170 173
171 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) 174 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) {
172 return -1; /* errno set by the failing function */ 175 return -1; /* errno set by the failing function */
176 }
173 177
174 /* child runs exceve() and _exit. */ 178 /* child runs exceve() and _exit. */
175 if (pid == 0) { 179 if (pid == 0) {
176#ifdef RLIMIT_CORE 180#ifdef RLIMIT_CORE
177 /* the program we execve shouldn't leave core files */ 181 /* the program we execve shouldn't leave core files */
178 getrlimit (RLIMIT_CORE, &limit); 182 getrlimit(RLIMIT_CORE, &limit);
179 limit.rlim_cur = 0; 183 limit.rlim_cur = 0;
180 setrlimit (RLIMIT_CORE, &limit); 184 setrlimit(RLIMIT_CORE, &limit);
181#endif 185#endif
182 close (pfd[0]); 186 close(pfd[0]);
183 if (pfd[1] != STDOUT_FILENO) { 187 if (pfd[1] != STDOUT_FILENO) {
184 dup2 (pfd[1], STDOUT_FILENO); 188 dup2(pfd[1], STDOUT_FILENO);
185 close (pfd[1]); 189 close(pfd[1]);
186 } 190 }
187 close (pfderr[0]); 191 close(pfderr[0]);
188 if (pfderr[1] != STDERR_FILENO) { 192 if (pfderr[1] != STDERR_FILENO) {
189 dup2 (pfderr[1], STDERR_FILENO); 193 dup2(pfderr[1], STDERR_FILENO);
190 close (pfderr[1]); 194 close(pfderr[1]);
191 } 195 }
192 196
193 /* close all descriptors in np_pids[] 197 /* close all descriptors in np_pids[]
194 * This is executed in a separate address space (pure child), 198 * This is executed in a separate address space (pure child),
195 * so we don't have to worry about async safety */ 199 * so we don't have to worry about async safety */
196 long maxfd = mp_open_max(); 200 long maxfd = mp_open_max();
197 for (i = 0; i < maxfd; i++) 201 for (i = 0; i < maxfd; i++) {
198 if(np_pids[i] > 0) 202 if (np_pids[i] > 0) {
199 close (i); 203 close(i);
204 }
205 }
200 206
201 execve (argv[0], argv, env); 207 execve(argv[0], argv, env);
202 _exit (STATE_UNKNOWN); 208 _exit(STATE_UNKNOWN);
203 } 209 }
204 210
205 /* parent picks up execution here */ 211 /* parent picks up execution here */
@@ -213,49 +219,51 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
213 return pfd[0]; 219 return pfd[0];
214} 220}
215 221
216 222static int np_runcmd_close(int fd) {
217static int
218np_runcmd_close(int fd)
219{
220 int status; 223 int status;
221 pid_t pid; 224 pid_t pid;
222 225
223 /* make sure this fd was opened by popen() */ 226 /* make sure this fd was opened by popen() */
224 long maxfd = mp_open_max(); 227 long maxfd = mp_open_max();
225 if(fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) 228 if (fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) {
226 return -1; 229 return -1;
230 }
227 231
228 np_pids[fd] = 0; 232 np_pids[fd] = 0;
229 if (close (fd) == -1) return -1; 233 if (close(fd) == -1) {
234 return -1;
235 }
230 236
231 /* EINTR is ok (sort of), everything else is bad */ 237 /* EINTR is ok (sort of), everything else is bad */
232 while (waitpid (pid, &status, 0) < 0) 238 while (waitpid(pid, &status, 0) < 0) {
233 if (errno != EINTR) return -1; 239 if (errno != EINTR) {
240 return -1;
241 }
242 }
234 243
235 /* return child's termination status */ 244 /* return child's termination status */
236 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; 245 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
237} 246}
238 247
248void runcmd_timeout_alarm_handler(int signo) {
239 249
240void 250 if (signo == SIGALRM) {
241runcmd_timeout_alarm_handler (int signo)
242{
243
244 if (signo == SIGALRM)
245 puts(_("CRITICAL - Plugin timed out while executing system call")); 251 puts(_("CRITICAL - Plugin timed out while executing system call"));
252 }
246 253
247 long maxfd = mp_open_max(); 254 long maxfd = mp_open_max();
248 if(np_pids) for(long int i = 0; i < maxfd; i++) { 255 if (np_pids) {
249 if(np_pids[i] != 0) kill(np_pids[i], SIGKILL); 256 for (long int i = 0; i < maxfd; i++) {
257 if (np_pids[i] != 0) {
258 kill(np_pids[i], SIGKILL);
259 }
260 }
250 } 261 }
251 262
252 exit (STATE_CRITICAL); 263 exit(STATE_CRITICAL);
253} 264}
254 265
255 266static int np_fetch_output(int fd, output *op, int flags) {
256static int
257np_fetch_output(int fd, output *op, int flags)
258{
259 size_t len = 0, i = 0, lineno = 0; 267 size_t len = 0, i = 0, lineno = 0;
260 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ 268 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
261 char *buf = NULL; 269 char *buf = NULL;
@@ -264,7 +272,7 @@ np_fetch_output(int fd, output *op, int flags)
264 272
265 op->buf = NULL; 273 op->buf = NULL;
266 op->buflen = 0; 274 op->buflen = 0;
267 while((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) { 275 while ((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) {
268 len = (size_t)ret; 276 len = (size_t)ret;
269 op->buf = realloc(op->buf, op->buflen + len + 1); 277 op->buf = realloc(op->buf, op->buflen + len + 1);
270 memcpy(op->buf + op->buflen, tmpbuf, len); 278 memcpy(op->buf + op->buflen, tmpbuf, len);
@@ -272,48 +280,47 @@ np_fetch_output(int fd, output *op, int flags)
272 i++; 280 i++;
273 } 281 }
274 282
275 if(ret < 0) { 283 if (ret < 0) {
276 printf("read() returned %d: %s\n", ret, strerror(errno)); 284 printf("read() returned %d: %s\n", ret, strerror(errno));
277 return ret; 285 return ret;
278 } 286 }
279 287
280 /* some plugins may want to keep output unbroken, and some commands 288 /* some plugins may want to keep output unbroken, and some commands
281 * will yield no output, so return here for those */ 289 * will yield no output, so return here for those */
282 if(flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) 290 if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) {
283 return op->buflen; 291 return op->buflen;
292 }
284 293
285 /* and some may want both */ 294 /* and some may want both */
286 if(flags & RUNCMD_NO_ASSOC) { 295 if (flags & RUNCMD_NO_ASSOC) {
287 buf = malloc(op->buflen); 296 buf = malloc(op->buflen);
288 memcpy(buf, op->buf, op->buflen); 297 memcpy(buf, op->buf, op->buflen);
298 } else {
299 buf = op->buf;
289 } 300 }
290 else buf = op->buf;
291 301
292 op->line = NULL; 302 op->line = NULL;
293 op->lens = NULL;
294 i = 0; 303 i = 0;
295 while(i < op->buflen) { 304 while (i < op->buflen) {
296 /* make sure we have enough memory */ 305 /* make sure we have enough memory */
297 if(lineno >= ary_size) { 306 if (lineno >= ary_size) {
298 /* ary_size must never be zero */ 307 /* ary_size must never be zero */
299 do { 308 do {
300 ary_size = op->buflen >> --rsf; 309 ary_size = op->buflen >> --rsf;
301 } while(!ary_size); 310 } while (!ary_size);
302 311
303 op->line = realloc(op->line, ary_size * sizeof(char *)); 312 op->line = realloc(op->line, ary_size * sizeof(char *));
304 op->lens = realloc(op->lens, ary_size * sizeof(size_t));
305 } 313 }
306 314
307 /* set the pointer to the string */ 315 /* set the pointer to the string */
308 op->line[lineno] = &buf[i]; 316 op->line[lineno] = &buf[i];
309 317
310 /* hop to next newline or end of buffer */ 318 /* hop to next newline or end of buffer */
311 while(buf[i] != '\n' && i < op->buflen) i++; 319 while (buf[i] != '\n' && i < op->buflen) {
320 i++;
321 }
312 buf[i] = '\0'; 322 buf[i] = '\0';
313 323
314 /* calculate the string length using pointer difference */
315 op->lens[lineno] = (size_t)&buf[i] - (size_t)op->line[lineno];
316
317 lineno++; 324 lineno++;
318 i++; 325 i++;
319 } 326 }
@@ -321,21 +328,27 @@ np_fetch_output(int fd, output *op, int flags)
321 return lineno; 328 return lineno;
322} 329}
323 330
324 331int np_runcmd(const char *cmd, output *out, output *err, int flags) {
325int
326np_runcmd(const char *cmd, output *out, output *err, int flags)
327{
328 int fd, pfd_out[2], pfd_err[2]; 332 int fd, pfd_out[2], pfd_err[2];
329 333
330 /* initialize the structs */ 334 /* initialize the structs */
331 if(out) memset(out, 0, sizeof(output)); 335 if (out) {
332 if(err) memset(err, 0, sizeof(output)); 336 memset(out, 0, sizeof(output));
337 }
338 if (err) {
339 memset(err, 0, sizeof(output));
340 }
333 341
334 if((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) 342 if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) {
335 die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 343 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
344 }
336 345
337 if(out) out->lines = np_fetch_output(pfd_out[0], out, flags); 346 if (out) {
338 if(err) err->lines = np_fetch_output(pfd_err[0], err, flags); 347 out->lines = np_fetch_output(pfd_out[0], out, flags);
348 }
349 if (err) {
350 err->lines = np_fetch_output(pfd_err[0], err, flags);
351 }
339 352
340 return np_runcmd_close(fd); 353 return np_runcmd_close(fd);
341} 354}
diff --git a/plugins/runcmd.h b/plugins/runcmd.h
index 2dcdadf0..63ce7b12 100644
--- a/plugins/runcmd.h
+++ b/plugins/runcmd.h
@@ -1,25 +1,25 @@
1/**************************************************************************** 1/****************************************************************************
2* 2 *
3* License: GPL 3 * License: GPL
4* Copyright (c) 2005 Monitoring Plugins Development Team 4 * Copyright (c) 2005 Monitoring Plugins Development Team
5* Author: Andreas Ericsson <ae@op5.se> 5 * Author: Andreas Ericsson <ae@op5.se>
6* 6 *
7* 7 *
8* This program is free software: you can redistribute it and/or modify 8 * This program is free software: you can redistribute it and/or modify
9* it under the terms of the GNU General Public License as published by 9 * it under the terms of the GNU General Public License as published by
10* the Free Software Foundation, either version 3 of the License, or 10 * the Free Software Foundation, either version 3 of the License, or
11* (at your option) any later version. 11 * (at your option) any later version.
12* 12 *
13* This program is distributed in the hope that it will be useful, 13 * This program is distributed in the hope that it will be useful,
14* but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16* GNU General Public License for more details. 16 * GNU General Public License for more details.
17* 17 *
18* You should have received a copy of the GNU General Public License 18 * You should have received a copy of the GNU General Public License
19* along with this program. If not, see <http://www.gnu.org/licenses/>. 19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20* 20 *
21* 21 *
22*****************************************************************************/ 22 *****************************************************************************/
23 23
24#ifndef NAGIOSPLUG_RUNCMD_H 24#ifndef NAGIOSPLUG_RUNCMD_H
25#define NAGIOSPLUG_RUNCMD_H 25#define NAGIOSPLUG_RUNCMD_H
@@ -29,8 +29,7 @@
29 29
30/** prototypes **/ 30/** prototypes **/
31int np_runcmd(const char *, output *, output *, int); 31int np_runcmd(const char *, output *, output *, int);
32void runcmd_timeout_alarm_handler(int) 32void runcmd_timeout_alarm_handler(int) __attribute__((__noreturn__));
33 __attribute__((__noreturn__));
34 33
35/* only multi-threaded plugins need to bother with this */ 34/* only multi-threaded plugins need to bother with this */
36void np_runcmd_init(void); 35void np_runcmd_init(void);
@@ -38,6 +37,6 @@ void np_runcmd_init(void);
38 37
39/* possible flags for np_runcmd()'s fourth argument */ 38/* possible flags for np_runcmd()'s fourth argument */
40#define RUNCMD_NO_ARRAYS 0x01 /* don't populate arrays at all */ 39#define RUNCMD_NO_ARRAYS 0x01 /* don't populate arrays at all */
41#define RUNCMD_NO_ASSOC 0x02 /* output.line won't point to buf */ 40#define RUNCMD_NO_ASSOC 0x02 /* output.line won't point to buf */
42 41
43#endif /* NAGIOSPLUG_RUNCMD_H */ 42#endif /* NAGIOSPLUG_RUNCMD_H */
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index 6bc0ba81..bcfb08d6 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -1,42 +1,43 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins SSL utilities 3 * Monitoring Plugins SSL utilities
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2005-2010 Monitoring Plugins Development Team 6 * Copyright (c) 2005-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains common functions for plugins that require SSL. 10 * This file contains common functions for plugins that require SSL.
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 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 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 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 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/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29#include "output.h"
29#define MAX_CN_LENGTH 256 30#define MAX_CN_LENGTH 256
30#include "common.h" 31#include "common.h"
31#include "netutils.h" 32#include "netutils.h"
33#include "../lib/monitoringplug.h"
34#include "states.h"
32 35
33#ifdef HAVE_SSL 36#ifdef HAVE_SSL
34static SSL_CTX *ctx=NULL; 37static SSL_CTX *ctx = NULL;
35static SSL *s=NULL; 38static SSL *s = NULL;
36 39
37int np_net_ssl_init(int sd) { 40int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); }
38 return np_net_ssl_init_with_hostname(sd, NULL);
39}
40 41
41int np_net_ssl_init_with_hostname(int sd, char *host_name) { 42int np_net_ssl_init_with_hostname(int sd, char *host_name) {
42 return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); 43 return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0);
@@ -46,7 +47,8 @@ int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int versi
46 return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL); 47 return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL);
47} 48}
48 49
49int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey) { 50int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert,
51 char *privkey) {
50 long options = 0; 52 long options = 0;
51 53
52 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) { 54 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) {
@@ -59,272 +61,530 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
59 printf("%s\n", _("UNKNOWN - SSL protocol version 2 is not supported by your SSL library.")); 61 printf("%s\n", _("UNKNOWN - SSL protocol version 2 is not supported by your SSL library."));
60 return STATE_UNKNOWN; 62 return STATE_UNKNOWN;
61 case MP_SSLv3: /* SSLv3 protocol */ 63 case MP_SSLv3: /* SSLv3 protocol */
62#if defined(OPENSSL_NO_SSL3) 64# if defined(OPENSSL_NO_SSL3)
63 printf("%s\n", _("UNKNOWN - SSL protocol version 3 is not supported by your SSL library.")); 65 printf("%s\n", _("UNKNOWN - SSL protocol version 3 is not supported by your SSL library."));
64 return STATE_UNKNOWN; 66 return STATE_UNKNOWN;
65#else 67# else
66 SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); 68 SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
67 SSL_CTX_set_max_proto_version(ctx, SSL3_VERSION); 69 SSL_CTX_set_max_proto_version(ctx, SSL3_VERSION);
68 break; 70 break;
69#endif 71# endif
70 case MP_TLSv1: /* TLSv1 protocol */ 72 case MP_TLSv1: /* TLSv1 protocol */
71#if defined(OPENSSL_NO_TLS1) 73# if defined(OPENSSL_NO_TLS1)
72 printf("%s\n", _("UNKNOWN - TLS protocol version 1 is not supported by your SSL library.")); 74 printf("%s\n", _("UNKNOWN - TLS protocol version 1 is not supported by your SSL library."));
73 return STATE_UNKNOWN; 75 return STATE_UNKNOWN;
74#else 76# else
75 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION); 77 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
76 SSL_CTX_set_max_proto_version(ctx, TLS1_VERSION); 78 SSL_CTX_set_max_proto_version(ctx, TLS1_VERSION);
77 break; 79 break;
78#endif 80# endif
79 case MP_TLSv1_1: /* TLSv1.1 protocol */ 81 case MP_TLSv1_1: /* TLSv1.1 protocol */
80#if !defined(SSL_OP_NO_TLSv1_1) 82# if !defined(SSL_OP_NO_TLSv1_1)
81 printf("%s\n", _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library.")); 83 printf("%s\n",
84 _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library."));
82 return STATE_UNKNOWN; 85 return STATE_UNKNOWN;
83#else 86# else
84 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); 87 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
85 SSL_CTX_set_max_proto_version(ctx, TLS1_1_VERSION); 88 SSL_CTX_set_max_proto_version(ctx, TLS1_1_VERSION);
86 break; 89 break;
87#endif 90# endif
88 case MP_TLSv1_2: /* TLSv1.2 protocol */ 91 case MP_TLSv1_2: /* TLSv1.2 protocol */
89#if !defined(SSL_OP_NO_TLSv1_2) 92# if !defined(SSL_OP_NO_TLSv1_2)
90 printf("%s\n", _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library.")); 93 printf("%s\n",
94 _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library."));
91 return STATE_UNKNOWN; 95 return STATE_UNKNOWN;
92#else 96# else
93 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); 97 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
94 SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION); 98 SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
95 break; 99 break;
96#endif 100# endif
97 case MP_TLSv1_2_OR_NEWER: 101 case MP_TLSv1_2_OR_NEWER:
98#if !defined(SSL_OP_NO_TLSv1_1) 102# if !defined(SSL_OP_NO_TLSv1_1)
99 printf("%s\n", _("UNKNOWN - Disabling TLSv1.1 is not supported by your SSL library.")); 103 printf("%s\n", _("UNKNOWN - Disabling TLSv1.1 is not supported by your SSL library."));
100 return STATE_UNKNOWN; 104 return STATE_UNKNOWN;
101#else 105# else
102 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); 106 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
103 break; 107 break;
104#endif 108# endif
105 case MP_TLSv1_1_OR_NEWER: 109 case MP_TLSv1_1_OR_NEWER:
106#if !defined(SSL_OP_NO_TLSv1) 110# if !defined(SSL_OP_NO_TLSv1)
107 printf("%s\n", _("UNKNOWN - Disabling TLSv1 is not supported by your SSL library.")); 111 printf("%s\n", _("UNKNOWN - Disabling TLSv1 is not supported by your SSL library."));
108 return STATE_UNKNOWN; 112 return STATE_UNKNOWN;
109#else 113# else
110 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); 114 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
111 break; 115 break;
112#endif 116# endif
113 case MP_TLSv1_OR_NEWER: 117 case MP_TLSv1_OR_NEWER:
114#if defined(SSL_OP_NO_SSLv3) 118# if defined(SSL_OP_NO_SSLv3)
115 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION); 119 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
116 break; 120 break;
117#endif 121# endif
118 case MP_SSLv3_OR_NEWER: 122 case MP_SSLv3_OR_NEWER:
119#if defined(SSL_OP_NO_SSLv2) 123# if defined(SSL_OP_NO_SSLv2)
120 SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); 124 SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
121 break; 125 break;
122#endif 126# endif
123 } 127 }
124 128
125 if (cert && privkey) { 129 if (cert && privkey) {
126#ifdef USE_OPENSSL 130# ifdef MOPL_USE_OPENSSL
127 if (!SSL_CTX_use_certificate_chain_file(ctx, cert)) { 131 if (!SSL_CTX_use_certificate_chain_file(ctx, cert)) {
128#elif USE_GNUTLS 132# elif USE_GNUTLS
129 if (!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM)) { 133 if (!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM)) {
130#else 134# else
131#error Unported for unknown SSL library 135# error Unported for unknown SSL library
132#endif 136# endif
133 printf ("%s\n", _("CRITICAL - Unable to open certificate chain file!\n")); 137 printf("%s\n", _("CRITICAL - Unable to open certificate chain file!\n"));
134 return STATE_CRITICAL; 138 return STATE_CRITICAL;
135 } 139 }
136 SSL_CTX_use_PrivateKey_file(ctx, privkey, SSL_FILETYPE_PEM); 140 SSL_CTX_use_PrivateKey_file(ctx, privkey, SSL_FILETYPE_PEM);
137#ifdef USE_OPENSSL 141# ifdef MOPL_USE_OPENSSL
138 if (!SSL_CTX_check_private_key(ctx)) { 142 if (!SSL_CTX_check_private_key(ctx)) {
139 printf ("%s\n", _("CRITICAL - Private key does not seem to match certificate!\n")); 143 printf("%s\n", _("CRITICAL - Private key does not seem to match certificate!\n"));
140 return STATE_CRITICAL; 144 return STATE_CRITICAL;
141 } 145 }
142#endif 146# endif
143 } 147 }
144#ifdef SSL_OP_NO_TICKET 148# ifdef SSL_OP_NO_TICKET
145 options |= SSL_OP_NO_TICKET; 149 options |= SSL_OP_NO_TICKET;
146#endif 150# endif
147 SSL_CTX_set_options(ctx, options); 151 SSL_CTX_set_options(ctx, options);
148 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); 152 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
149 if ((s = SSL_new(ctx)) != NULL) { 153 if ((s = SSL_new(ctx)) != NULL) {
150#ifdef SSL_set_tlsext_host_name 154# ifdef SSL_set_tlsext_host_name
151 if (host_name != NULL) 155 if (host_name != NULL) {
152 SSL_set_tlsext_host_name(s, host_name); 156 SSL_set_tlsext_host_name(s, host_name);
153#endif 157 }
158# endif
154 SSL_set_fd(s, sd); 159 SSL_set_fd(s, sd);
155 if (SSL_connect(s) == 1) { 160 if (SSL_connect(s) == 1) {
156 return OK; 161 return OK;
157 } else { 162 } else {
158 printf("%s\n", _("CRITICAL - Cannot make SSL connection.")); 163 printf("%s\n", _("CRITICAL - Cannot make SSL connection."));
159# ifdef USE_OPENSSL /* XXX look into ERR_error_string */ 164# ifdef MOPL_USE_OPENSSL /* XXX look into ERR_error_string */
160 ERR_print_errors_fp(stdout); 165 ERR_print_errors_fp(stdout);
161# endif /* USE_OPENSSL */ 166# endif /* MOPL_USE_OPENSSL */
162 } 167 }
163 } else { 168 } else {
164 printf("%s\n", _("CRITICAL - Cannot initiate SSL handshake.")); 169 printf("%s\n", _("CRITICAL - Cannot initiate SSL handshake."));
165 } 170 }
166 return STATE_CRITICAL; 171 return STATE_CRITICAL;
167} 172}
168 173
169void np_net_ssl_cleanup() { 174void np_net_ssl_cleanup() {
170 if (s) { 175 if (s) {
171#ifdef SSL_set_tlsext_host_name 176# ifdef SSL_set_tlsext_host_name
172 SSL_set_tlsext_host_name(s, NULL); 177 SSL_set_tlsext_host_name(s, NULL);
173#endif 178# endif
174 SSL_shutdown(s); 179 SSL_shutdown(s);
175 SSL_free(s); 180 SSL_free(s);
176 if (ctx) { 181 if (ctx) {
177 SSL_CTX_free(ctx); 182 SSL_CTX_free(ctx);
178 ctx=NULL; 183 ctx = NULL;
179 } 184 }
180 s=NULL; 185 s = NULL;
181 } 186 }
182} 187}
183 188
184int np_net_ssl_write(const void *buf, int num) { 189int np_net_ssl_write(const void *buf, int num) { return SSL_write(s, buf, num); }
185 return SSL_write(s, buf, num);
186}
187
188int np_net_ssl_read(void *buf, int num) {
189 return SSL_read(s, buf, num);
190}
191 190
192int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit){ 191int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); }
193# ifdef USE_OPENSSL
194 X509_NAME *subj=NULL;
195 char timestamp[50] = "";
196 char cn[MAX_CN_LENGTH]= "";
197 char *tz;
198
199 int cnlen =-1;
200 int status=STATE_UNKNOWN;
201
202 ASN1_STRING *tm;
203 int offset;
204 struct tm stamp;
205 float time_left;
206 int days_left;
207 int time_remaining;
208 time_t tm_t;
209 192
193mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
194 int days_till_exp_crit) {
195# ifdef MOPL_USE_OPENSSL
210 if (!certificate) { 196 if (!certificate) {
211 printf("%s\n",_("CRITICAL - Cannot retrieve server certificate.")); 197 printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
212 return STATE_CRITICAL; 198 return STATE_CRITICAL;
213 } 199 }
214 200
215 /* Extract CN from certificate subject */ 201 /* Extract CN from certificate subject */
216 subj=X509_get_subject_name(certificate); 202 X509_NAME *subj = X509_get_subject_name(certificate);
217 203
218 if (!subj) { 204 if (!subj) {
219 printf("%s\n",_("CRITICAL - Cannot retrieve certificate subject.")); 205 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
220 return STATE_CRITICAL; 206 return STATE_CRITICAL;
221 } 207 }
222 cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); 208
223 if (cnlen == -1) 209 char cn[MAX_CN_LENGTH] = "";
210 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn));
211 if (cnlen == -1) {
224 strcpy(cn, _("Unknown CN")); 212 strcpy(cn, _("Unknown CN"));
213 }
225 214
226 /* Retrieve timestamp of certificate */ 215 /* Retrieve timestamp of certificate */
227 tm = X509_get_notAfter(certificate); 216 ASN1_STRING *tm = X509_get_notAfter(certificate);
228 217
218 int offset = 0;
219 struct tm stamp = {};
229 /* Generate tm structure to process timestamp */ 220 /* Generate tm structure to process timestamp */
230 if (tm->type == V_ASN1_UTCTIME) { 221 if (tm->type == V_ASN1_UTCTIME) {
231 if (tm->length < 10) { 222 if (tm->length < 10) {
232 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 223 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
233 return STATE_CRITICAL; 224 return STATE_CRITICAL;
234 } else {
235 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
236 if (stamp.tm_year < 50)
237 stamp.tm_year += 100;
238 offset = 0;
239 } 225 }
226 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
227 if (stamp.tm_year < 50) {
228 stamp.tm_year += 100;
229 }
230 offset = 0;
231
240 } else { 232 } else {
241 if (tm->length < 12) { 233 if (tm->length < 12) {
242 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 234 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
243 return STATE_CRITICAL; 235 return STATE_CRITICAL;
244 } else {
245 stamp.tm_year =
246 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
247 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
248 stamp.tm_year -= 1900;
249 offset = 2;
250 } 236 }
237 stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
238 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
239 stamp.tm_year -= 1900;
240 offset = 2;
251 } 241 }
252 stamp.tm_mon = 242 stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
253 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; 243 stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
254 stamp.tm_mday = 244 stamp.tm_hour = (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
255 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); 245 stamp.tm_min = (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
256 stamp.tm_hour = 246 stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0');
257 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
258 stamp.tm_min =
259 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
260 stamp.tm_sec =
261 (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0');
262 stamp.tm_isdst = -1; 247 stamp.tm_isdst = -1;
263 248
264 tm_t = timegm(&stamp); 249 time_t tm_t = timegm(&stamp);
265 time_left = difftime(tm_t, time(NULL)); 250 float time_left = difftime(tm_t, time(NULL));
266 days_left = time_left / 86400; 251 int days_left = time_left / 86400;
267 tz = getenv("TZ"); 252 char *tz = getenv("TZ");
268 setenv("TZ", "GMT", 1); 253 setenv("TZ", "GMT", 1);
269 tzset(); 254 tzset();
255
256 char timestamp[50] = "";
270 strftime(timestamp, 50, "%c %z", localtime(&tm_t)); 257 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
271 if (tz) 258 if (tz) {
272 setenv("TZ", tz, 1); 259 setenv("TZ", tz, 1);
273 else 260 } else {
274 unsetenv("TZ"); 261 unsetenv("TZ");
262 }
263
275 tzset(); 264 tzset();
276 265
266 int time_remaining;
267 mp_state_enum status = STATE_UNKNOWN;
277 if (days_left > 0 && days_left <= days_till_exp_warn) { 268 if (days_left > 0 && days_left <= days_till_exp_warn) {
278 printf (_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", cn, days_left, timestamp); 269 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
279 if (days_left > days_till_exp_crit) 270 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, days_left, timestamp);
271 if (days_left > days_till_exp_crit) {
280 status = STATE_WARNING; 272 status = STATE_WARNING;
281 else 273 } else {
282 status = STATE_CRITICAL; 274 status = STATE_CRITICAL;
275 }
283 } else if (days_left == 0 && time_left > 0) { 276 } else if (days_left == 0 && time_left > 0) {
284 if (time_left >= 3600) 277 if (time_left >= 3600) {
285 time_remaining = (int) time_left / 3600; 278 time_remaining = (int)time_left / 3600;
286 else 279 } else {
287 time_remaining = (int) time_left / 60; 280 time_remaining = (int)time_left / 60;
281 }
288 282
289 printf (_("%s - Certificate '%s' expires in %u %s (%s)\n"), 283 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
290 (days_left>days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining, 284 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining,
291 time_left >= 3600 ? "hours" : "minutes", timestamp); 285 time_left >= 3600 ? "hours" : "minutes", timestamp);
292 286
293 if ( days_left > days_till_exp_crit) 287 if (days_left > days_till_exp_crit) {
294 status = STATE_WARNING; 288 status = STATE_WARNING;
295 else 289 } else {
296 status = STATE_CRITICAL; 290 status = STATE_CRITICAL;
291 }
297 } else if (time_left < 0) { 292 } else if (time_left < 0) {
298 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp); 293 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp);
299 status=STATE_CRITICAL; 294 status = STATE_CRITICAL;
300 } else if (days_left == 0) { 295 } else if (days_left == 0) {
301 printf (_("%s - Certificate '%s' just expired (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", cn, timestamp); 296 printf(_("%s - Certificate '%s' just expired (%s).\n"),
302 if (days_left > days_till_exp_crit) 297 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, timestamp);
298 if (days_left > days_till_exp_crit) {
303 status = STATE_WARNING; 299 status = STATE_WARNING;
304 else 300 } else {
305 status = STATE_CRITICAL; 301 status = STATE_CRITICAL;
302 }
306 } else { 303 } else {
307 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp); 304 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp);
308 status = STATE_OK; 305 status = STATE_OK;
309 } 306 }
310 X509_free(certificate); 307 X509_free(certificate);
311 return status; 308 return status;
312# else /* ifndef USE_OPENSSL */ 309# else /* ifndef MOPL_USE_OPENSSL */
313 printf("%s\n", _("WARNING - Plugin does not support checking certificates.")); 310 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
314 return STATE_WARNING; 311 return STATE_WARNING;
315# endif /* USE_OPENSSL */ 312# endif /* MOPL_USE_OPENSSL */
316} 313}
317 314
318int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit){ 315retrieve_expiration_time_result np_net_ssl_get_cert_expiration(X509 *certificate) {
319# ifdef USE_OPENSSL 316# ifdef MOPL_USE_OPENSSL
317 retrieve_expiration_time_result result = {
318 .errors = ALL_OK,
319 .remaining_seconds = 0,
320 };
321
322 if (!certificate) {
323 // printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
324 result.errors = NO_SERVER_CERTIFICATE_PRESENT;
325 return result;
326 }
327
328 /* Extract CN from certificate subject */
329 X509_NAME *subj = X509_get_subject_name(certificate);
330
331 if (!subj) {
332 // printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
333 result.errors = UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT;
334 return result;
335 }
336
337 char cn[MAX_CN_LENGTH] = "";
338 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn));
339 if (cnlen == -1) {
340 strcpy(cn, _("Unknown CN"));
341 }
342
343 /* Retrieve timestamp of certificate */
344 ASN1_STRING *expiration_timestamp = X509_get_notAfter(certificate);
345
346 int offset = 0;
347 struct tm stamp = {};
348 /* Generate tm structure to process timestamp */
349 if (expiration_timestamp->type == V_ASN1_UTCTIME) {
350 if (expiration_timestamp->length < 10) {
351 result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
352 return result;
353 }
354
355 stamp.tm_year =
356 (expiration_timestamp->data[0] - '0') * 10 + (expiration_timestamp->data[1] - '0');
357 if (stamp.tm_year < 50) {
358 stamp.tm_year += 100;
359 }
360 offset = 0;
361 } else {
362 if (expiration_timestamp->length < 12) {
363 result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
364 return result;
365 }
366
367 stamp.tm_year = (expiration_timestamp->data[0] - '0') * 1000 +
368 (expiration_timestamp->data[1] - '0') * 100 +
369 (expiration_timestamp->data[2] - '0') * 10 +
370 (expiration_timestamp->data[3] - '0');
371 stamp.tm_year -= 1900;
372 offset = 2;
373 }
374 stamp.tm_mon = (expiration_timestamp->data[2 + offset] - '0') * 10 +
375 (expiration_timestamp->data[3 + offset] - '0') - 1;
376 stamp.tm_mday = (expiration_timestamp->data[4 + offset] - '0') * 10 +
377 (expiration_timestamp->data[5 + offset] - '0');
378 stamp.tm_hour = (expiration_timestamp->data[6 + offset] - '0') * 10 +
379 (expiration_timestamp->data[7 + offset] - '0');
380 stamp.tm_min = (expiration_timestamp->data[8 + offset] - '0') * 10 +
381 (expiration_timestamp->data[9 + offset] - '0');
382 stamp.tm_sec = (expiration_timestamp->data[10 + offset] - '0') * 10 +
383 (expiration_timestamp->data[11 + offset] - '0');
384 stamp.tm_isdst = -1;
385
386 time_t tm_t = timegm(&stamp);
387 double time_left = difftime(tm_t, time(NULL));
388 result.remaining_seconds = time_left;
389
390 char *timezone = getenv("TZ");
391 setenv("TZ", "GMT", 1);
392 tzset();
393
394 char timestamp[50] = "";
395 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
396 if (timezone) {
397 setenv("TZ", timezone, 1);
398 } else {
399 unsetenv("TZ");
400 }
401
402 tzset();
403
404 X509_free(certificate);
405
406 return result;
407# else /* ifndef MOPL_USE_OPENSSL */
408 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
409 return STATE_WARNING;
410# endif /* MOPL_USE_OPENSSL */
411}
412
413net_ssl_check_cert_result np_net_ssl_check_cert2(unsigned int days_till_exp_warn, unsigned int days_till_exp_crit) {
414# ifdef MOPL_USE_OPENSSL
320 X509 *certificate = NULL; 415 X509 *certificate = NULL;
321 certificate=SSL_get_peer_certificate(s); 416 certificate = SSL_get_peer_certificate(s);
322 return(np_net_ssl_check_certificate(certificate, days_till_exp_warn, days_till_exp_crit)); 417
323# else /* ifndef USE_OPENSSL */ 418 retrieve_expiration_time_result expiration_date = np_net_ssl_get_cert_expiration(certificate);
419
420 net_ssl_check_cert_result result = {
421 .result_state = STATE_UNKNOWN,
422 .remaining_seconds = expiration_date.remaining_seconds,
423 .errors = expiration_date.errors,
424 };
425
426 if (expiration_date.errors == ALL_OK) {
427 // got a valid expiration date
428 unsigned int remaining_days = result.remaining_seconds / 86400;
429
430 if (remaining_days < days_till_exp_crit) {
431 result.result_state = STATE_CRITICAL;
432 } else if (remaining_days < days_till_exp_warn) {
433 result.result_state = STATE_WARNING;
434 } else {
435 result.result_state = STATE_OK;
436 }
437 }
438
439 return result;
440
441# else /* ifndef MOPL_USE_OPENSSL */
442 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
443 return STATE_WARNING;
444# endif /* MOPL_USE_OPENSSL */
445}
446
447mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
448# ifdef MOPL_USE_OPENSSL
449 X509 *certificate = NULL;
450 certificate = SSL_get_peer_certificate(s);
451 return (np_net_ssl_check_certificate(certificate, days_till_exp_warn, days_till_exp_crit));
452# else /* ifndef MOPL_USE_OPENSSL */
324 printf("%s\n", _("WARNING - Plugin does not support checking certificates.")); 453 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
325 return STATE_WARNING; 454 return STATE_WARNING;
326# endif /* USE_OPENSSL */ 455# endif /* MOPL_USE_OPENSSL */
327} 456}
328 457
458mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
459 int days_till_exp_crit) {
460 mp_subcheck sc_cert = mp_subcheck_init();
461# ifdef MOPL_USE_OPENSSL
462 if (!certificate) {
463 xasprintf(&sc_cert.output, _("No server certificate present to inspect"));
464 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
465 return sc_cert;
466 }
467
468 /* Extract CN from certificate subject */
469 X509_NAME *subj = X509_get_subject_name(certificate);
470
471 if (!subj) {
472 xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject"));
473 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
474 return sc_cert;
475 }
476
477 char commonName[MAX_CN_LENGTH] = "";
478 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName));
479 if (cnlen == -1) {
480 strcpy(commonName, _("Unknown CN"));
481 }
482
483 /* Retrieve timestamp of certificate */
484 ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate);
485
486 int offset = 0;
487 struct tm stamp = {};
488 /* Generate tm structure to process timestamp */
489 if (expiry_timestamp->type == V_ASN1_UTCTIME) {
490 if (expiry_timestamp->length < 10) {
491 xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
492 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
493 return sc_cert;
494 }
495
496 stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0');
497 if (stamp.tm_year < 50) {
498 stamp.tm_year += 100;
499 }
500
501 offset = 0;
502 } else {
503 if (expiry_timestamp->length < 12) {
504 xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
505 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
506 return sc_cert;
507 }
508 stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 +
509 (expiry_timestamp->data[1] - '0') * 100 +
510 (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0');
511 stamp.tm_year -= 1900;
512 offset = 2;
513 }
514
515 stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 +
516 (expiry_timestamp->data[3 + offset] - '0') - 1;
517 stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 +
518 (expiry_timestamp->data[5 + offset] - '0');
519 stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 +
520 (expiry_timestamp->data[7 + offset] - '0');
521 stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 +
522 (expiry_timestamp->data[9 + offset] - '0');
523 stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 +
524 (expiry_timestamp->data[11 + offset] - '0');
525 stamp.tm_isdst = -1;
526
527 time_t tm_t = timegm(&stamp);
528 double time_left = difftime(tm_t, time(NULL));
529 int days_left = (int)(time_left / 86400);
530 char *timeZone = getenv("TZ");
531 setenv("TZ", "GMT", 1);
532 tzset();
533
534 char timestamp[50] = "";
535 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
536 if (timeZone) {
537 setenv("TZ", timeZone, 1);
538 } else {
539 unsetenv("TZ");
540 }
541
542 tzset();
543
544 int time_remaining;
545 if (days_left > 0 && days_left <= days_till_exp_warn) {
546 xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName,
547 days_left, timestamp);
548 if (days_left > days_till_exp_crit) {
549 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
550 } else {
551 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
552 }
553 } else if (days_left == 0 && time_left > 0) {
554 if (time_left >= 3600) {
555 time_remaining = (int)time_left / 3600;
556 } else {
557 time_remaining = (int)time_left / 60;
558 }
559
560 xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName,
561 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp);
329 562
563 if (days_left > days_till_exp_crit) {
564 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
565 } else {
566 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
567 }
568 } else if (time_left < 0) {
569 xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp);
570 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
571 } else if (days_left == 0) {
572 xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName, timestamp);
573 if (days_left > days_till_exp_crit) {
574 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
575 } else {
576 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
577 }
578 } else {
579 xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName, timestamp);
580 sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK);
581 }
582 X509_free(certificate);
583 return sc_cert;
584# else /* ifndef MOPL_USE_OPENSSL */
585 xasprintf(&sc_cert.output, _("Plugin does not support checking certificates"));
586 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
587 return sc_cert;
588# endif /* MOPL_USE_OPENSSL */
589}
330#endif /* HAVE_SSL */ 590#endif /* HAVE_SSL */
diff --git a/plugins/t/check_apt.t b/plugins/t/check_apt.t
index 430eb53e..736bc2f2 100644
--- a/plugins/t/check_apt.t
+++ b/plugins/t/check_apt.t
@@ -5,6 +5,7 @@
5# 5#
6 6
7use strict; 7use strict;
8use warnings;
8use Test::More; 9use Test::More;
9use NPTest; 10use NPTest;
10 11
@@ -12,18 +13,18 @@ sub make_result_regexp {
12 my ($warning, $critical) = @_; 13 my ($warning, $critical) = @_;
13 my $status; 14 my $status;
14 if ($warning == 0 && $critical == 0) { 15 if ($warning == 0 && $critical == 0) {
15 $status = "OK"; 16 $status = "OK";
16 } elsif ($critical == 0) { 17 } elsif ($critical == 0) {
17 $status = "WARNING"; 18 $status = "WARNING";
18 } else { 19 } else {
19 $status = "CRITICAL"; 20 $status = "CRITICAL";
20 } 21 }
21 return sprintf('/^APT %s: %d packages available for upgrade \(%d critical updates\)\. |available_upgrades=%d;;;0 critical_updates=%d;;;0$/', 22 return sprintf('/.*[%s].*Updates available: %d.*Security updates available: %d.*\'available_upgrades\'=%d;;; \'critical_updates\'=%d;;; /s',
22 $status, $warning, $critical, $warning, $critical); 23 $status, $warning, $critical, $warning, $critical);
23} 24}
24 25
25if (-x "./check_apt") { 26if (-x "./check_apt") {
26 plan tests => 36; 27 plan tests => 35;
27} else { 28} else {
28 plan skip_all => "No check_apt compiled"; 29 plan skip_all => "No check_apt compiled";
29} 30}
@@ -42,7 +43,8 @@ like( $result->output, make_result_regexp(13, 0), "Output correct" );
42 43
43$result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") ); 44$result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") );
44is( $result->return_code, 0, "Debian apt output, no critical" ); 45is( $result->return_code, 0, "Debian apt output, no critical" );
45like( $result->output, make_result_regexp(13, 0), "Output correct" ); 46# this test does not work, since -o was given
47# like( $result->output, make_result_regexp(13, 0), "Output correct" );
46 48
47$result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") ); 49$result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") );
48is( $result->return_code, 2, "Debian apt output, some critical" ); 50is( $result->return_code, 2, "Debian apt output, some critical" );
diff --git a/plugins/t/check_by_ssh.t b/plugins/t/check_by_ssh.t
index b6479f1f..0ee310cd 100644
--- a/plugins/t/check_by_ssh.t
+++ b/plugins/t/check_by_ssh.t
@@ -16,7 +16,7 @@ my $ssh_conf = getTestParameter( "NP_SSH_CONFIGFILE", "A config file with ssh
16 16
17plan skip_all => "SSH_HOST and SSH_IDENTITY must be defined" unless ($ssh_service && $ssh_key); 17plan skip_all => "SSH_HOST and SSH_IDENTITY must be defined" unless ($ssh_service && $ssh_key);
18 18
19plan tests => 42; 19plan tests => 33;
20 20
21# Some random check strings/response 21# Some random check strings/response
22my @response = ('OK: Everything is fine', 22my @response = ('OK: Everything is fine',
@@ -47,70 +47,70 @@ for (my $i=0; $i<4; $i++) {
47 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[$i]; exit $i'" 47 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[$i]; exit $i'"
48 ); 48 );
49 cmp_ok($result->return_code, '==', $i, "Exit with return code $i"); 49 cmp_ok($result->return_code, '==', $i, "Exit with return code $i");
50 is($result->output, $response[$i], "Status text is correct for check $i"); 50 like($result->output, "/$response[$i]/", "Status text is correct for check $i");
51} 51}
52 52
53$result = NPTest->testCmd( 53$result = NPTest->testCmd(
54 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 0'" 54 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 0'"
55 ); 55 );
56cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); 56cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
57is($result->output, 'OK - check_by_ssh: Remote command \'exit 0\' returned status 0', "Status text if command returned none (OK)"); 57like($result->output, '/command \'exit 0\' returned status 0/', "Status text if command returned none (OK)");
58 58
59$result = NPTest->testCmd( 59$result = NPTest->testCmd(
60 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 1'" 60 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 1'"
61 ); 61 );
62cmp_ok($result->return_code, '==', 1, "Exit with return code 1 (WARNING)"); 62cmp_ok($result->return_code, '==', 1, "Exit with return code 1 (WARNING)");
63is($result->output, 'WARNING - check_by_ssh: Remote command \'exit 1\' returned status 1', "Status text if command returned none (WARNING)"); 63like($result->output, '/command \'exit 1\' returned status 1/', "Status text if command returned none (WARNING)");
64 64
65$result = NPTest->testCmd( 65$result = NPTest->testCmd(
66 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 2'" 66 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 2'"
67 ); 67 );
68cmp_ok($result->return_code, '==', 2, "Exit with return code 2 (CRITICAL)"); 68cmp_ok($result->return_code, '==', 2, "Exit with return code 2 (CRITICAL)");
69is($result->output, 'CRITICAL - check_by_ssh: Remote command \'exit 2\' returned status 2', "Status text if command returned none (CRITICAL)"); 69like($result->output, '/command \'exit 2\' returned status 2/', "Status text if command returned none (CRITICAL)");
70 70
71$result = NPTest->testCmd( 71$result = NPTest->testCmd(
72 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 3'" 72 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 3'"
73 ); 73 );
74cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)"); 74cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)");
75is($result->output, 'UNKNOWN - check_by_ssh: Remote command \'exit 3\' returned status 3', "Status text if command returned none (UNKNOWN)"); 75like($result->output, '/command \'exit 3\' returned status 3/', "Status text if command returned none (UNKNOWN)");
76 76
77$result = NPTest->testCmd( 77$result = NPTest->testCmd(
78 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 7'" 78 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 7'"
79 ); 79 );
80cmp_ok($result->return_code, '==', 7, "Exit with return code 7 (out of bounds)"); 80cmp_ok($result->return_code, '==', 3, "Exit with return code 3");
81is($result->output, 'UNKNOWN - check_by_ssh: Remote command \'exit 7\' returned status 7', "Status text if command returned none (out of bounds)"); 81like($result->output, '/command \'exit 7\' returned status 7/', "Status text if command returned none (out of bounds)");
82 82
83$result = NPTest->testCmd( 83$result = NPTest->testCmd(
84 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[4]; exit 8'" 84 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[4]; exit 8'"
85 ); 85 );
86cmp_ok($result->return_code, '==', 8, "Exit with return code 8 (out of bounds)"); 86cmp_ok($result->return_code, '==', 3, "Exit with return code 3");
87is($result->output, $response[4], "Return proper status text even with unknown status codes"); 87like($result->output, "/$response[4]/", "Return proper status text even with unknown status codes");
88 88
89$result = NPTest->testCmd( 89$result = NPTest->testCmd(
90 "./check_by_ssh -i $ssh_key -H $ssh_service -F $ssh_conf -C 'exit 0'" 90 "./check_by_ssh -i $ssh_key -H $ssh_service -F $ssh_conf -C 'exit 0'"
91 ); 91 );
92cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); 92cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
93is($result->output, 'OK - check_by_ssh: Remote command \'exit 0\' returned status 0', "Status text if command returned none (OK)"); 93like($result->output, '/command \'exit 0\' returned status 0/', "Status text if command returned none (OK)");
94 94
95# Multiple active checks 95# Multiple active checks
96$result = NPTest->testCmd( 96$result = NPTest->testCmd(
97 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[1]; sh -c exit\\ 1' -C '$check[0]; sh -c exit\\ 0' -C '$check[3]; sh -c exit\\ 3' -C '$check[2]; sh -c exit\\ 2'" 97 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[1]; sh -c exit\\ 1' -C '$check[0]; sh -c exit\\ 0' -C '$check[3]; sh -c exit\\ 3' -C '$check[2]; sh -c exit\\ 2'"
98 ); 98 );
99cmp_ok($result->return_code, '==', 0, "Multiple checks always return OK"); 99cmp_ok($result->return_code, '==', 0, "Multiple checks always return OK");
100my @lines = split(/\n/, $result->output); 100# my @lines = split(/\n/, $result->output);
101cmp_ok(scalar(@lines), '==', 8, "Correct number of output lines for multiple checks"); 101# cmp_ok(scalar(@lines), '==', 8, "Correct number of output lines for multiple checks");
102my %linemap = ( 102# my %linemap = (
103 '0' => '1', 103# '0' => '1',
104 '2' => '0', 104# '2' => '0',
105 '4' => '3', 105# '4' => '3',
106 '6' => '2', 106# '6' => '2',
107); 107# );
108foreach my $line (0, 2, 4, 6) { 108# foreach my $line (0, 2, 4, 6) {
109 my $code = $linemap{$line}; 109 # my $code = $linemap{$line};
110 my $statline = $line+1; 110 # my $statline = $line+1;
111 is($lines[$line], "$response[$code]", "multiple checks status text is correct for line $line"); 111 # is($lines[$line], "$response[$code]", "multiple checks status text is correct for line $line");
112 is($lines[$statline], "STATUS CODE: $code", "multiple check status code is correct for line $line"); 112 # is($lines[$statline], "STATUS CODE: $code", "multiple check status code is correct for line $line");
113} 113# }
114 114
115# Passive checks 115# Passive checks
116unlink("/tmp/check_by_ssh.$$"); 116unlink("/tmp/check_by_ssh.$$");
diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t
index 7a930a4e..0f4d0de7 100644
--- a/plugins/t/check_curl.t
+++ b/plugins/t/check_curl.t
@@ -13,19 +13,25 @@ use vars qw($tests $has_ipv6);
13BEGIN { 13BEGIN {
14 use NPTest; 14 use NPTest;
15 $has_ipv6 = NPTest::has_ipv6(); 15 $has_ipv6 = NPTest::has_ipv6();
16 $tests = $has_ipv6 ? 59 : 57; 16 $tests = $has_ipv6 ? 57 : 92;
17 plan tests => $tests; 17 plan tests => $tests;
18} 18}
19 19
20 20
21my $successOutput = '/OK.*HTTP.*second/'; 21my $successOutput = '/.*HTTP.*second/';
22 22
23my $res; 23my $res;
24my $plugin = 'check_http'; 24my $plugin = 'check_http';
25$plugin = 'check_curl' if $0 =~ m/check_curl/mx; 25$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
26 26
27my $host_tcp_http = getTestParameter("NP_HOST_TCP_HTTP", "A host providing the HTTP Service (a web server)", "localhost"); 27my $host_tcp_http = getTestParameter("NP_HOST_TCP_HTTP", "A host providing the HTTP Service (a web server)", "localhost");
28my $host_tcp_http_subdomain = getTestParameter("NP_HOST_TCP_HTTP_SUBDOMAIN", "A host that is served under a subdomain name", "subdomain1.localhost.com");
29my $host_tcp_http_ipv4 = getTestParameter("NP_HOST_TCP_HTTP_IPV4", "An IPv6 address providing a HTTP Service (a web server)", "127.0.0.1");
30my $host_tcp_http_ipv4_cidr_1 = getTestParameter("NP_HOST_TCP_HTTP_IPV4_CIDR_1", "A CIDR that the provided IPv4 address is in.");
31my $host_tcp_http_ipv4_cidr_2 = getTestParameter("NP_HOST_TCP_HTTP_IPV4_CIDR_2", "A CIDR that the provided IPv4 address is in.");
28my $host_tcp_http_ipv6 = getTestParameter("NP_HOST_TCP_HTTP_IPV6", "An IPv6 address providing a HTTP Service (a web server)", "::1"); 32my $host_tcp_http_ipv6 = getTestParameter("NP_HOST_TCP_HTTP_IPV6", "An IPv6 address providing a HTTP Service (a web server)", "::1");
33my $host_tcp_http_ipv6_cidr_1 = getTestParameter("NP_HOST_TCP_HTTP_IPV6_CIDR_1", "A CIDR that the provided IPv6 address is in.");
34my $host_tcp_http_ipv6_cidr_2 = getTestParameter("NP_HOST_TCP_HTTP_IPV6_CIDR_2", "A CIDR that the provided IPv6 address is in.");
29my $host_tls_http = getTestParameter("NP_HOST_TLS_HTTP", "A host providing the HTTPS Service (a tls web server)", "localhost"); 35my $host_tls_http = getTestParameter("NP_HOST_TLS_HTTP", "A host providing the HTTPS Service (a tls web server)", "localhost");
30my $host_tls_cert = getTestParameter("NP_HOST_TLS_CERT", "the common name of the certificate.", "localhost"); 36my $host_tls_cert = getTestParameter("NP_HOST_TLS_CERT", "the common name of the certificate.", "localhost");
31my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1"); 37my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1");
@@ -63,7 +69,7 @@ $res = NPTest->testCmd(
63 ); 69 );
64cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); 70cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
65# was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!) 71# was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!)
66like( $res->output, "/HTTP CRITICAL - Invalid HTTP response received from host on port 80: cURL returned 28 - Connection timed out after/", "Output OK"); 72like( $res->output, "/cURL returned 28 - Connection timed out after/", "Output OK");
67 73
68$res = NPTest->testCmd( 74$res = NPTest->testCmd(
69 "./$plugin $hostname_invalid -wt 1 -ct 2" 75 "./$plugin $hostname_invalid -wt 1 -ct 2"
@@ -124,14 +130,14 @@ SKIP: {
124 130
125 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" ); 131 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
126 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); 132 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
127 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'"); 133 like ( $res->output, "/matched not/", "Error message says 'matched not'");
128 134
129 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" ); 135 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
130 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); 136 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
131 137
132 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); 138 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
133 cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); 139 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
134 like ( $res->output, "/pattern found/", "Error message says 'pattern found'"); 140 like ( $res->output, "/matched/", "Error message says 'matched'");
135 141
136 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); 142 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
137 cmp_ok( $res->return_code, "==", 0, "And also when not found"); 143 cmp_ok( $res->return_code, "==", 0, "And also when not found");
@@ -151,63 +157,181 @@ SKIP: {
151 157
152 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" ); 158 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
153 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); 159 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http");
154 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); 160 like ( $res->output, qr/Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
155 161
156 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); 162 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
157 is( $res->return_code, 0, "Old syntax for cert checking okay" ); 163 is( $res->return_code, 0, "Old syntax for cert checking okay" );
158 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 164 # deactivated since different timings will change the output
165 # TODO compare without perfdata
166 # is( $res->output, $saved_cert_output, "Same output as new syntax" );
159 167
160 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" ); 168 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
161 is( $res->return_code, 0, "Updated syntax for cert checking okay" ); 169 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
162 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 170 # deactivated since different timings will change the output
171 # TODO compare without perfdata
172 # is( $res->output, $saved_cert_output, "Same output as new syntax" );
163 173
164 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" ); 174 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
165 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); 175 # deactivated since different timings will change the output
176 # TODO compare without perfdata
177 # cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
166 178
167 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); 179 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
168 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); 180 # deactivated since different timings will change the output
181 # TODO compare without perfdata
182 # cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
169 183
170 # run some certificate checks with faketime 184 # run some certificate checks with faketime
171 SKIP: { 185 SKIP: {
172 skip "No faketime binary found", 12 if !$faketime; 186 skip "No faketime binary found", 12 if !$faketime;
173 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http"); 187 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http");
174 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output"); 188 like($res->output, qr/Certificate '$host_tls_cert' will expire on/, "Catch cert output");
175 is( $res->return_code, 0, "Catch cert output exit code" ); 189 is( $res->return_code, 0, "Catch cert output exit code" );
190
176 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); 191 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
177 if(!defined $year) { 192 if(!defined $year) {
178 die("parsing date failed from: ".$res->output); 193 die("parsing date failed from: ".$res->output);
179 } 194 }
195
180 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11}; 196 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11};
181 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900); 197 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
182 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); 198 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
199
183 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http"); 200 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http");
184 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date"); 201 like($res->output, qr/Certificate '$host_tls_cert' just expired/, "Output on expire date");
185 is( $res->return_code, 2, "Output on expire date" ); 202 is( $res->return_code, 2, "Output on expire date" );
186 203
187 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http"); 204 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http");
188 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); 205 like($res->output, qr/Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output");
189 is( $res->return_code, 2, "cert expires in 1 second exit code" ); 206 is( $res->return_code, 2, "cert expires in 1 second exit code" );
190 207
191 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http"); 208 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http");
192 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); 209 like($res->output, qr/Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output");
193 is( $res->return_code, 2, "cert expires in 2 minutes exit code" ); 210 is( $res->return_code, 2, "cert expires in 2 minutes exit code" );
194 211
195 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http"); 212 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http");
196 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); 213 like($res->output, qr/Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output");
197 is( $res->return_code, 2, "cert expires in 2 hours exit code" ); 214 is( $res->return_code, 2, "cert expires in 2 hours exit code" );
198 215
199 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http"); 216 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http");
200 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output"); 217 like($res->output, qr/Certificate '$host_tls_cert' expired on/, "Certificate expired output");
201 is( $res->return_code, 2, "Certificate expired exit code" ); 218 is( $res->return_code, 2, "Certificate expired exit code" );
202 }; 219 };
203 220
204 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" ); 221 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
205 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 222 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' );
206 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); 223 like ( $res->output, '/\'time_tls\'=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
207 224
208 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org -u /download.html -f follow" ); 225 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org -u /download.html -f follow" );
209 is( $res->return_code, 0, "Redirection based on location is okay"); 226 is( $res->return_code, 0, "Redirection based on location is okay");
210 227
211 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" ); 228 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" );
212 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 229 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' );
230}
231SKIP: {
232 skip "No internet access", 2 if $internet_access eq "no";
233
234 # Proxy tests
235 # These are the proxy tests that require a working proxy server
236 # The debian container in the github workflow runs a squid proxy server at port 3128
237 # Test that dont require one, like argument/environment variable parsing are in plugins/tests/check_curl.t
238
239 # Test if proxy works
240 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
241 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used, there are no preventative measures ");
242 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
243
244 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
245 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used, there are no preventative measures ");
246 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_ipv4 works" );
247
248 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
249 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used, there are no preventative measures ");
250 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_ipv6 works" );
251
252 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
253 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used, there are no preventative measures ");
254 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http2 works" );
255
256 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
257 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used, there are no preventative measures ");
258 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_subdomain works" );
259
260 $res = NPTest->testCmd( "./$plugin -H $host_tls_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
261 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used, there are no preventative measures ");
262 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tls_http works" );
263
264 # Noproxy '*' should prevent using proxy in any setting, even if its specified
265 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
266 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since noproxy has \"\*\" ");
267 is( $res->return_code, 0, "Should reach $host_tcp_http_subdomain with or without proxy." );
268
269 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
270 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since noproxy has \"\*\" ");
271 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
272
273 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
274 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since noproxy has \"\*\" ");
275 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
276
277 # Noproxy domain should prevent using proxy for subdomains of that domain
278 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http -v" );
279 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since subdomain: $host_tcp_http_subdomain is under a noproxy domain: $host_tcp_http");
280 is( $res->return_code, 0, "Should reach $host_tcp_http_subdomain with or without proxy." );
281
282 # Noproxy should prevent using IP matches if an IP is found directly
283 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4 -v" );
284 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is added into noproxy: $host_tcp_http_ipv4");
285 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
286
287 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6 -v" );
288 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is added into noproxy: $host_tcp_http_ipv6");
289 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
290
291 # Noproxy should prevent using IP matches if a CIDR region that contains that Ip is used directly.
292 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4_cidr_1 -v" );
293 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is inside CIDR range: $host_tcp_http_ipv4_cidr_1");
294 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
295
296 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4_cidr_2 -v" );
297 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is inside CIDR range: $host_tcp_http_ipv4_cidr_2");
298 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
299
300 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6_cidr_1 -v " );
301 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is inside CIDR range: $host_tcp_http_ipv6_cidr_1");
302 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
303
304 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6_cidr_2 -v" );
305 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is inside CIDR range: $host_tcp_http_ipv6_cidr_2");
306 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
307
308 # Noproxy should discern over different types of proxy schemes
309 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
310 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used for resolving hostname, and is using scheme http ");
311 is( $res->return_code, 0, "Using proxy http:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
312
313 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy https://$host_tcp_proxy:$port_tcp_proxy -v" );
314 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used for resolving hostname, and is using scheme https");
315 # Squid is not configured for https
316 # is( $res->return_code, 0, "Using proxy https:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
317
318 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks4://$host_tcp_proxy:$port_tcp_proxy -v" );
319 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used for resolving hostname, and is using scheme socks4");
320 # Squid is not configured for socks4
321 # is( $res->return_code, 0, "Using proxy socks4:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
322
323 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks4a://$host_tcp_proxy:$port_tcp_proxy -v" );
324 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used for resolving hostname, and is using scheme socks4a");
325 # Squid is not configured for socks4a
326 # is( $res->return_code, 0, "Using proxy socks4a:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
327
328 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks5://$host_tcp_proxy:$port_tcp_proxy -v" );
329 like($res->output, qr/^\* have local name resolution: true/m, "proxy is not used for resolving hostname, and is using scheme socks5");
330 # Squid is not configured for socks5
331 # is( $res->return_code, 0, "Using proxy socks5:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
332
333 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks5h://$host_tcp_proxy:$port_tcp_proxy -v" );
334 like($res->output, qr/^\* have local name resolution: false/m, "proxy is used for resolving hostname, and is using scheme socks5h");
335 # Squid is not configured for socks5h
336 # is( $res->return_code, 0, "Using proxy socks5h:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
213} 337}
diff --git a/plugins/t/check_dbi.t b/plugins/t/check_dbi.t
index c24b5a8c..75444de4 100644
--- a/plugins/t/check_dbi.t
+++ b/plugins/t/check_dbi.t
@@ -21,12 +21,12 @@ plan tests => $tests;
21my $missing_driver_output = "failed to open DBI driver 'sqlite3'"; 21my $missing_driver_output = "failed to open DBI driver 'sqlite3'";
22 22
23my $bad_driver_output = "/failed to open DBI driver 'nodriver'/"; 23my $bad_driver_output = "/failed to open DBI driver 'nodriver'/";
24my $conn_time_output = "/OK - connection time: [0-9\.]+s \|/"; 24my $conn_time_output = "/connection time: [0-9\.]+s \|/";
25my $missing_query_output = "/Must specify a query to execute/"; 25my $missing_query_output = "/Must specify a query to execute/";
26my $no_rows_output = "/WARNING - no rows returned/"; 26my $no_rows_output = "/no rows returned/";
27my $not_numeric_output = "/CRITICAL - result value is not a numeric:/"; 27my $not_numeric_output = "/result value is not a numeric:/";
28my $query_time_output = "/OK - connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/"; 28my $query_time_output = "/connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/";
29my $syntax_error_output = "/CRITICAL - failed to execute query 'GET ALL FROM test': 1: near \"GET\": syntax error/"; 29my $syntax_error_output = "/1: near \"GET\": syntax error/";
30 30
31my $result; 31my $result;
32 32
diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t
index 9eb77ce4..ba149842 100644
--- a/plugins/t/check_disk.t
+++ b/plugins/t/check_disk.t
@@ -7,9 +7,11 @@
7# TODO: Add in tests for perf data. Need to beef up Monitoring::Plugin::Performance to cater for max, min, etc 7# TODO: Add in tests for perf data. Need to beef up Monitoring::Plugin::Performance to cater for max, min, etc
8 8
9use strict; 9use strict;
10use warnings;
10use Test::More; 11use Test::More;
11use NPTest; 12use NPTest;
12use POSIX qw(ceil floor); 13use POSIX qw(ceil floor);
14use Data::Dumper;
13 15
14my $successOutput = '/^DISK OK/'; 16my $successOutput = '/^DISK OK/';
15my $failureOutput = '/^DISK CRITICAL/'; 17my $failureOutput = '/^DISK CRITICAL/';
@@ -20,228 +22,293 @@ my $result;
20my $mountpoint_valid = getTestParameter( "NP_MOUNTPOINT_VALID", "Path to valid mountpoint", "/"); 22my $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"); 23my $mountpoint2_valid = getTestParameter( "NP_MOUNTPOINT2_VALID", "Path to another valid mountpoint. Must be different from 1st one", "/var");
22 24
25my $output_format = "--output-format mp-test-json";
26
23if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") { 27if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") {
24 plan skip_all => "Need 2 mountpoints to test"; 28 plan skip_all => "Need 2 mountpoints to test";
25} else { 29} else {
26 plan tests => 94; 30 plan tests => 96;
27} 31}
28 32
29$result = NPTest->testCmd( 33$result = NPTest->testCmd(
30 "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -p $mountpoint2_valid" 34 "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -P -p $mountpoint2_valid $output_format"
31 ); 35 );
32cmp_ok( $result->return_code, "==", 0, "Checking two mountpoints (must have at least 1% free in space and inodes)"); 36cmp_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 37
38my $result_mp2 = $result->{'mp_test_result'}->{'checks'}->[0];
39my $result_mp1 = $result->{'mp_test_result'}->{'checks'}->[1];
40
41like($result->{'mp_test_result'}->{'state'}, "/OK/", "Main result is OK");
42like($result_mp2->{'state'}, "/OK/", "First sub result is OK");
43like($result_mp1->{'state'}, "/OK/", "Second sub result is OK");
44
45my @perfdata;
46@perfdata[0] = $result_mp2->{'checks'}->[0]->{'perfdata'}->[0];
47@perfdata[1] = $result_mp1->{'checks'}->[0]->{'perfdata'}->[0];
48
49my $absolut_space_mp1 = $perfdata[1]->{'max'}->{'value'};
50# print("absolute space on mp1: ". $absolut_space_mp1 . "\n");
51my $absolut_used_space_mp1 = $perfdata[1]->{'value'}->{'value'};
38 52
39# Get perf data 53my $free_percent_on_mp1 = ($absolut_space_mp1 - $absolut_used_space_mp1) / ($absolut_space_mp1/100);
40# Should use Monitoring::Plugin 54# print("free percent on mp1: ". $free_percent_on_mp1 . "\n");
41my @perf_data = sort(split(/ /, $result->perf_output));
42 55
56my $absolut_space_mp2 = $perfdata[0]->{'max'}->{'value'};
57# print("absolute space on mp2: ". $absolut_space_mp2 . "\n");
58my $absolut_used_space_mp2 = $perfdata[0]->{'value'}->{'value'};
59
60my $free_percent_on_mp2 = (($absolut_space_mp2 - $absolut_used_space_mp2)/ ($absolut_space_mp2/100));
61# print("free percent on mp2: ". $free_percent_on_mp2 . "\n");
62
63# Decrease precision of numbers since the the fs might be modified between the two runs
64$perfdata[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000);
65$perfdata[1]->{'value'}->{'value'} = int($perfdata[1]->{'value'}->{'value'} / 1000000);
43 66
44# Calculate avg_free free on mountpoint1 and mountpoint2 67# Calculate avg_free free on mountpoint1 and mountpoint2
45# because if you check in the middle, you should get different errors 68# because if you check in the middle, you should get different errors
46$_ = $result->output; 69my $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+)%/); 70# 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); 71my ($more_free, $less_free);
51if ($free_on_mp1 > $free_on_mp2) { 72if ($free_percent_on_mp1 > $free_percent_on_mp2) {
52 $more_free = $mountpoint_valid; 73 $more_free = $mountpoint_valid;
53 $less_free = $mountpoint2_valid; 74 $less_free = $mountpoint2_valid;
54} elsif ($free_on_mp1 < $free_on_mp2) { 75} elsif ($free_percent_on_mp1 < $free_percent_on_mp2) {
55 $more_free = $mountpoint2_valid; 76 $more_free = $mountpoint2_valid;
56 $less_free = $mountpoint_valid; 77 $less_free = $mountpoint_valid;
57} else { 78} else {
58 die "Two mountpoints are the same - cannot do rest of test"; 79 die "Two mountpoints are the same - cannot do rest of test";
59} 80}
60if($free_on_mp1 == $avg_free || $free_on_mp2 == $avg_free) {
61 die "One mountpoints has average space free - cannot do rest of test";
62}
63 81
82# print("less free: " . $less_free . "\n");
83# print("more free: " . $more_free . "\n");
64 84
65# Do same for inodes 85if($free_percent_on_mp1 == $avg_free_percent || $free_percent_on_mp2 == $avg_free_percent) {
66$_ = $result->output; 86 die "One mountpoints has average space free - cannot do rest of test";
67my ($free_inode_on_mp1, $free_inode_on_mp2) = (m/inode=(\d+)%.*inode=(\d+)%/);
68die "Cannot parse free inodes: $_" unless ($free_inode_on_mp1 && $free_inode_on_mp2);
69my $avg_inode_free = ceil(($free_inode_on_mp1 + $free_inode_on_mp2)/2);
70my ($more_inode_free, $less_inode_free);
71if ($free_inode_on_mp1 > $free_inode_on_mp2) {
72 $more_inode_free = $mountpoint_valid;
73 $less_inode_free = $mountpoint2_valid;
74} elsif ($free_inode_on_mp1 < $free_inode_on_mp2) {
75 $more_inode_free = $mountpoint2_valid;
76 $less_inode_free = $mountpoint_valid;
77} else {
78 die "Two mountpoints with same inodes free - cannot do rest of test";
79}
80if($free_inode_on_mp1 == $avg_inode_free || $free_inode_on_mp2 == $avg_inode_free) {
81 die "One mountpoints has average inodes free - cannot do rest of test";
82} 87}
83 88
89# TODO enable inode checks later when there is enough nerves for Perl
90# my $have_inodes = 1;
91# my $more_inode_free;
92# my $less_inode_free;
93# my $avg_inode_free_percentage;
94
95# # Do we have an inode reading? might not be if the filesystem does not have a fixed number of inodes
96# if ($result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2] && $result->{'mp_test_result'}->{'checks'}->[0]) {
97# my $used_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'};
98# my $total_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'};
99
100# my $free_inodes_on_mp1 = $total_inodes_on_mp1 - $used_inodes_on_mp1;
101# my $free_inode_percentage_on_mp1 = $free_inodes_on_mp1 / ($total_inodes_on_mp1 / 100);
102
103# # print("free inodes on mp1: " . $free_inodes_on_mp1 . "\n");
104# # print("total inodes on mp1: " . $total_inodes_on_mp1 . "\n");
105# # print("free inode percentage on mp1: " . $free_inode_percentage_on_mp1 . "\n");
106
107# my $used_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'};
108# my $total_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'};
109# my $free_inodes_on_mp2 = $total_inodes_on_mp2 - $used_inodes_on_mp2;
110# my $free_inode_percentage_on_mp2 = $free_inodes_on_mp2 / ($total_inodes_on_mp2 / 100);
111
112# # print("free inodes on mp2: " . $free_inodes_on_mp2 . "\n");
113# # print("total inodes on mp2: " . $total_inodes_on_mp2 . "\n");
114# # print("free inode percentage on mp2: " . $free_inode_percentage_on_mp2 . "\n");
115
116# my $avg_inode_free_percentage = ceil(($free_inode_percentage_on_mp1 + $free_inode_percentage_on_mp2)/2);
117# if ($free_inode_percentage_on_mp1 > $free_inode_percentage_on_mp2) {
118# $more_inode_free = $mountpoint_valid;
119# $less_inode_free = $mountpoint2_valid;
120# } elsif ($free_inode_percentage_on_mp1 < $free_inode_percentage_on_mp2) {
121# $more_inode_free = $mountpoint2_valid;
122# $less_inode_free = $mountpoint_valid;
123# } else {
124# die "Two mountpoints with same inodes free - cannot do rest of test";
125# }
126# if($free_inode_percentage_on_mp1 == $avg_inode_free_percentage || $free_inode_percentage_on_mp2 == $avg_inode_free_percentage) {
127# die "One mountpoints has average inodes free - cannot do rest of test";
128# }
129# } else {
130# $have_inodes = 0;
131# }
132
84# Verify performance data 133# Verify performance data
85# First check absolute thresholds... 134# First check absolute thresholds...
86$result = NPTest->testCmd( 135$result = NPTest->testCmd(
87 "./check_disk -w 20 -c 10 -p $mountpoint_valid" 136 "./check_disk -w 20 -c 10 -p $mountpoint_valid $output_format"
88 ); 137 );
89$_ = $result->perf_output; 138
90my ($warn_absth_data, $crit_absth_data, $total_absth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); 139cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
91# default unit is MiB, but perfdata is always bytes 140
92is ($warn_absth_data, $total_absth_data - (20 * (2 ** 20)), "Wrong warning in perf data using absolute thresholds"); 141my $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"); 142my $crit_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'};
143my $total_absth_data= $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'};
144
145# print("warn: " .$warn_absth_data . "\n");
146# print("crit: " .$crit_absth_data . "\n");
147# print("total: " .$total_absth_data . "\n");
148
149is ($warn_absth_data <=> (20 * (2 ** 20)), 0, "Wrong warning in perf data using absolute thresholds");
150is ($crit_absth_data <=> (10 * (2 ** 20)), 0, "Wrong critical in perf data using absolute thresholds");
94 151
95# Then check percent thresholds. 152# Then check percent thresholds.
96$result = NPTest->testCmd( 153$result = NPTest->testCmd(
97 "./check_disk -w 20% -c 10% -p $mountpoint_valid" 154 "./check_disk -w 20% -c 10% -p $mountpoint_valid $output_format"
98 ); 155 );
99$_ = $result->perf_output; 156
100my ($warn_percth_data, $crit_percth_data, $total_percth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); 157cmp_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"); 158
102is ($crit_percth_data, int((1-10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds"); 159my $warn_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'};
160my $crit_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'};
161my $total_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'};
162
163# print("warn_percth_data: " . $warn_percth_data . "\n");
164# print("crit_percth_data: " . $crit_percth_data . "\n");
165
166is (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);
167is (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 168
104 169
105# Check when order of mount points are reversed, that perf data remains same 170# Check when order of mount points are reversed, that perf data remains same
106$result = NPTest->testCmd( 171$result = NPTest->testCmd(
107 "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid" 172 "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid $output_format"
108 ); 173 );
109@_ = sort(split(/ /, $result->perf_output)); 174cmp_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 175
176# write comparison set for perfdata here, but in reversed order, maybe there is a smarter way
177my @perfdata2;
178@perfdata2[0] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0];
179@perfdata2[1] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0];
112 180
113# Basic filesystem checks for sizes 181my $free_on_mp1 = ($perfdata2[1]->{'max'}->{'value'} - $perfdata2[1]->{'value'}->{'value'});
114$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free" ); 182my $free_on_mp2 = ($perfdata2[0]->{'max'}->{'value'} - $perfdata2[0]->{'value'}->{'value'});
115cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free"); 183# print "free on mp1: " . $free_on_mp1 . "\n";
116like ( $result->output, $successOutput, "OK output" ); 184# print "free on mp2: " . $free_on_mp2 . "\n";
117like ( $result->only_output, qr/free space/, "Have free space text"); 185# Either one of those should not be zero
118like ( $result->only_output, qr/$more_free/, "Have disk name in text"); 186die "Cannot parse output: $_" unless (defined($free_on_mp1) && defined($free_on_mp2));
119 187
120$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free" ); 188my $free_on_all = $free_on_mp1 + $free_on_mp2;
121cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free and $less_free");
122 189
123$_ = $result->output; 190# print "free on all: " . $free_on_all . "\n";
124 191
125my ($free_mb_on_mp1, $free_mb_on_mp2) = (m/(\d+)MiB .* (\d+)MiB /g); 192# Decrease precision of numbers since the the fs might be modified between the two runs
126die "Cannot parse output: $_" unless ($free_mb_on_mp1 && $free_mb_on_mp2); 193$perfdata2[0]->{'value'}->{'value'} = int($perfdata2[0]->{'value'}->{'value'} / 1000000);
194$perfdata2[1]->{'value'}->{'value'} = int($perfdata2[1]->{'value'}->{'value'} / 1000000);
195is_deeply(\@perfdata, \@perfdata2, "perf data for both filesystems same when reversed");
127 196
128my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2; 197# Basic filesystem checks for sizes
198$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free $output_format");
199cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
200like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free");
129 201
202$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free $output_format" );
203cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
204like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free and $less_free");
130 205
131 206
132$result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free" ); 207$result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free $output_format" );
133is( $result->only_output, "DISK OK", "No print out of disks with -e for OKs"); 208cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
134 209
135$result = NPTest->testCmd( "./check_disk 100 100 $more_free" ); 210$result = NPTest->testCmd( "./check_disk 101 101 $more_free" );
136cmp_ok( $result->return_code, '==', 0, "Old syntax okay" ); 211like($result->output, "/OK/", "OK in Output");
212cmp_ok( $result->return_code, '==', 0, "Old syntax okay, output was: ". $result->output . "\n" );
137 213
138$result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" ); 214$result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" );
139cmp_ok( $result->return_code, "==", 0, "At least 1% free" ); 215cmp_ok( $result->return_code, "==", 0, "At least 1% free" );
140 216
141$result = NPTest->testCmd( 217$result = NPTest->testCmd(
142 "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free" 218 "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free $output_format"
143 ); 219 );
144cmp_ok( $result->return_code, "==", 2, "Get critical on less_free mountpoint $less_free" ); 220cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
145like( $result->output, $failureOutput, "Right output" ); 221like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Get critical on less_free mountpoint $less_free");
146 222
147 223
148$result = NPTest->testCmd( 224$result = NPTest->testCmd(
149 "./check_disk -w $avg_free% -c 0% -p $less_free" 225 "./check_disk -w $avg_free_percent% -c 0% -p $less_free $output_format"
150 ); 226 );
151cmp_ok( $result->return_code, '==', 1, "Get warning on less_free mountpoint, when checking avg_free"); 227cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
228like($result->{'mp_test_result'}->{'state'}, "/WARNING/", "Get warning on less_free mountpoint, when checking avg_free");
152 229
153$result = NPTest->testCmd( 230$result = NPTest->testCmd(
154 "./check_disk -w $avg_free% -c $avg_free% -p $more_free" 231 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
155 ); 232 );
156cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free"); 233cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free");
157 234
158$result = NPTest->testCmd( 235$result = NPTest->testCmd(
159 "./check_disk -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" 236 "./check_disk -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
160 ); 237 );
161cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning"); 238cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning");
162my $all_disks = $result->output; 239my $all_disks = $result->output;
163 240
164$result = NPTest->testCmd( 241$result = NPTest->testCmd(
165 "./check_disk -e -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" 242 "./check_disk -e -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
166 ); 243 );
167isnt( $result->output, $all_disks, "-e gives different output"); 244isnt( $result->output, $all_disks, "-e gives different output");
168 245
169# Need spaces around filesystem name in case less_free and more_free are nested 246# Need spaces around filesystem name in case less_free and more_free are nested
170like( $result->output, qr/ $less_free /, "Found problem $less_free"); 247like( $result->output, qr/ $less_free /, "Found problem $less_free");
171unlike( $result->only_output, qr/ $more_free /, "Has ignored $more_free as not a problem"); 248unlike( $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"); 249like( $result->perf_output, qr/'$more_free'=/, "But $more_free is still in perf data");
173 250
174$result = NPTest->testCmd( 251$result = NPTest->testCmd(
175 "./check_disk -w $avg_free% -c 0% -p $more_free" 252 "./check_disk -w $avg_free_percent% -c 0% -p $more_free"
176 ); 253 );
177cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free"); 254cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free");
178 255
179$result = NPTest->testCmd( 256$result = NPTest->testCmd(
180 "./check_disk -w $avg_free% -c $avg_free% -p $less_free" 257 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free"
181 ); 258 );
182cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free"); 259cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free");
183$result = NPTest->testCmd( 260$result = NPTest->testCmd(
184 "./check_disk -w $avg_free% -c 0% -p $more_free -w $avg_free% -c $avg_free% -p $less_free" 261 "./check_disk -w $avg_free_percent% -c 0% -p $more_free -w $avg_free_percent% -c $avg_free_percent% -p $less_free"
185 ); 262 );
186cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical"); 263cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical");
187 264
188$result = NPTest->testCmd( 265$result = NPTest->testCmd(
189 "./check_disk -w $avg_free% -c $avg_free% -p $less_free -w $avg_free% -c 0% -p $more_free" 266 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free -w $avg_free_percent% -c 0% -p $more_free"
190 ); 267 );
191cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); 268cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference");
192 269
193 270
194 271
195# Basic inode checks for sizes 272# Basic inode checks for sizes
273SKIP: {
274 skip "No inode data", 14 if 1 eq 1;
196 275
197$result = NPTest->testCmd( "./check_disk --icritical 1% --iwarning 1% -p $more_inode_free" ); 276 # $result = NPTest->testCmd( "./check_disk --icritical 1% --iwarning 1% -p $more_inode_free" );
198is( $result->return_code, 0, "At least 1% free on inodes for both mountpoints"); 277 # is( $result->return_code, 0, "At least 1% free on inodes for both mountpoints");
199
200$result = NPTest->testCmd( "./check_disk -K 100% -W 100% -p $less_inode_free" );
201is( $result->return_code, 2, "Critical requesting 100% free inodes for both mountpoints");
202
203$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");
205 278
206$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free" ); 279 # $result = NPTest->testCmd( "./check_disk -K 100% -W 100% -p $less_inode_free" );
207is( $result->return_code, 1, "Get warning on less_inode_free, when checking average"); 280 # is( $result->return_code, 2, "Critical requesting 100% free inodes for both mountpoints");
208 281
209$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free "); 282 # $result = NPTest->testCmd( "./check_disk --iwarning 1% --icritical 1% -p $more_inode_free -K 100% -W 100% -p $less_inode_free" );
210is( $result->return_code, 0, "Get ok on more_inode_free when checking average"); 283 # is( $result->return_code, 2, "Get critical on less_inode_free mountpoint $less_inode_free");
211 284
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" ); 285 # $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free" );
213is ($result->return_code, 1, "Combine above two tests, get warning"); 286 # is( $result->return_code, 1, "Get warning on less_inode_free, when checking average");
214$all_disks = $result->output;
215 287
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" ); 288 # $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free ");
217isnt( $result->output, $all_disks, "-e gives different output"); 289 # is( $result->return_code, 0, "Get ok on more_inode_free when checking average");
218like( $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");
220like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data");
221
222$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free" );
223is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average");
224
225$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" );
226is( $result->return_code, 2, "Get critical on less_inode_free, checking average");
227
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" );
229is( $result->return_code, 2, "Combining above two tests, get critical");
230
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" );
232cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference");
233 290
291 # $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" );
292 # is ($result->return_code, 1, "Combine above two tests, get warning");
293 # $all_disks = $result->output;
234 294
295 # $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" );
296 # isnt( $result->output, $all_disks, "-e gives different output");
297 # like( $result->output, qr/$less_inode_free/, "Found problem $less_inode_free");
298 # unlike( $result->only_output, qr/$more_inode_free\s/, "Has ignored $more_inode_free as not a problem");
299 # like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data");
235 300
301 # $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" );
302 # is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average");
236 303
304 # $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" );
305 # is( $result->return_code, 2, "Get critical on less_inode_free, checking average");
237 306
307 # $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" );
308 # is( $result->return_code, 2, "Combining above two tests, get critical");
238 309
239TODO: { 310 # $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" );
240 local $TODO = "Invalid percent figures"; 311 # cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference");
241 $result = NPTest->testCmd(
242 "./check_disk -w 10% -c 15% -p $mountpoint_valid"
243 );
244 cmp_ok( $result->return_code, '==', 3, "Invalid command line options" );
245} 312}
246 313
247$result = NPTest->testCmd( 314$result = NPTest->testCmd(
@@ -249,9 +316,9 @@ $result = NPTest->testCmd(
249 ); 316 );
250cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" ); 317cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" );
251 318
252$result = NPTest->testCmd( "./check_disk -w 100% -c 100% ".${mountpoint_valid} ); # 100% empty 319$result = NPTest->testCmd( "./check_disk -w 100% -c 100% $output_format ".${mountpoint_valid} ); # 100% empty
253cmp_ok( $result->return_code, "==", 2, "100% empty" ); 320cmp_ok( $result->return_code, "==", 0, "100% empty" );
254like( $result->output, $failureOutput, "Right output" ); 321like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "100% empty");
255 322
256$result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" ); 323$result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" );
257cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" ); 324cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" );
@@ -263,7 +330,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 330# 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} ); 331$result = NPTest->testCmd( "./check_disk 0 0 ".${mountpoint_valid} );
265cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used"); 332cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used");
266like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments"); 333# like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments");
334# TODO not sure what the above should test, taking it out
267 335
268$result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" ); 336$result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" );
269cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" ); 337cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" );
@@ -311,23 +379,27 @@ $result = NPTest->testCmd( "./check_disk -w 0% -c 0% -p / -p /" );
311unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice"); 379unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice");
312 380
313# are partitions added if -C is given without path selection -p ? 381# 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" ); 382$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"); 383cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
384cmp_ok(scalar $result->{'mp_test_result'}->{'checks'}, '>', 1, "-C invokes matchall logic again");
385
386my $value_below = ($free_on_all - (10**6)) % (10**5) * (10**5);
387my $value_above = $free_on_all + (10**6) % (10**5) * (10**5);
316 388
317# grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit 389# 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" ); 390$result = NPTest->testCmd( "./check_disk -u Bytes -w ". $value_above ." -c ". $value_above ." -g group -p $mountpoint_valid -p $mountpoint2_valid" );
319cmp_ok( $result->return_code, '==', 2, "grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit\nInstead received: " . $result->output); 391cmp_ok( $result->return_code, '==', 2, "grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit");
320 392
321# grouping: exit warning if the sum of free megs on mp1+mp2 is between -w and -c 393# grouping: exit warning if the sum of free megs on mp1+mp2 is between -w and -c
322$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" ); 394$result = NPTest->testCmd( "./check_disk -u Bytes -w ". $value_above ." -c ". $value_below ." -g group -p $mountpoint_valid -p $mountpoint2_valid" );
323cmp_ok( $result->return_code, '==', 1, "grouping: exit warning if the sum of free megs on mp1+mp2 is between -w and -c "); 395cmp_ok( $result->return_code, '==', 1, "grouping: exit warning if the sum of free megs on mp1+mp2 is between -w and -c ");
324 396
325# grouping: exit ok if the sum of free megs on mp1+mp2 is more than warn/crit 397# grouping: exit ok if the sum of free megs on mp1+mp2 is more than warn/crit
326$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" ); 398$result = NPTest->testCmd( "./check_disk -u Bytes -w ". $value_below ." -c ". $value_below ." -g group -p $mountpoint_valid -p $mountpoint2_valid" );
327cmp_ok( $result->return_code, '==', 0, "grouping: exit ok if the sum of free megs on mp1+mp2 is more than warn/crit"); 399cmp_ok( $result->return_code, '==', 0, "grouping: exit ok if the sum of free megs on mp1+mp2 is more than warn/crit");
328 400
329# grouping: exit unknown if group name is given after -p 401# grouping: exit unknown if group name is given after -p
330$result = NPTest->testCmd( "./check_disk -w ". ($free_mb_on_all - 1) ." -c ". ($free_mb_on_all - 1) ." -p $mountpoint_valid -g group -p $mountpoint2_valid" ); 402$result = NPTest->testCmd( "./check_disk -u Bytes -w ". $value_below ." -c ". $value_below ." -p $mountpoint_valid -g group -p $mountpoint2_valid" );
331cmp_ok( $result->return_code, '==', 3, "Invalid options: -p must come after groupname"); 403cmp_ok( $result->return_code, '==', 3, "Invalid options: -p must come after groupname");
332 404
333# regex: exit unknown if given regex is not compilable 405# regex: exit unknown if given regex is not compilable
@@ -359,39 +431,37 @@ like( $result->output, qr/$mountpoint2_valid/,"ignore: output data does have $mo
359# ignore-missing: exit okay, when fs is not accessible 431# ignore-missing: exit okay, when fs is not accessible
360$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p /bob"); 432$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"); 433cmp_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'); 434like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
363 435
364# ignore-missing: exit okay, when regex does not match 436# ignore-missing: exit okay, when regex does not match
365$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r /bob"); 437$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"); 438cmp_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'); 439like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
368 440
369# ignore-missing: exit okay, when fs with exact match (-E) is not found 441# 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"); 442$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"); 443cmp_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'); 444like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
373 445
374# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (regex) 446# 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 '^/\$'"); 447$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"); 448cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
377like( $result->output, '/^DISK OK - free space: \/ .*$/', 'Output OK');
378 449
379# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (path) 450# 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 '/'"); 451$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"); 452cmp_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'); 453# like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK');
383 454
384# ignore-missing: exit okay, when checking one non-existing fs (path) and one ignored 455# 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"); 456$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"); 457cmp_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'); 458like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
388 459
389# ignore-missing: exit okay, when regex match does not find anything 460# 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"); 461$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"); 462cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
392like( $result->output, '/^DISK OK\|$/', 'Output OK');
393 463
394# ignore-missing: exit okay, when regex match does not find anything 464# 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"); 465$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"); 466cmp_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'); 467like( $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_http.t b/plugins/t/check_http.t
index 6ab4a5b6..bb1fd27d 100644
--- a/plugins/t/check_http.t
+++ b/plugins/t/check_http.t
@@ -45,7 +45,7 @@ $res = NPTest->testCmd(
45 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3" 45 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3"
46 ); 46 );
47cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); 47cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
48cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK"); 48like( $res->output, "/Socket timeout after/", "Output OK");
49 49
50$res = NPTest->testCmd( 50$res = NPTest->testCmd(
51 "./$plugin $hostname_invalid -wt 1 -ct 2" 51 "./$plugin $hostname_invalid -wt 1 -ct 2"
diff --git a/plugins/t/check_jabber.t b/plugins/t/check_jabber.t
index fcdae179..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 = '/CRITICAL\s-\sSocket 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_ldap.t b/plugins/t/check_ldap.t
index b8a4a766..f3162ebb 100644
--- a/plugins/t/check_ldap.t
+++ b/plugins/t/check_ldap.t
@@ -24,7 +24,7 @@ SKIP: {
24 24
25 $result = NPTest->testCmd("$command -H $host_nonresponsive -b ou=blah -t 2 -w 1 -c 1"); 25 $result = NPTest->testCmd("$command -H $host_nonresponsive -b ou=blah -t 2 -w 1 -c 1");
26 is( $result->return_code, 2, "$command -H $host_nonresponsive -b ou=blah -t 5 -w 2 -c 3" ); 26 is( $result->return_code, 2, "$command -H $host_nonresponsive -b ou=blah -t 5 -w 2 -c 3" );
27 is( $result->output, 'CRITICAL - Socket timeout after 2 seconds', "output ok" ); 27 like($result->output, '/Socket timeout after \d+ seconds/', "output ok" );
28}; 28};
29 29
30SKIP: { 30SKIP: {
@@ -32,7 +32,7 @@ SKIP: {
32 32
33 $result = NPTest->testCmd("$command -H $hostname_invalid -b ou=blah -t 5"); 33 $result = NPTest->testCmd("$command -H $hostname_invalid -b ou=blah -t 5");
34 is( $result->return_code, 2, "$command -H $hostname_invalid -b ou=blah -t 5" ); 34 is( $result->return_code, 2, "$command -H $hostname_invalid -b ou=blah -t 5" );
35 is( $result->output, 'Could not bind to the LDAP server', "output ok" ); 35 like( $result->output, '/could not bind to the LDAP server/', "output ok" );
36}; 36};
37 37
38SKIP: { 38SKIP: {
@@ -42,30 +42,30 @@ SKIP: {
42 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3"; 42 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3";
43 $result = NPTest->testCmd($cmd); 43 $result = NPTest->testCmd($cmd);
44 is( $result->return_code, 0, $cmd ); 44 is( $result->return_code, 0, $cmd );
45 like( $result->output, '/^LDAP OK - \d+.\d+ seconds response time\|time=\d+\.\d+s;2\.0+;3\.0+;0\.0+$/', "output ok" ); 45 like( $result->output, '/connection time \d+.\d+s/', "output ok" );
46 46
47 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000 -C 10000001"; 47 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000 -C 10000001";
48 $result = NPTest->testCmd($cmd); 48 $result = NPTest->testCmd($cmd);
49 is( $result->return_code, 0, $cmd ); 49 is( $result->return_code, 0, $cmd );
50 like( $result->output, '/^LDAP OK - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000;10000001;0\.0+$/', "output ok" ); 50 like( $result->output, '/found \d+ entries/', "output ok" );
51 51
52 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001:"; 52 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001:";
53 $result = NPTest->testCmd($cmd); 53 $result = NPTest->testCmd($cmd);
54 is( $result->return_code, 2, $cmd ); 54 is( $result->return_code, 2, $cmd );
55 like( $result->output, '/^LDAP CRITICAL - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000:;10000001:;0\.0+$/', "output ok" ); 55 like( $result->output, '/found \d+ entries/', "output ok" );
56 56
57 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 0 -C 0"; 57 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 0 -C 0";
58 $result = NPTest->testCmd($cmd); 58 $result = NPTest->testCmd($cmd);
59 is( $result->return_code, 2, $cmd ); 59 is( $result->return_code, 2, $cmd );
60 like( $result->output, '/^LDAP CRITICAL - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;0;0;0\.0+$/', "output ok" ); 60 like( $result->output, '/found \d+ entries/', "output ok" );
61 61
62 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001"; 62 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001";
63 $result = NPTest->testCmd($cmd); 63 $result = NPTest->testCmd($cmd);
64 is( $result->return_code, 1, $cmd ); 64 is( $result->return_code, 1, $cmd );
65 like( $result->output, '/^LDAP WARNING - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000:;10000001;0\.0+$/', "output ok" ); 65 like( $result->output, '/found \d+ entries/', "output ok" );
66 66
67 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -C 10000001"; 67 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -C 10000001";
68 $result = NPTest->testCmd($cmd); 68 $result = NPTest->testCmd($cmd);
69 is( $result->return_code, 0, $cmd ); 69 is( $result->return_code, 0, $cmd );
70 like( $result->output, '/^LDAP OK - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;;10000001;0\.0+$/', "output ok" ); 70 like( $result->output, '/found \d+ entries/', "output ok" );
71}; 71};
diff --git a/plugins/t/check_load.t b/plugins/t/check_load.t
index bba8947c..fc26bb35 100644
--- a/plugins/t/check_load.t
+++ b/plugins/t/check_load.t
@@ -16,28 +16,28 @@ my $successScaledOutput = "/^LOAD OK - scaled load average: $loadValue, $loadVal
16my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/"; 16my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/";
17my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/"; 17my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/";
18 18
19plan tests => 13; 19plan tests => 8;
20 20
21$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100" ); 21$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100" );
22cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); 22cmp_ok( $res->return_code, 'eq', 0, "load not over 100");
23like( $res->output, $successOutput, "Output OK"); 23# like( $res->output, $successOutput, "Output OK");
24 24
25$res = NPTest->testCmd( "./check_load -w 0,0,0 -c 0,0,0" ); 25$res = NPTest->testCmd( "./check_load -w 0,0,0 -c 0,0,0" );
26cmp_ok( $res->return_code, 'eq', 2, "Load over 0"); 26cmp_ok( $res->return_code, 'eq', 2, "Load over 0");
27like( $res->output, $failureOutput, "Output OK"); 27# like( $res->output, $failureOutput, "Output OK");
28 28
29$res = NPTest->testCmd( "./check_load -r -w 0,0,0 -c 0,0,0" ); 29$res = NPTest->testCmd( "./check_load -r -w 0,0,0 -c 0,0,0" );
30cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division"); 30cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division");
31like( $res->output, $failurScaledOutput, "Output OK"); 31# like( $res->output, $failurScaledOutput, "Output OK");
32 32
33$res = NPTest->testCmd( "./check_load -w 100 -c 100,110" ); 33$res = NPTest->testCmd( "./check_load -w 100 -c 100,110" );
34cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments"); 34cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments");
35like( $res->output, $successOutput, "Output OK"); 35# like( $res->output, $successOutput, "Output OK");
36like( $res->perf_output, "/load1=$loadValue;100.000;100.000/", "Test handling of non triplet thresholds (load1)"); 36like( $res->perf_output, "/'load1'=$loadValue;~:100.0+;~:100.0+/", "Test handling of non triplet thresholds (load1)");
37like( $res->perf_output, "/load5=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load5)"); 37like( $res->perf_output, "/'load5'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load5)");
38like( $res->perf_output, "/load15=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load15)"); 38like( $res->perf_output, "/'load15'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load15)");
39 39
40 40
41$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100 -r" ); 41$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100 -r" );
42cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); 42cmp_ok( $res->return_code, 'eq', 0, "load not over 100");
43like( $res->output, $successScaledOutput, "Output OK"); 43# like( $res->output, $successScaledOutput, "Output OK");
diff --git a/plugins/t/check_mysql.t b/plugins/t/check_mysql.t
index baf3acc6..9114cccc 100644
--- a/plugins/t/check_mysql.t
+++ b/plugins/t/check_mysql.t
@@ -11,6 +11,7 @@
11# mysql -u$user -p$password -h$host $db 11# mysql -u$user -p$password -h$host $db
12 12
13use strict; 13use strict;
14use warnings;
14use Test::More; 15use Test::More;
15use NPTest; 16use NPTest;
16 17
@@ -21,11 +22,11 @@ plan skip_all => "check_mysql not compiled" unless (-x "check_mysql");
21plan tests => 15; 22plan tests => 15;
22 23
23my $bad_login_output = '/Access denied for user /'; 24my $bad_login_output = '/Access denied for user /';
24my $mysqlserver = getTestParameter("NP_MYSQL_SERVER", "A MySQL Server hostname or IP with no slaves setup"); 25my $mysqlserver = getTestParameter("NP_MYSQL_SERVER", "A MySQL Server hostname or IP with no replica setup");
25my $mysqlsocket = getTestParameter("NP_MYSQL_SOCKET", "Full path to a MySQL Server socket with no slaves setup"); 26my $mysqlsocket = getTestParameter("NP_MYSQL_SOCKET", "Full path to a MySQL Server socket with no replica setup");
26my $mysql_login_details = getTestParameter("NP_MYSQL_LOGIN_DETAILS", "Command line parameters to specify login access (requires REPLICATION CLIENT privileges)", "-u test -ptest"); 27my $mysql_login_details = getTestParameter("NP_MYSQL_LOGIN_DETAILS", "Command line parameters to specify login access (requires REPLICATION CLIENT privileges)", "-u test -ptest");
27my $with_slave = getTestParameter("NP_MYSQL_WITH_SLAVE", "MySQL server with slaves setup"); 28my $with_replica = getTestParameter("NP_MYSQL_WITH_REPLICA", "MySQL server with replica setup");
28my $with_slave_login = getTestParameter("NP_MYSQL_WITH_SLAVE_LOGIN", "Login details for server with slave (requires REPLICATION CLIENT privileges)", $mysql_login_details || "-u test -ptest"); 29my $with_replica_login = getTestParameter("NP_MYSQL_WITH_REPLICA_LOGIN", "Login details for server with replica (requires REPLICATION CLIENT privileges)", $mysql_login_details || "-u test -ptest");
29 30
30my $result; 31my $result;
31 32
@@ -39,8 +40,8 @@ SKIP: {
39 like( $result->output, $bad_login_output, "Expected login failure message"); 40 like( $result->output, $bad_login_output, "Expected login failure message");
40 41
41 $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details"); 42 $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details");
42 cmp_ok( $result->return_code, "==", 1, "No slaves defined" ); 43 cmp_ok( $result->return_code, "==", 1, "No replicas defined" );
43 like( $result->output, "/No slaves defined/", "Correct error message"); 44 like( $result->output, "/no replicas defined/", "Correct error message");
44} 45}
45 46
46SKIP: { 47SKIP: {
@@ -53,22 +54,22 @@ SKIP: {
53 like( $result->output, $bad_login_output, "Expected login failure message"); 54 like( $result->output, $bad_login_output, "Expected login failure message");
54 55
55 $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details"); 56 $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details");
56 cmp_ok( $result->return_code, "==", 1, "No slaves defined" ); 57 cmp_ok( $result->return_code, "==", 1, "No replicas defined" );
57 like( $result->output, "/No slaves defined/", "Correct error message"); 58 like( $result->output, "/no replicas defined/", "Correct error message");
58} 59}
59 60
60SKIP: { 61SKIP: {
61 skip "No mysql server with slaves defined", 5 unless $with_slave; 62 skip "No mysql server with replicas defined", 5 unless $with_replica;
62 $result = NPTest->testCmd("./check_mysql -H $with_slave $with_slave_login"); 63 $result = NPTest->testCmd("./check_mysql -H $with_replica $with_replica_login");
63 cmp_ok( $result->return_code, '==', 0, "Login okay"); 64 cmp_ok( $result->return_code, '==', 0, "Login okay");
64 65
65 $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login"); 66 $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login");
66 cmp_ok( $result->return_code, "==", 0, "Slaves okay" ); 67 cmp_ok( $result->return_code, "==", 0, "Replicas okay" );
67 68
68 $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login -w 60"); 69 $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60");
69 cmp_ok( $result->return_code, '==', 0, 'Slaves are not > 60 seconds behind'); 70 cmp_ok( $result->return_code, '==', 0, 'Replicas are not > 60 seconds behind');
70 71
71 $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login -w 60:"); 72 $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60:");
72 cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind'); 73 cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind');
73 like( $result->output, "/^SLOW_SLAVE WARNING:/", "Output okay"); 74 like( $result->output, "/^slow_replica/", "Output okay");
74} 75}
diff --git a/plugins/t/check_mysql_query.t b/plugins/t/check_mysql_query.t
index c30245b2..6de48bf6 100644
--- a/plugins/t/check_mysql_query.t
+++ b/plugins/t/check_mysql_query.t
@@ -54,5 +54,5 @@ like( $result->output, "/No rows returned/", "No rows error message");
54 54
55$result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES' -H $mysqlserver $mysql_login_details"); 55$result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES' -H $mysqlserver $mysql_login_details");
56cmp_ok( $result->return_code, '==', 2, "Data not numeric"); 56cmp_ok( $result->return_code, '==', 2, "Data not numeric");
57like( $result->output, "/Is not a numeric/", "Data not numeric error message"); 57like( $result->output, "/is not numeric/", "Data not numeric error message");
58 58
diff --git a/plugins/t/check_ntp.t b/plugins/t/check_ntp.t
index b8fc8fdf..7703bc3b 100644
--- a/plugins/t/check_ntp.t
+++ b/plugins/t/check_ntp.t
@@ -8,7 +8,7 @@ use strict;
8use Test::More; 8use Test::More;
9use NPTest; 9use NPTest;
10 10
11my @PLUGINS1 = ('check_ntp', 'check_ntp_peer', 'check_ntp_time'); 11my @PLUGINS1 = ('check_ntp_peer', 'check_ntp_time');
12my @PLUGINS2 = ('check_ntp_peer'); 12my @PLUGINS2 = ('check_ntp_peer');
13 13
14plan tests => (12 * scalar(@PLUGINS1)) + (6 * scalar(@PLUGINS2)); 14plan tests => (12 * scalar(@PLUGINS1)) + (6 * scalar(@PLUGINS2));
@@ -37,7 +37,7 @@ my $ntp_critmatch1 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?
37my $ntp_okmatch2 = '/^NTP\sOK:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2},\struechimers=[0-9]+/'; 37my $ntp_okmatch2 = '/^NTP\sOK:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2},\struechimers=[0-9]+/';
38my $ntp_warnmatch2 = '/^NTP\sWARNING:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}\s\(WARNING\),\struechimers=[0-9]+/'; 38my $ntp_warnmatch2 = '/^NTP\sWARNING:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}\s\(WARNING\),\struechimers=[0-9]+/';
39my $ntp_critmatch2 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+\s\(CRITICAL\),\sstratum=[0-9]{1,2},\struechimers=[0-9]+/'; 39my $ntp_critmatch2 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+\s\(CRITICAL\),\sstratum=[0-9]{1,2},\struechimers=[0-9]+/';
40my $ntp_noresponse = '/^(CRITICAL - Socket timeout after 3 seconds)|(NTP CRITICAL: No response from NTP server)$/'; 40my $ntp_noresponse = '/(.*Socket timeout after \d+ seconds.*)|(.*No response from NTP server.*)/';
41my $ntp_nosuchhost = '/^check_ntp.*: Invalid hostname/address - ' . $hostname_invalid . '/'; 41my $ntp_nosuchhost = '/^check_ntp.*: Invalid hostname/address - ' . $hostname_invalid . '/';
42 42
43 43
diff --git a/plugins/t/check_smtp.t b/plugins/t/check_smtp.t
index 1a1ebe3e..c2f53c3d 100644
--- a/plugins/t/check_smtp.t
+++ b/plugins/t/check_smtp.t
@@ -5,6 +5,7 @@
5# 5#
6 6
7use strict; 7use strict;
8use warnings;
8use Test::More; 9use Test::More;
9use NPTest; 10use NPTest;
10 11
@@ -24,7 +25,7 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
24 "An invalid (not known to DNS) hostname", "nosuchhost" ); 25 "An invalid (not known to DNS) hostname", "nosuchhost" );
25my $res; 26my $res;
26 27
27plan tests => 16; 28plan tests => 13;
28 29
29SKIP: { 30SKIP: {
30 skip "No SMTP server defined", 4 unless $host_tcp_smtp; 31 skip "No SMTP server defined", 4 unless $host_tcp_smtp;
@@ -42,12 +43,11 @@ SKIP: {
42 43
43 TODO: { 44 TODO: {
44 local $TODO = "Output is over two lines"; 45 local $TODO = "Output is over two lines";
45 like ( $res->output, qr/^SMTP WARNING/, "Correct error message" );
46 } 46 }
47 47
48 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp --ssl -p 25" ); 48 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp --ssl -p 25" );
49 is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp with TLS on standard SMTP port" ); 49 is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp with TLS on standard SMTP port" );
50 like ($res->output, qr/^CRITICAL - Cannot make SSL connection\./, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port"); 50 like ($res->output, qr/cannot create TLS context/, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port");
51} 51}
52 52
53SKIP: { 53SKIP: {
@@ -68,12 +68,10 @@ SKIP: {
68 skip "No SMTP server with TLS defined", 1 unless $host_tcp_smtp_tls; 68 skip "No SMTP server with TLS defined", 1 unless $host_tcp_smtp_tls;
69 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls --ssl" ); 69 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls --ssl" );
70 is ($res->return_code, 0, "Check rc of connecting to $host_tcp_smtp_tls with TLS" ); 70 is ($res->return_code, 0, "Check rc of connecting to $host_tcp_smtp_tls with TLS" );
71 like ($res->output, qr/^SMTP OK - /, "Check output of connecting to $host_tcp_smtp_tls with TLS" );
72 71
73 my $unused_port = 4465; 72 my $unused_port = 4465;
74 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" ); 73 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" );
75 is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port" ); 74 is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port" );
76 like ($res->output, qr/^connect to address $host_tcp_smtp_tls and port $unused_port: Connection refused/, "Check output of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port");
77} 75}
78 76
79$res = NPTest->testCmd( "./check_smtp $host_nonresponsive" ); 77$res = NPTest->testCmd( "./check_smtp $host_nonresponsive" );
diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t
index 576cc506..8d435df3 100644
--- a/plugins/t/check_snmp.t
+++ b/plugins/t/check_snmp.t
@@ -10,7 +10,7 @@ use NPTest;
10 10
11BEGIN { 11BEGIN {
12 plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp"; 12 plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp";
13 plan tests => 63; 13 plan tests => 62;
14} 14}
15 15
16my $res; 16my $res;
@@ -24,7 +24,7 @@ my $user_snmp = getTestParameter("NP_SNMP_USER", "An SNMP user", "auth_
24 24
25$res = NPTest->testCmd( "./check_snmp -t 1" ); 25$res = NPTest->testCmd( "./check_snmp -t 1" );
26is( $res->return_code, 3, "No host name" ); 26is( $res->return_code, 3, "No host name" );
27is( $res->output, "No host specified" ); 27is( $res->output, "No OIDs specified" );
28 28
29$res = NPTest->testCmd( "./check_snmp -H fakehostname --ignore-mib-parsing-errors" ); 29$res = NPTest->testCmd( "./check_snmp -H fakehostname --ignore-mib-parsing-errors" );
30is( $res->return_code, 3, "No OIDs specified" ); 30is( $res->return_code, 3, "No OIDs specified" );
@@ -32,145 +32,124 @@ is( $res->output, "No OIDs specified" );
32 32
33$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3 -U not_a_user --seclevel=rubbish" ); 33$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3 -U not_a_user --seclevel=rubbish" );
34is( $res->return_code, 3, "Invalid seclevel" ); 34is( $res->return_code, 3, "Invalid seclevel" );
35like( $res->output, "/check_snmp: Invalid seclevel - rubbish/" ); 35like( $res->output, "/invalid security level: rubbish/" );
36 36
37$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3c" ); 37$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3c" );
38is( $res->return_code, 3, "Invalid protocol" ); 38is( $res->return_code, 3, "Invalid protocol" );
39like( $res->output, "/check_snmp: Invalid SNMP version - 3c/" ); 39like( $res->output, "/invalid SNMP version/protocol: 3c/" );
40 40
41SKIP: { 41SKIP: {
42 skip "no snmp host defined", 50 if ( ! $host_snmp ); 42 skip "no snmp host defined", 50 if ( ! $host_snmp );
43 43
44 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); 44 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -P 2c -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:");
45 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" ); 45 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" );
46 like($res->output, '/^SNMP OK - (\d+)/', "String contains SNMP OK"); 46 $res->output =~ /\|.*=(\d+);/;
47 $res->output =~ /^SNMP OK - (\d+)/;
48 my $value = $1; 47 my $value = $1;
49 cmp_ok( $value, ">", 0, "Got a time value" ); 48 cmp_ok( $value, ">", 0, "Got a time value" );
50 like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it"); 49 like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it");
51 50
52 51
53 # some more threshold tests 52 # some more threshold tests
54 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1"); 53 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1 -P 2c");
55 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1" ); 54 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1" );
56 55
57 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:"); 56 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1: -P 2c");
58 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 1:" ); 57 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 1:" );
59 58
60 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1"); 59 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1 -P 2c");
61 cmp_ok( $res->return_code, '==', 2, "Threshold test -c ~:1" ); 60 cmp_ok( $res->return_code, '==', 2, "Threshold test -c ~:1" );
62 61
63 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10"); 62 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10 -P 2c");
64 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1:10" ); 63 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1:10" );
65 64
66 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10"); 65 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10 -P 2c");
67 cmp_ok( $res->return_code, '==', 0, "Threshold test -c \@1:10" ); 66 cmp_ok( $res->return_code, '==', 0, "Threshold test -c \@1:10" );
68 67
69 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 10:1");
70 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 10:1" );
71
72 68
73 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1:"); 69 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1: -P 2c");
74 cmp_ok( $res->return_code, '==', 0, "Test with numeric OID (no mibs loaded)" ); 70 cmp_ok( $res->return_code, '==', 0, "Test with numeric OID (no mibs loaded)" );
75 like($res->output, '/^SNMP OK - \d+/', "String contains SNMP OK");
76 71
77 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0"); 72 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -P 2c");
78 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" ); 73 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" );
79 unlike($res->perf_output, '/sysDescr/', "Perfdata doesn't contain string values"); 74 unlike($res->perf_output, '/sysDescr/', "Perfdata doesn't contain string values");
80 75
81 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0"); 76 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0 -P 2c");
82 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, comma-separated" ); 77 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, comma-separated" );
83 like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
84 78
85 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0"); 79 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0 -P 2c");
86 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, repeated option" ); 80 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, repeated option" );
87 like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
88 81
89 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1"); 82 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1 -P 2c");
90 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrSWRunIndex.1" ); 83 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrSWRunIndex.1" );
91 like($res->output, '/^SNMP OK - 1\s.*$/', "String fits SNMP OK and output format");
92 84
93 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1:"); 85 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1: -P 2c");
94 cmp_ok( $res->return_code, '==', 1, "Exit WARNING when querying hrSWRunIndex.1 and warn-th doesn't apply " ); 86 cmp_ok( $res->return_code, '==', 1, "Exit WARNING when querying hrSWRunIndex.1 and warn-th doesn't apply " );
95 like($res->output, '/^SNMP WARNING - \*1\*\s.*$/', "String matches SNMP WARNING and output format");
96 87
97 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0"); 88 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0 -P 2c");
98 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" ); 89 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" );
99 like($res->output, '/^SNMP CRITICAL - \*1\*\s.*$/', "String matches SNMP CRITICAL and output format");
100 90
101 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2"); 91 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2 -P 2c");
102 cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" ); 92 cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" );
103 like($res->output, "/^SNMP OK - 2 1/", "Got two values back" ); 93 like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" );
104 like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); 94 like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" );
105 like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" );
106 95
107 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2"); 96 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2 -P 2c");
108 cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" ); 97 cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" );
109 like($res->output, "/^SNMP CRITICAL - 2 *1*/", "Got two values back" ); 98 like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" );
110 like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); 99 like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" );
111 like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" );
112 100
113 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1:"); 101 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1: -P 2c");
114 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrMemorySize and hrSystemProcesses"); 102 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrMemorySize and hrSystemProcesses");
115 like($res->output, '/^SNMP OK - \d+ \d+/', "String contains hrMemorySize and hrSystemProcesses");
116 103
117 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0"); 104 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0 -P 2c");
118 cmp_ok( $res->return_code, '==', 0, "Exit OK with inside-range thresholds"); 105 cmp_ok( $res->return_code, '==', 0, "Exit OK with inside-range thresholds");
119 like($res->output, '/^SNMP OK - 1\s.*$/', "String matches SNMP OK and output format");
120 106
121 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3"); 107 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -P 2c");
122 $res->output =~ m/^SNMP OK - (\d+\.\d{2})\s.*$/; 108 $res->output =~ m/^.*Value: (\d+).*$/;
123 my $lower = $1 - 0.05; 109 my $lower = $1 - 0.05;
124 my $higher = $1 + 0.05; 110 my $higher = $1 + 0.05;
125 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3 -w $lower -c $higher"); 111 # $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -w $lower -c $higher -P 2c");
126 cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractionnal arguments"); 112 # cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractional arguments");
127 113
128 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2"); 114 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2 -P 2c");
129 cmp_ok( $res->return_code, '==', 1, "Exit WARNING on 2nd threshold"); 115 cmp_ok( $res->return_code, '==', 1, "Exit WARNING on 2nd threshold");
130 like($res->output, '/^SNMP WARNING - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s+\*1\*\s.*$/', "First OID returned as string, 2nd checked for thresholds");
131 116
132 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c ''"); 117 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c '' -P 2c");
133 cmp_ok( $res->return_code, '==', 0, "Empty thresholds doesn't crash"); 118 cmp_ok( $res->return_code, '==', 0, "Empty thresholds doesn't crash");
134 119
135 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2"); 120 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2 -P 2c");
136 cmp_ok( $res->return_code, '==', 0, "Skipping first two thresholds on 2 OID check"); 121 cmp_ok( $res->return_code, '==', 0, "Skipping first two thresholds on 2 OID check");
137 like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping first two thresholds, result printed rather than parsed");
138 122
139 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,,"); 123 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,, -P 2c");
140 cmp_ok( $res->return_code, '==', 0, "Skipping all thresholds"); 124 cmp_ok( $res->return_code, '==', 0, "Skipping all thresholds");
141 like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping all thresholds, result printed rather than parsed");
142 125
143 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec'"); 126 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec' -P 2c");
144 cmp_ok( $res->return_code, '==', 2, "Timetick used as a threshold"); 127 cmp_ok( $res->return_code, '==', 2, "Timetick used as a threshold");
145 like($res->output, '/^SNMP CRITICAL - \*\d+\* 1\/100 sec.*$/', "Timetick used as a threshold, parsed as numeric");
146 128
147 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0"); 129 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -P 2c");
148 cmp_ok( $res->return_code, '==', 0, "Timetick used as a string"); 130 cmp_ok( $res->return_code, '==', 0, "Timetick used as a string");
149 like($res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "Timetick used as a string, result printed rather than parsed");
150 131
151 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1"); 132 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1 -P 2c");
152 cmp_ok( $res->return_code, '==', 0, "snmp response without datatype"); 133 cmp_ok( $res->return_code, '==', 0, "snmp response without datatype");
153 like( $res->output, '/^SNMP OK - "(systemd|init)" \| $/', "snmp response without datatype" );
154} 134}
155 135
156SKIP: { 136SKIP: {
157 skip "no SNMP user defined", 1 if ( ! $user_snmp ); 137 skip "no SNMP user defined", 1 if ( ! $user_snmp );
158 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -o HOST-RESOURCES-MIB::hrSystemUptime.0 -P 3 -U $user_snmp -L noAuthNoPriv"); 138 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -o HOST-RESOURCES-MIB::hrSystemUptime.0 -P 3 -U $user_snmp -L noAuthNoPriv");
159 like( $res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "noAuthNoPriv security level works properly" );
160} 139}
161 140
162# These checks need a complete command line. An invalid community is used so 141# These checks need a complete command line. An invalid community is used so
163# the tests can run on hosts w/o snmp host/community in NPTest.cache. Execution will fail anyway 142# the tests can run on hosts w/o snmp host/community in NPTest.cache. Execution will fail anyway
164SKIP: { 143SKIP: {
165 skip "no non responsive host defined", 2 if ( ! $host_nonresponsive ); 144 skip "no non responsive host defined", 2 if ( ! $host_nonresponsive );
166 $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); 145 $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c");
167 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL with non responsive host" ); 146 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL with non responsive host" );
168 like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem"); 147 # like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem");
169} 148}
170 149
171SKIP: { 150SKIP: {
172 skip "no non invalid host defined", 2 if ( ! $hostname_invalid ); 151 skip "no non invalid host defined", 2 if ( ! $hostname_invalid );
173 $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); 152 $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c");
174 cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); 153 cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" );
175 like($res->output, '/External command error: .*(nosuchhost|Name or service not known|Unknown host).*/s', "String matches invalid host"); 154 like($res->output, '/.*Unknown host.*/s', "String matches invalid host");
176} 155}
diff --git a/plugins/t/check_ssh.t b/plugins/t/check_ssh.t
index 907d33a8..8a20782e 100644
--- a/plugins/t/check_ssh.t
+++ b/plugins/t/check_ssh.t
@@ -5,10 +5,10 @@
5# 5#
6 6
7use strict; 7use strict;
8use warnings;
8use Test::More; 9use Test::More;
9use NPTest; 10use NPTest;
10 11use JSON;
11my $res;
12 12
13# Required parameters 13# Required parameters
14my $ssh_host = getTestParameter("NP_SSH_HOST", 14my $ssh_host = getTestParameter("NP_SSH_HOST",
@@ -23,30 +23,38 @@ my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID",
23 "An invalid (not known to DNS) hostname", 23 "An invalid (not known to DNS) hostname",
24 "nosuchhost" ); 24 "nosuchhost" );
25 25
26 my $outputFormat = '--output-format mp-test-json';
27
28plan tests => 24;
26 29
27plan tests => 14 + 6; 30my $output;
31my $result;
28 32
29SKIP: { 33SKIP: {
30 skip "SSH_HOST must be defined", 6 unless $ssh_host; 34 skip "SSH_HOST must be defined", 6 unless $ssh_host;
35
36
31 my $result = NPTest->testCmd( 37 my $result = NPTest->testCmd(
32 "./check_ssh -H $ssh_host" 38 "./check_ssh -H $ssh_host" ." ". $outputFormat
33 ); 39 );
34 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); 40 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
35 like($result->output, '/^SSH OK - /', "Status text if command returned none (OK)"); 41 $output = decode_json($result->output);
42 is($output->{'state'}, "OK", "State was correct");
36 43
37 44
38 $result = NPTest->testCmd( 45 $result = NPTest->testCmd(
39 "./check_ssh -H $host_nonresponsive -t 2" 46 "./check_ssh -H $host_nonresponsive -t 2" ." ". $outputFormat
40 ); 47 );
41 cmp_ok($result->return_code, '==', 2, "Exit with return code 0 (OK)"); 48 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
42 like($result->output, '/^CRITICAL - Socket timeout after 2 seconds/', "Status text if command returned none (OK)"); 49 $output = decode_json($result->output);
50 is($output->{'state'}, "CRITICAL", "State was correct");
43 51
44 52
45 53
46 $result = NPTest->testCmd( 54 $result = NPTest->testCmd(
47 "./check_ssh -H $hostname_invalid -t 2" 55 "./check_ssh -H $hostname_invalid -t 2" ." ". $outputFormat
48 ); 56 );
49 cmp_ok($result->return_code, '==', 3, "Exit with return code 0 (OK)"); 57 cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)");
50 like($result->output, '/^check_ssh: Invalid hostname/', "Status text if command returned none (OK)"); 58 like($result->output, '/^check_ssh: Invalid hostname/', "Status text if command returned none (OK)");
51 59
52 60
@@ -63,46 +71,80 @@ SKIP: {
63 # 71 #
64 # where `comments` is optional, protoversion is the SSH protocol version and 72 # where `comments` is optional, protoversion is the SSH protocol version and
65 # softwareversion is an arbitrary string representing the server software version 73 # softwareversion is an arbitrary string representing the server software version
74
75 my $found_version = 0;
76
66 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1' | nc ${nc_flags}|"); 77 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1' | nc ${nc_flags}|");
67 sleep 0.1; 78 sleep 0.1;
68 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 79 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
69 cmp_ok( $res->return_code, '==', 0, "Got SSH protocol version control string"); 80 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
70 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.1 \(protocol 2.0\)/', "Output OK"); 81 $output = decode_json($result->output);
82 is($output->{'state'}, "OK", "State was correct");
83
84 # looking for the version
85 for my $subcheck (@{$output->{'checks'}}) {
86 if ($subcheck->{'output'} =~ /.*nagiosplug.ssh.0.1 \(protocol version: 2.0\).*/ ){
87 $found_version = 1;
88 }
89 }
90 cmp_ok($found_version, '==', 1, "Output OK");
71 close NC; 91 close NC;
72 92
73 open(NC, "echo 'SSH-2.0-3.2.9.1' | nc ${nc_flags}|"); 93 open(NC, "echo 'SSH-2.0-3.2.9.1' | nc ${nc_flags}|");
74 sleep 0.1; 94 sleep 0.1;
75 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 95 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
76 cmp_ok( $res->return_code, "==", 0, "Got SSH protocol version control string with non-alpha softwareversion string"); 96 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
77 like( $res->output, '/^SSH OK - 3.2.9.1 \(protocol 2.0\)/', "Output OK for non-alpha softwareversion string"); 97 $output = decode_json($result->output);
98 is($output->{'state'}, "OK", "State was correct");
99
100 $found_version = 0;
101 for my $subcheck (@{$output->{'checks'}}) {
102 if ($subcheck->{'output'} =~ /3.2.9.1 \(protocol version: 2.0\)/ ){
103 $found_version = 1;
104 }
105 }
106 cmp_ok($found_version, '==', 1, "Output OK");
78 close NC; 107 close NC;
79 108
80 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1 this is a comment' | nc ${nc_flags} |"); 109 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1 this is a comment' | nc ${nc_flags} |");
81 sleep 0.1; 110 sleep 0.1;
82 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003 -r nagiosplug.ssh.0.1" ); 111 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003 -r nagiosplug.ssh.0.1" ." ". $outputFormat);
83 cmp_ok( $res->return_code, '==', 0, "Got SSH protocol version control string, and parsed comment appropriately"); 112 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
84 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.1 \(protocol 2.0\)/', "Output OK"); 113 $output = decode_json($result->output);
114 is($output->{'state'}, "OK", "State was correct");
115
116 # looking for the version
117 $found_version = 0;
118 for my $subcheck (@{$output->{'checks'}}) {
119 if ($subcheck->{'output'} =~ /nagiosplug.ssh.0.1 \(protocol version: 2.0\)/ ){
120 $found_version = 1;
121 }
122 }
123 cmp_ok($found_version, '==', 1, "Output OK");
85 close NC; 124 close NC;
86 125
87 open(NC, "echo 'SSH-' | nc ${nc_flags}|"); 126 open(NC, "echo 'SSH-' | nc ${nc_flags}|");
88 sleep 0.1; 127 sleep 0.1;
89 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 128 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
90 cmp_ok( $res->return_code, '==', 2, "Got invalid SSH protocol version control string"); 129 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
91 like( $res->output, '/^SSH CRITICAL/', "Output OK"); 130 $output = decode_json($result->output);
131 is($output->{'state'}, "CRITICAL", "Got invalid SSH protocol version control string");
92 close NC; 132 close NC;
93 133
94 open(NC, "echo '' | nc ${nc_flags}|"); 134 open(NC, "echo '' | nc ${nc_flags}|");
95 sleep 0.1; 135 sleep 0.1;
96 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 136 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
97 cmp_ok( $res->return_code, '==', 2, "No version control string received"); 137 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
98 like( $res->output, '/^SSH CRITICAL - No version control string received/', "Output OK"); 138 $output = decode_json($result->output);
139 is($output->{'state'}, "CRITICAL", "No version control string received");
99 close NC; 140 close NC;
100 141
101 open(NC, "echo 'Not a version control string' | nc ${nc_flags}|"); 142 open(NC, "echo 'Not a version control string' | nc ${nc_flags}|");
102 sleep 0.1; 143 sleep 0.1;
103 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 144 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
104 cmp_ok( $res->return_code, '==', 2, "No version control string received"); 145 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
105 like( $res->output, '/^SSH CRITICAL - No version control string received/', "Output OK"); 146 $output = decode_json($result->output);
147 is($output->{'state'}, "CRITICAL", "No version control string received");
106 close NC; 148 close NC;
107 149
108 150
@@ -116,8 +158,18 @@ SKIP: {
116 echo 'Some\nPrepended\nData\nLines\n'; sleep 0.2; 158 echo 'Some\nPrepended\nData\nLines\n'; sleep 0.2;
117 echo 'SSH-2.0-nagiosplug.ssh.0.2';} | nc ${nc_flags}|"); 159 echo 'SSH-2.0-nagiosplug.ssh.0.2';} | nc ${nc_flags}|");
118 sleep 0.1; 160 sleep 0.1;
119 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 161 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
120 cmp_ok( $res->return_code, '==', 0, "Got delayed SSH protocol version control string"); 162 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
121 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.2 \(protocol 2.0\)/', "Output OK"); 163 $output = decode_json($result->output);
164 is($output->{'state'}, "OK", "State was correct");
165
166 # looking for the version
167 $found_version = 0;
168 for my $subcheck (@{$output->{'checks'}}) {
169 if ($subcheck->{'output'} =~ /nagiosplug.ssh.0.2 \(protocol version: 2.0\)/ ){
170 $found_version = 1;
171 }
172 }
173 cmp_ok($found_version, '==', 1, "Output OK");
122 close NC; 174 close NC;
123} 175}
diff --git a/plugins/t/check_swap.t b/plugins/t/check_swap.t
index 18780386..68946f6d 100644
--- a/plugins/t/check_swap.t
+++ b/plugins/t/check_swap.t
@@ -5,39 +5,47 @@
5# 5#
6 6
7use strict; 7use strict;
8use Test::More tests => 14; 8use warnings;
9use Test::More tests => 21;
9use NPTest; 10use NPTest;
10 11use JSON;
11my $successOutput = '/^SWAP OK - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/';
12my $failureOutput = '/^SWAP CRITICAL - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/';
13my $warnOutput = '/^SWAP WARNING - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/';
14 12
15my $result; 13my $result;
14my $outputFormat = '--output-format mp-test-json';
15my $output;
16my $message = '/^[0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/';
16 17
17$result = NPTest->testCmd( "./check_swap" ); # Always OK 18$result = NPTest->testCmd( "./check_swap $outputFormat" ); # Always OK
18cmp_ok( $result->return_code, "==", 0, "Always OK" ); 19cmp_ok( $result->return_code, "==", 0, "Always OK" );
19like( $result->output, $successOutput, "Right output" ); 20is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct");
21like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
20 22
21$result = NPTest->testCmd( "./check_swap -w 1048576 -c 1048576" ); # 1 MB free 23$result = NPTest->testCmd( "./check_swap -w 1048576 -c 1048576 $outputFormat" ); # 1 MB free
22cmp_ok( $result->return_code, "==", 0, "At least 1MB free" ); 24cmp_ok( $result->return_code, "==", 0, "Always OK" );
23like( $result->output, $successOutput, "Right output" ); 25is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct");
26like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
24 27
25$result = NPTest->testCmd( "./check_swap -w 1% -c 1%" ); # 1% free 28$result = NPTest->testCmd( "./check_swap -w 1% -c 1% $outputFormat" ); # 1% free
26cmp_ok( $result->return_code, "==", 0, 'At least 1% free' ); 29cmp_ok( $result->return_code, "==", 0, "Always OK" );
27like( $result->output, $successOutput, "Right output" ); 30is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct");
31like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
28 32
29$result = NPTest->testCmd( "./check_swap -w 100% -c 100%" ); # 100% (always critical) 33$result = NPTest->testCmd( "./check_swap -w 100% -c 100% $outputFormat" ); # 100% (always critical)
30cmp_ok( $result->return_code, "==", 2, 'Get critical because not 100% free' ); 34cmp_ok( $result->return_code, "==", 0, "Always OK" );
31like( $result->output, $failureOutput, "Right output" ); 35is($result->{'mp_test_result'}->{'state'}, "CRITICAL", "State was correct");
36like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
32 37
33$result = NPTest->testCmd( "./check_swap -w 100% -c 1%" ); # 100% (always warn) 38$result = NPTest->testCmd( "./check_swap -w 100% -c 1% $outputFormat" ); # 100% (always warn)
34cmp_ok( $result->return_code, "==", 1, 'Get warning because not 100% free' ); 39cmp_ok( $result->return_code, "==", 0, "Always OK" );
35like( $result->output, $warnOutput, "Right output" ); 40is($result->{'mp_test_result'}->{'state'}, "WARNING", "State was correct");
41like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
36 42
37$result = NPTest->testCmd( "./check_swap -w 100%" ); # 100% (single threshold, always warn) 43$result = NPTest->testCmd( "./check_swap -w 100% $outputFormat" ); # 100% (single threshold, always warn)
38cmp_ok( $result->return_code, "==", 1, 'Get warning because not 100% free' ); 44cmp_ok( $result->return_code, "==", 0, "Always OK" );
39like( $result->output, $warnOutput, "Right output" ); 45is($result->{'mp_test_result'}->{'state'}, "WARNING", "State was correct");
46like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
40 47
41$result = NPTest->testCmd( "./check_swap -c 100%" ); # 100% (single threshold, always critical) 48$result = NPTest->testCmd( "./check_swap -c 100% $outputFormat" ); # 100% (single threshold, always critical)
42cmp_ok( $result->return_code, "==", 2, 'Get critical because not 100% free' ); 49cmp_ok( $result->return_code, "==", 0, "Always OK" );
43like( $result->output, $failureOutput, "Right output" ); 50is($result->{'mp_test_result'}->{'state'}, "CRITICAL", "State was correct");
51like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
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/plugins/t/check_users.t b/plugins/t/check_users.t
index 21c3e53d..446e0476 100644
--- a/plugins/t/check_users.t
+++ b/plugins/t/check_users.t
@@ -15,8 +15,8 @@ use NPTest;
15use vars qw($tests); 15use vars qw($tests);
16BEGIN {$tests = 12; plan tests => $tests} 16BEGIN {$tests = 12; plan tests => $tests}
17 17
18my $successOutput = '/^USERS OK - [0-9]+ users currently logged in/'; 18my $successOutput = '/[0-9]+ users currently logged in/';
19my $failureOutput = '/^USERS CRITICAL - [0-9]+ users currently logged in/'; 19my $failureOutput = '/[0-9]+ users currently logged in/';
20my $wrongOptionOutput = '/Usage:/'; 20my $wrongOptionOutput = '/Usage:/';
21 21
22my $t; 22my $t;
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
index eaa9f518..d0a866cb 100755
--- a/plugins/tests/check_curl.t
+++ b/plugins/tests/check_curl.t
@@ -15,14 +15,20 @@
15# Email Address []:devel@monitoring-plugins.org 15# Email Address []:devel@monitoring-plugins.org
16 16
17use strict; 17use strict;
18use warnings;
18use Test::More; 19use Test::More;
19use NPTest; 20use NPTest;
20use FindBin qw($Bin); 21use FindBin qw($Bin);
21 22
23use URI;
24use URI::QueryParam;
25use HTTP::Daemon;
26use HTTP::Daemon::SSL;
27
22$ENV{'LC_TIME'} = "C"; 28$ENV{'LC_TIME'} = "C";
23 29
24my $common_tests = 75; 30my $common_tests = 111;
25my $ssl_only_tests = 8; 31my $ssl_only_tests = 12;
26# Check that all dependent modules are available 32# Check that all dependent modules are available
27eval "use HTTP::Daemon 6.01;"; 33eval "use HTTP::Daemon 6.01;";
28plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@; 34plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@;
@@ -35,7 +41,7 @@ my $plugin = 'check_http';
35$plugin = 'check_curl' if $0 =~ m/check_curl/mx; 41$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
36 42
37# look for libcurl version to see if some advanced checks are possible (>= 7.49.0) 43# look for libcurl version to see if some advanced checks are possible (>= 7.49.0)
38my $advanced_checks = 12; 44my $advanced_checks = 16;
39my $use_advanced_checks = 0; 45my $use_advanced_checks = 0;
40my $required_version = '7.49.0'; 46my $required_version = '7.49.0';
41my $virtual_host = 'www.somefunnyhost.com'; 47my $virtual_host = 'www.somefunnyhost.com';
@@ -185,6 +191,123 @@ sub run_server {
185 $c->send_response('moved to /redirect2'); 191 $c->send_response('moved to /redirect2');
186 } elsif ($r->url->path eq "/redir_timeout") { 192 } elsif ($r->url->path eq "/redir_timeout") {
187 $c->send_redirect( "/timeout" ); 193 $c->send_redirect( "/timeout" );
194 } elsif ($r->url->path =~ m{^/redirect_with_increment}) {
195 # <scheme>://<username>:<password>@<host>:<port>/<path>;<parameters>?<query>#<fragment>
196 # Find every parameter, query , and fragment keys and increment them
197
198 my $content = "";
199
200 # Use URI to help with query/fragment; parse path params manually.
201 my $original_url = $r->url->as_string;
202 $content .= " original_url: ${original_url}\n";
203 my $uri = URI->new($original_url);
204 $content .= " uri: ${uri}\n";
205
206 my $path = $uri->path // '';
207 my $query = $uri->query // '';
208 my $fragment = $uri->fragment // '';
209
210 $content .= " path: ${path}\n";
211 $content .= " query: ${query}\n";
212 $content .= " fragment: ${fragment}\n";
213
214 # split the URI part and parameters. URI package cannot do this
215 # group 1 is captured: anything without a semicolon: ([^;]*)
216 # group 2 is uncaptured: (?:;(.*))?
217 # (?: ... )? prevents capturing the parameter section
218 # inside group 2, ';' matches the first ever semicolon
219 # group3 is captured: any character string : (.*)
220 # \? matches an actual ? mark, which starts the query parameters
221 my ($before_params, $params) = $uri =~ m{^([^;]*)(?:;(.*))?\?};
222 $before_params //= '';
223 $params //= '';
224 $content .= " before_params: ${before_params}\n";
225 $content .= " params: ${params}\n";
226 my @parameter_pairs;
227 if (defined $params && length $params) {
228 for my $p (split /;/, $params) {
229 my ($key,$value) = split /=/, $p, 2;
230 $value //= '';
231 push @parameter_pairs, [ $key, $value ];
232 $content .= " parameter: ${key} -> ${value}\n";
233 }
234 }
235
236 # query parameters are offered directly from the library
237 my @query_form = $uri->query_form;
238 my @query_parameter_pairs;
239 while (@query_form) {
240 my $key = shift @query_form;
241 my $value = shift @query_form;
242 $value //= ''; # there can be valueless keys
243 push @query_parameter_pairs, [ $key, $value ];
244 $content .= " query: ${key} -> ${value}\n";
245 }
246
247 # helper to increment value
248 my $increment = sub {
249 my ($v) = @_;
250 return $v if !defined $v || $v eq '';
251 # numeric integer
252 if ($v =~ /^-?\d+$/) {
253 return $v + 1;
254 }
255 # otherwise -> increment as if its an ascii character
256 # sed replacement syntax, but the $& holds the matched character
257 if (length($v)) {
258 (my $new_v = $v) =~ s/./chr(ord($&) + 1)/ge;
259 return $new_v;
260 }
261 };
262
263 # increment values in pairs
264 for my $pair (@parameter_pairs) {
265 $pair->[1] = $increment->($pair->[1]);
266 $content .= " parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n";
267 }
268 for my $pair (@query_parameter_pairs) {
269 $pair->[1] = $increment->($pair->[1]);
270 $content .= " query parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n";
271 }
272
273 # rebuild strings
274 my $new_parameter_str = join(';', map { $_->[0] . '=' . $_->[1] } @parameter_pairs);
275 $content .= " new_parameter_str: ${new_parameter_str}\n";
276
277 # library can rebuild from an array
278 my @new_query_form;
279 for my $p (@query_parameter_pairs) { push @new_query_form, $p->[0], $p->[1] }
280
281 my $new_fragment_str = '';
282 for my $pair (@parameter_pairs) {
283 my $key = $pair->[0];
284 my $value = $pair->[1];
285 if ($key eq "fragment") {
286 $new_fragment_str = $value
287 }
288 }
289 $content .= " new_fragment_str: ${new_fragment_str}\n";
290
291 # construct new URI using the library
292 my $new_uri = URI->new('');
293 $new_uri->path( $before_params . ($new_parameter_str ? ';' . $new_parameter_str : '') );
294 $new_uri->query_form( \@new_query_form ) if @new_query_form;
295 $new_uri->fragment( $new_fragment_str ) if $new_fragment_str ne '';
296 $content .= " new_uri: ${new_uri}\n";
297
298 # Redirect until fail_count or redirect_count reaches 3
299 if ($new_uri =~ /fail_count=3/){
300 $c->send_error(HTTP::Status->RC_FORBIDDEN, "fail count reached 3, url path:" . $r->url->path );
301 } elsif ($new_uri =~ /redirect_count=3/){
302 $c->send_response(HTTP::Response->new( 200, 'OK', undef , $content ));
303 } elsif ($new_uri =~ /location_redirect_count=3/){
304 $c->send_basic_header(302);
305 $c->send_header("Location", "$new_uri" );
306 $c->send_crlf;
307 $c->send_response("$content \n moved to $new_uri");
308 } else {
309 $c->send_redirect( $new_uri->as_string, 301, $content );
310 }
188 } elsif ($r->url->path eq "/timeout") { 311 } elsif ($r->url->path eq "/timeout") {
189 # Keep $c from being destroyed, but prevent severe leaks 312 # Keep $c from being destroyed, but prevent severe leaks
190 unshift @persist, $c; 313 unshift @persist, $c;
@@ -214,7 +337,7 @@ sub run_server {
214 return($chunk); 337 return($chunk);
215 })); 338 }));
216 } else { 339 } else {
217 $c->send_error(HTTP::Status->RC_FORBIDDEN); 340 $c->send_error(HTTP::Status->RC_FORBIDDEN, "unknown url path:" . $r->url->path );
218 } 341 }
219 $c->close; 342 $c->close;
220 } 343 }
@@ -245,21 +368,21 @@ SKIP: {
245 368
246 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); 369 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" );
247 is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); 370 is( $result->return_code, 0, "$command -p $port_https -S -C 14" );
248 is( $result->output, "OK - Certificate 'Monitoring Plugins' will expire on $expiry.", "output ok" ); 371 like( $result->output, '/.*Certificate \'Monitoring Plugins\' will expire on ' . quotemeta($expiry) . '.*/', "output ok" );
249 372
250 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); 373 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" );
251 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); 374 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" );
252 like( $result->output, '/WARNING - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); 375 like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" );
253 376
254 # Expired cert tests 377 # Expired cert tests
255 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); 378 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" );
256 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" ); 379 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" );
257 like( $result->output, '/CRITICAL - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); 380 like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" );
258 381
259 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); 382 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" );
260 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" ); 383 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" );
261 is( $result->output, 384 like( $result->output,
262 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.', 385 '/.*Certificate \'Monitoring Plugins\' expired on Wed Jan\s+2 12:00:00 2008 \+0000.*/',
263 "output ok" ); 386 "output ok" );
264 387
265} 388}
@@ -274,19 +397,54 @@ SKIP: {
274 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$"; 397 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$";
275 $result = NPTest->testCmd( $cmd ); 398 $result = NPTest->testCmd( $cmd );
276 is( $result->return_code, 0, $cmd); 399 is( $result->return_code, 0, $cmd);
277 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 400 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
278 401
279 # http with virtual port (!= 80) 402 # http with virtual port (!= 80)
280 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$"; 403 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$";
281 $result = NPTest->testCmd( $cmd ); 404 $result = NPTest->testCmd( $cmd );
282 is( $result->return_code, 0, $cmd); 405 is( $result->return_code, 0, $cmd);
283 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 406 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
284 407
285 # http with virtual port (80) 408 # http with virtual port (80)
286 $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$"; 409 $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$";
287 $result = NPTest->testCmd( $cmd ); 410 $result = NPTest->testCmd( $cmd );
288 is( $result->return_code, 0, $cmd); 411 is( $result->return_code, 0, $cmd);
289 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 412 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
413
414 # curlopt proxy/noproxy parsing tests, ssl disabled
415 {
416 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
417
418 local $ENV{"http_proxy"} = 'http://proxy.example.com:8080';
419 $cmd = "$command -u /statuscode/200 -v";
420 $result = NPTest->testCmd( $cmd );
421 like( $result->output, '/.*CURLOPT_PROXY: http://proxy.example.com:8080 */', "Correctly took 'http_proxy' environment variable: ".$result->output );
422 delete($ENV{"http_proxy"});
423
424 local $ENV{"http_proxy"} = 'http://taken.proxy.example:8080';
425 local $ENV{"HTTP_PROXY"} = 'http://discarded.proxy.example:8080';
426 $cmd = "$command -u /statuscode/200 -v";
427 $result = NPTest->testCmd( $cmd );
428 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Correctly took 'http_proxy' environment variable over 'HTTP_PROXY': ".$result->output );
429 delete(local $ENV{"http_proxy"});
430 delete(local $ENV{"HTTP_PROXY"});
431
432 local $ENV{"http_proxy"} = 'http://discarded1.proxy.example:8080';
433 local $ENV{"HTTP_PROXY"} = 'http://discarded2.proxy.example:8080';
434 $cmd = "$command -u /statuscode/200 -x 'http://taken.proxy.example:8080' -v";
435 $result = NPTest->testCmd( $cmd );
436 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Argument -x overwrote 'http_proxy' and 'HTTP_PROXY' environment variables: ".$result->output );
437 delete(local $ENV{"http_proxy"});
438 delete(local $ENV{"HTTP_PROXY"});
439
440 local $ENV{"http_proxy"} = 'http://discarded1.proxy.example:8080';
441 local $ENV{"HTTP_PROXY"} = 'http://discarded2.proxy.example:8080';
442 $cmd = "$command -u /statuscode/200 --proxy 'http://taken.example.com:8080' -v";
443 $result = NPTest->testCmd( $cmd );
444 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument --proxy overwrote 'http_proxy' and 'HTTP_PROXY' environment variables: ".$result->output );
445 delete(local $ENV{"http_proxy"});
446 delete(local $ENV{"HTTP_PROXY"});
447 }
290} 448}
291 449
292# and the same for SSL 450# and the same for SSL
@@ -296,19 +454,54 @@ SKIP: {
296 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$"; 454 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$";
297 $result = NPTest->testCmd( $cmd ); 455 $result = NPTest->testCmd( $cmd );
298 is( $result->return_code, 0, $cmd); 456 is( $result->return_code, 0, $cmd);
299 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 457 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
300 458
301 # https with virtual port (!= 443) 459 # https with virtual port (!= 443)
302 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$"; 460 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$";
303 $result = NPTest->testCmd( $cmd ); 461 $result = NPTest->testCmd( $cmd );
304 is( $result->return_code, 0, $cmd); 462 is( $result->return_code, 0, $cmd);
305 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 463 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
306 464
307 # https with virtual port (443) 465 # https with virtual port (443)
308 $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$"; 466 $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$";
309 $result = NPTest->testCmd( $cmd ); 467 $result = NPTest->testCmd( $cmd );
310 is( $result->return_code, 0, $cmd); 468 is( $result->return_code, 0, $cmd);
311 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 469 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
470
471 # curlopt proxy/noproxy parsing tests, ssl enabled
472 {
473 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
474
475 local $ENV{"https_proxy"} = 'http://proxy.example.com:8080';
476 $cmd = "$command -u /statuscode/200 --ssl -v";
477 $result = NPTest->testCmd( $cmd );
478 like( $result->output, '/.*CURLOPT_PROXY: http://proxy.example.com:8080 */', "Correctly took 'https_proxy' environment variable: ".$result->output );
479 delete($ENV{"https_proxy"});
480
481 local $ENV{"https_proxy"} = 'http://taken.proxy.example:8080';
482 local $ENV{"HTTPS_PROXY"} = 'http://discarded.proxy.example:8080';
483 $cmd = "$command -u /statuscode/200 --ssl -v";
484 $result = NPTest->testCmd( $cmd );
485 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Correctly took 'https_proxy' environment variable over 'HTTPS_PROXY': ".$result->output );
486 delete(local $ENV{"https_proxy"});
487 delete(local $ENV{"HTTPS_PROXY"});
488
489 local $ENV{"https_proxy"} = 'http://discarded1.proxy.example:8080';
490 local $ENV{"HTTPS_PROXY"} = 'http://discarded2.proxy.example:8080';
491 $cmd = "$command -u /statuscode/200 --ssl -x 'http://taken.example.com:8080' -v";
492 $result = NPTest->testCmd( $cmd );
493 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument -x overwrote environment variables 'https_proxy' and 'HTTPS_PROXY': ".$result->output );
494 delete(local $ENV{"https_proxy"});
495 delete(local $ENV{"HTTPS_PROXY"});
496
497 local $ENV{"https_proxy"} = 'http://discarded1.proxy.example:8080';
498 local $ENV{"HTTPS_PROXY"} = 'http://discarded2.proxy.example:8080';
499 $cmd = "$command -u /statuscode/200 --ssl --proxy 'http://taken.example.com:8080' -v";
500 $result = NPTest->testCmd( $cmd );
501 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument --proxy overwrote environment variables 'https_proxy' and 'HTTPS_PROXY': ".$result->output );
502 delete(local $ENV{"https_proxy"});
503 delete(local $ENV{"HTTPS_PROXY"});
504 }
312} 505}
313 506
314 507
@@ -321,165 +514,223 @@ sub run_common_tests {
321 514
322 $result = NPTest->testCmd( "$command -u /file/root" ); 515 $result = NPTest->testCmd( "$command -u /file/root" );
323 is( $result->return_code, 0, "/file/root"); 516 is( $result->return_code, 0, "/file/root");
324 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); 517 like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" );
325 518
326 $result = NPTest->testCmd( "$command -u /file/root -s Root" ); 519 $result = NPTest->testCmd( "$command -u /file/root -s Root" );
327 is( $result->return_code, 0, "/file/root search for string"); 520 is( $result->return_code, 0, "/file/root search for string");
328 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); 521 like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" );
329 522
330 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" ); 523 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" );
331 is( $result->return_code, 2, "Missing string check"); 524 is( $result->return_code, 2, "Missing string check");
332 like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); 525 like( $result->output, qr%string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
333 526
334 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" ); 527 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" );
335 is( $result->return_code, 2, "Missing string check"); 528 is( $result->return_code, 2, "Missing string check");
336 like( $result->output, qr%HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); 529 like( $result->output, qr%string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
337 530
338 $result = NPTest->testCmd( "$command -u /header_check -d foo" ); 531 $result = NPTest->testCmd( "$command -u /header_check -d foo" );
339 is( $result->return_code, 0, "header_check search for string"); 532 is( $result->return_code, 0, "header_check search for string");
340 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second/', "Output correct" ); 533 like( $result->output, '/.*HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second.*/', "Output correct" );
341 534
342 $result = NPTest->testCmd( "$command -u /header_check -d bar" ); 535 $result = NPTest->testCmd( "$command -u /header_check -d bar" );
343 is( $result->return_code, 2, "Missing header string check"); 536 is( $result->return_code, 2, "Missing header string check");
344 like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location"); 537 like( $result->output, qr%header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location");
345 538
346 $result = NPTest->testCmd( "$command -u /header_broken_check" ); 539 $result = NPTest->testCmd( "$command -u /header_broken_check" );
347 is( $result->return_code, 0, "header_check search for string"); 540 is( $result->return_code, 0, "header_check search for string");
348 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second/', "Output correct" ); 541 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct" );
349 542
350 my $cmd; 543 my $cmd;
351 $cmd = "$command -u /slow"; 544 $cmd = "$command -u /slow";
352 $result = NPTest->testCmd( $cmd ); 545 $result = NPTest->testCmd( $cmd );
353 is( $result->return_code, 0, "$cmd"); 546 is( $result->return_code, 0, "$cmd");
354 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 547 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
355 $result->output =~ /in ([\d\.]+) second/; 548 $result->output =~ /in ([\d\.]+) second/;
356 cmp_ok( $1, ">", 1, "Time is > 1 second" ); 549 cmp_ok( $1, ">", 1, "Time is > 1 second" );
357 550
358 $cmd = "$command -u /statuscode/200"; 551 $cmd = "$command -u /statuscode/200";
359 $result = NPTest->testCmd( $cmd ); 552 $result = NPTest->testCmd( $cmd );
360 is( $result->return_code, 0, $cmd); 553 is( $result->return_code, 0, $cmd);
361 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 554 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
362 555
363 $cmd = "$command -u /statuscode/200 -e 200"; 556 $cmd = "$command -u /statuscode/200 -e 200";
364 $result = NPTest->testCmd( $cmd ); 557 $result = NPTest->testCmd( $cmd );
365 is( $result->return_code, 0, $cmd); 558 is( $result->return_code, 0, $cmd);
366 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 559 like( $result->output, '/.*Status line output matched "200".*/', "Output correct: ".$result->output );
367 560
368 $cmd = "$command -u /statuscode/201"; 561 $cmd = "$command -u /statuscode/201";
369 $result = NPTest->testCmd( $cmd ); 562 $result = NPTest->testCmd( $cmd );
370 is( $result->return_code, 0, $cmd); 563 is( $result->return_code, 0, $cmd);
371 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); 564 like( $result->output, '/.*HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
372 565
373 $cmd = "$command -u /statuscode/201 -e 201"; 566 $cmd = "$command -u /statuscode/201 -e 201";
374 $result = NPTest->testCmd( $cmd ); 567 $result = NPTest->testCmd( $cmd );
375 is( $result->return_code, 0, $cmd); 568 is( $result->return_code, 0, $cmd);
376 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "201" - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); 569 like( $result->output, '/.*Status line output matched "201".*/', "Output correct: ".$result->output );
377 570
378 $cmd = "$command -u /statuscode/201 -e 200"; 571 $cmd = "$command -u /statuscode/201 -e 200";
379 $result = NPTest->testCmd( $cmd ); 572 $result = NPTest->testCmd( $cmd );
380 is( $result->return_code, 2, $cmd); 573 is( $result->return_code, 2, $cmd);
381 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created/', "Output correct: ".$result->output ); 574 like( $result->output, '/.*Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created.*/', "Output correct: ".$result->output );
382 575
383 $cmd = "$command -u /statuscode/200 -e 200,201,202"; 576 $cmd = "$command -u /statuscode/200 -e 200,201,202";
384 $result = NPTest->testCmd( $cmd ); 577 $result = NPTest->testCmd( $cmd );
385 is( $result->return_code, 0, $cmd); 578 is( $result->return_code, 0, $cmd);
386 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 579 like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output );
387 580
388 $cmd = "$command -u /statuscode/201 -e 200,201,202"; 581 $cmd = "$command -u /statuscode/201 -e 200,201,202";
389 $result = NPTest->testCmd( $cmd ); 582 $result = NPTest->testCmd( $cmd );
390 is( $result->return_code, 0, $cmd); 583 is( $result->return_code, 0, $cmd);
391 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 584 like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output );
392 585
393 $cmd = "$command -u /statuscode/203 -e 200,201,202"; 586 $cmd = "$command -u /statuscode/203 -e 200,201,202";
394 $result = NPTest->testCmd( $cmd ); 587 $result = NPTest->testCmd( $cmd );
395 is( $result->return_code, 2, $cmd); 588 is( $result->return_code, 2, $cmd);
396 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information/', "Output correct: ".$result->output ); 589 like( $result->output, '/.*Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information.*/', "Output correct: ".$result->output );
397 590
398 $cmd = "$command -j HEAD -u /method"; 591 $cmd = "$command -j HEAD -u /method";
399 $result = NPTest->testCmd( $cmd ); 592 $result = NPTest->testCmd( $cmd );
400 is( $result->return_code, 0, $cmd); 593 is( $result->return_code, 0, $cmd);
401 like( $result->output, '/^HTTP OK: HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 594 like( $result->output, '/.*HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
402 595
403 $cmd = "$command -j POST -u /method"; 596 $cmd = "$command -j POST -u /method";
404 $result = NPTest->testCmd( $cmd ); 597 $result = NPTest->testCmd( $cmd );
405 is( $result->return_code, 0, $cmd); 598 is( $result->return_code, 0, $cmd);
406 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 599 like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
407 600
408 $cmd = "$command -j GET -u /method"; 601 $cmd = "$command -j GET -u /method";
409 $result = NPTest->testCmd( $cmd ); 602 $result = NPTest->testCmd( $cmd );
410 is( $result->return_code, 0, $cmd); 603 is( $result->return_code, 0, $cmd);
411 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 604 like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
412 605
413 $cmd = "$command -u /method"; 606 $cmd = "$command -u /method";
414 $result = NPTest->testCmd( $cmd ); 607 $result = NPTest->testCmd( $cmd );
415 is( $result->return_code, 0, $cmd); 608 is( $result->return_code, 0, $cmd);
416 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 609 like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
417 610
418 $cmd = "$command -P foo -u /method"; 611 $cmd = "$command -P foo -u /method";
419 $result = NPTest->testCmd( $cmd ); 612 $result = NPTest->testCmd( $cmd );
420 is( $result->return_code, 0, $cmd); 613 is( $result->return_code, 0, $cmd);
421 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 614 like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
422 615
423 $cmd = "$command -j DELETE -u /method"; 616 $cmd = "$command -j DELETE -u /method";
424 $result = NPTest->testCmd( $cmd ); 617 $result = NPTest->testCmd( $cmd );
425 is( $result->return_code, 1, $cmd); 618 is( $result->return_code, 1, $cmd);
426 like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output ); 619 like( $result->output, '/.*HTTP/1.1 405 Method Not Allowed.*/', "Output correct: ".$result->output );
427 620
428 $cmd = "$command -j foo -u /method"; 621 $cmd = "$command -j foo -u /method";
429 $result = NPTest->testCmd( $cmd ); 622 $result = NPTest->testCmd( $cmd );
430 is( $result->return_code, 2, $cmd); 623 is( $result->return_code, 2, $cmd);
431 like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output ); 624 like( $result->output, '/.*HTTP/1.1 501 Not Implemented.*/', "Output correct: ".$result->output );
432 625
433 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude"; 626 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude";
434 $result = NPTest->testCmd( $cmd ); 627 $result = NPTest->testCmd( $cmd );
435 is( $result->return_code, 0, $cmd); 628 is( $result->return_code, 0, $cmd);
436 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 629 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
437 630
438 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude"; 631 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude";
439 $result = NPTest->testCmd( $cmd ); 632 $result = NPTest->testCmd( $cmd );
440 is( $result->return_code, 0, $cmd); 633 is( $result->return_code, 0, $cmd);
441 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 634 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
442 635
443 # To confirm that the free doesn't segfault 636 # To confirm that the free doesn't segfault
444 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude"; 637 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude";
445 $result = NPTest->testCmd( $cmd ); 638 $result = NPTest->testCmd( $cmd );
446 is( $result->return_code, 0, $cmd); 639 is( $result->return_code, 0, $cmd);
447 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 640 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
448 641
449 $cmd = "$command -u /redirect"; 642 $cmd = "$command -u /redirect";
450 $result = NPTest->testCmd( $cmd ); 643 $result = NPTest->testCmd( $cmd );
451 is( $result->return_code, 0, $cmd); 644 is( $result->return_code, 0, $cmd);
452 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 645 like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
453 646
454 $cmd = "$command -f follow -u /redirect"; 647 $cmd = "$command -f follow -u /redirect";
455 $result = NPTest->testCmd( $cmd ); 648 $result = NPTest->testCmd( $cmd );
456 is( $result->return_code, 0, $cmd); 649 is( $result->return_code, 0, $cmd);
457 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 650 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
458 651
459 $cmd = "$command -u /redirect -k 'follow: me'"; 652 $cmd = "$command -u /redirect -k 'follow: me'";
460 $result = NPTest->testCmd( $cmd ); 653 $result = NPTest->testCmd( $cmd );
461 is( $result->return_code, 0, $cmd); 654 is( $result->return_code, 0, $cmd);
462 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 655 like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
463 656
464 $cmd = "$command -f follow -u /redirect -k 'follow: me'"; 657 $cmd = "$command -f follow -u /redirect -k 'follow: me'";
465 $result = NPTest->testCmd( $cmd ); 658 $result = NPTest->testCmd( $cmd );
466 is( $result->return_code, 0, $cmd); 659 is( $result->return_code, 0, $cmd);
467 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 660 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
468 661
469 $cmd = "$command -f sticky -u /redirect -k 'follow: me'"; 662 $cmd = "$command -f sticky -u /redirect -k 'follow: me'";
470 $result = NPTest->testCmd( $cmd ); 663 $result = NPTest->testCmd( $cmd );
471 is( $result->return_code, 0, $cmd); 664 is( $result->return_code, 0, $cmd);
472 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 665 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
473 666
474 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'"; 667 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'";
475 $result = NPTest->testCmd( $cmd ); 668 $result = NPTest->testCmd( $cmd );
476 is( $result->return_code, 0, $cmd); 669 is( $result->return_code, 0, $cmd);
477 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 670 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
478 671
479 $cmd = "$command -f follow -u /redirect_rel -s redirected"; 672 $cmd = "$command -f follow -u /redirect_rel -s redirected";
480 $result = NPTest->testCmd( $cmd ); 673 $result = NPTest->testCmd( $cmd );
481 is( $result->return_code, 0, $cmd); 674 is( $result->return_code, 0, $cmd);
482 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 675 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
676
677 # Redirect with increment tests. These are for checking if the url parameters, query parameters and fragment are parsed.
678 # The server at this point has dynamic redirection. It tries to increment values that it sees in these fields, then redirects.
679 # It also appends some debug log and writes it into HTTP content, pass the -vvv parameter to see them.
680
681 $cmd = "$command -u '/redirect_with_increment/path1/path2/path3/path4' --onredirect=follow -vvv";
682 $result = NPTest->testCmd( "$cmd" );
683 is( $result->return_code, 1, $cmd);
684 like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count was not present, got redirected to / : ".$result->output );
685
686 # redirect_count=0 is parsed as a parameter and incremented. When it goes up to 3, the redirection returns HTTP OK
687 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
688 $result = NPTest->testCmd( "$cmd" );
689 is( $result->return_code, 0, $cmd);
690 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count went up to 3, and returned OK: ".$result->output );
691
692 # location_redirect_count=0 goes up to 3, which uses the HTTP 302 style of redirection with 'Location' header
693 $cmd = "$command -u '/redirect_with_increment/path1/path2;location_redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
694 $result = NPTest->testCmd( "$cmd" );
695 is( $result->return_code, 0, $cmd);
696 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
697
698 # fail_count parameter may also go up to 3, which returns a HTTP 403
699 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;fail_count=2' --onredirect=follow -vvv";
700 $result = NPTest->testCmd( "$cmd" );
701 is( $result->return_code, 1, $cmd);
702 like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, early due to fail_count reaching 3: ".$result->output );
703
704 # redirect_count=0, p1=1 , p2=ab => redirect_count=1, p1=2 , p2=bc => redirect_count=2, p1=3 , p2=cd => redirect_count=3 , p1=4 , p2=de
705 # Last visited URI returns HTTP OK instead of redirect, and the one before that contains the new_uri in its content
706 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
707 $result = NPTest->testCmd( "$cmd" );
708 is( $result->return_code, 0, $cmd);
709 like( $result->output, '/.*redirect_count=3;p1=4;p2=de\?*/', "Output correct, parsed and incremented both parameters p1 and p2 : ".$result->output );
710 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
711
712 # Same incrementation as before, uses the query parameters that come after the first '?' : qp1 and qp2
713 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
714 $result = NPTest->testCmd( "$cmd" );
715 is( $result->return_code, 0, $cmd);
716 like( $result->output, '/.*\?qp1=13&qp2=no*/', "Output correct, parsed and incremented both query parameters qp1 and qp2 : ".$result->output );
717 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
718
719 # Check if the query parameter order is kept intact
720 $cmd = "$command -u '/redirect_with_increment;redirect_count=0;?qp0=0&qp1=1&qp2=2&qp3=3&qp4=4&qp5=5' --onredirect=follow -vvv";
721 $result = NPTest->testCmd( "$cmd" );
722 is( $result->return_code, 0, $cmd);
723 like( $result->output, '/.*\?qp0=3&qp1=4&qp2=5&qp3=6&qp4=7&qp5=8*/', "Output correct, parsed and incremented query parameters qp1,qp2,qp3,qp4,qp5 in order : ".$result->output );
724 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
725
726 # The fragment is passed as another parameter.
727 # During the server redirects the fragment will be set to its value, if such a key is present.
728 # 'ebiil' => 'fcjjm' => 'gdkkn' => 'hello'
729 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;fragment=ebiil?qp1=0' --onredirect=follow -vvv";
730 $result = NPTest->testCmd( "$cmd" );
731 is( $result->return_code, 0, $cmd);
732 like( $result->output, '/.*redirect_count=3;fragment=hello\?qp1=3#hello*/', "Output correct, fragments are specified by server and followed by check_curl: ".$result->output );
733 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
483 734
484 # These tests may block 735 # These tests may block
485 # stickyport - on full urlS port is set back to 80 otherwise 736 # stickyport - on full urlS port is set back to 80 otherwise
@@ -531,4 +782,63 @@ sub run_common_tests {
531 $result = NPTest->testCmd( $cmd, 5 ); 782 $result = NPTest->testCmd( $cmd, 5 );
532 }; 783 };
533 is( $@, "", $cmd ); 784 is( $@, "", $cmd );
785
786 # curlopt proxy/noproxy parsing tests
787 {
788 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
789
790 local $ENV{"no_proxy"} = 'internal.acme.org';
791 $cmd = "$command -u /statuscode/200 -v";
792 $result = NPTest->testCmd( $cmd );
793 like( $result->output, '/.* curl CURLOPT_NOPROXY: internal.acme.org */', "Correctly took 'no_proxy' environment variable: ".$result->output );
794 delete($ENV{"no_proxy"});
795
796 local $ENV{"no_proxy"} = 'taken.acme.org';
797 local $ENV{"NO_PROXY"} = 'discarded.acme.org';
798 $cmd = "$command -u /statuscode/200 -v";
799 $result = NPTest->testCmd( $cmd );
800 is( $result->return_code, 0, $cmd);
801 like( $result->output, '/.*CURLOPT_NOPROXY: taken.acme.org*/', "Correctly took 'no_proxy' environment variable over 'NO_PROXY': ".$result->output );
802 delete(local $ENV{"no_proxy"});
803 delete(local $ENV{"NO_PROXY"});
804
805 local $ENV{"no_proxy"} = 'taken.acme.org';
806 local $ENV{"NO_PROXY"} = 'discarded.acme.org';
807 $cmd = "$command -u /statuscode/200 --noproxy 'taken.acme.org' -v";
808 $result = NPTest->testCmd( $cmd );
809 is( $result->return_code, 0, $cmd);
810 like( $result->output, '/.*CURLOPT_NOPROXY: taken.acme.org*/', "Argument --noproxy overwrote environment variables 'no_proxy' and 'NO_PROXY': ".$result->output );
811 delete(local $ENV{"no_proxy"});
812 delete(local $ENV{"NO_PROXY"});
813
814 $cmd = "$command -u /statuscode/200 --noproxy 'internal1.acme.org,internal2.acme.org,internal3.acme.org' -v";
815 $result = NPTest->testCmd( $cmd );
816 is( $result->return_code, 0, $cmd);
817 like( $result->output, '/.*CURLOPT_NOPROXY: internal1.acme.org,internal2.acme.org,internal3.acme.org*/', "Argument --noproxy read multiple noproxy domains: ".$result->output );
818
819 $cmd = "$command -u /statuscode/200 --noproxy '10.11.12.13,256.256.256.256,0.0.0.0,192.156.0.0/22,10.0.0.0/4' -v";
820 $result = NPTest->testCmd( $cmd );
821 is( $result->return_code, 0, $cmd);
822 like( $result->output, '/.*CURLOPT_NOPROXY: 10.11.12.13,256.256.256.256,0.0.0.0,192.156.0.0/22,10.0.0.0/4*/', "Argument --noproxy took multiple noproxy domains: ".$result->output );
823
824 $cmd = "$command -u /statuscode/200 --noproxy '0123:4567:89AB:CDEF:0123:4567:89AB:CDEF,0123::CDEF,0123:4567/96,[::1],::1,[1234::5678:ABCD/4]' -v";
825 $result = NPTest->testCmd( $cmd );
826 is( $result->return_code, 0, $cmd);
827 like( $result->output, '/.*CURLOPT_NOPROXY: 0123:4567:89AB:CDEF:0123:4567:89AB:CDEF,0123::CDEF,0123:4567\/96,\[::1\],::1,\[1234::5678:ABCD\/4\].*/', "Argument --noproxy took multiple noproxy domains: ".$result->output );
828
829 $cmd = "$command -u /statuscode/200 --noproxy '300.400.500.600,1.2.3,XYZD:0123::,1:2:3:4:5:6:7,1::2::3,1.1.1.1/64,::/256' -v";
830 $result = NPTest->testCmd( $cmd );
831 is( $result->return_code, 0, $cmd);
832
833 $cmd = "$command -u /statuscode/200 --proxy http://proxy.example.com:8080 --noproxy '*' -v";
834 $result = NPTest->testCmd( $cmd );
835 is( $result->return_code, 0, $cmd);
836 like( $result->output, '/.*have local name resolution: true.*/', "Proxy will not be used due to '*' in noproxy: ".$result->output );
837
838 $cmd = "$command -u /statuscode/200 --proxy http://proxy.example.com:8080 --noproxy '127.0.0.1' -v";
839 $result = NPTest->testCmd( $cmd );
840 is( $result->return_code, 0, $cmd);
841 like( $result->output, '/.*have local name resolution: true.*/', "Proxy will not be used due to '127.0.0.1' in noproxy: ".$result->output );
842 }
843
534} 844}
diff --git a/plugins/tests/check_nt.t b/plugins/tests/check_nt.t
deleted file mode 100755
index 223d4933..00000000
--- a/plugins/tests/check_nt.t
+++ /dev/null
@@ -1,80 +0,0 @@
1#! /usr/bin/perl -w -I ..
2#
3# Test check_nt by having a stub check_nt daemon
4#
5
6use strict;
7use Test::More;
8use NPTest;
9use FindBin qw($Bin);
10
11use IO::Socket;
12use IO::Select;
13use POSIX;
14
15my $port = 50000 + int(rand(1000));
16
17my $pid = fork();
18if ($pid) {
19 # Parent
20 #print "parent\n";
21 # give our webserver some time to startup
22 sleep(1);
23} else {
24 # Child
25 #print "child\n";
26
27 my $server = IO::Socket::INET->new(
28 LocalPort => $port,
29 Type => SOCK_STREAM,
30 Reuse => 1,
31 Proto => "tcp",
32 Listen => 10,
33 ) or die "Cannot be a tcp server on port $port: $@";
34
35 $server->autoflush(1);
36
37 print "Please contact me at port $port\n";
38 while (my $client = $server->accept ) {
39 my $data = "";
40 my $rv = $client->recv($data, POSIX::BUFSIZ, 0);
41
42 my ($password, $command, $arg) = split('&', $data);
43
44 if ($command eq "4") {
45 if ($arg eq "c") {
46 print $client "930000000&1000000000";
47 } elsif ($arg eq "d") {
48 print $client "UNKNOWN: Drive is not a fixed drive";
49 }
50 }
51 }
52 exit;
53}
54
55END { if ($pid) { print "Killing $pid\n"; kill "INT", $pid } };
56
57if ($ARGV[0] && $ARGV[0] eq "-d") {
58 sleep 1000;
59}
60
61if (-x "./check_nt") {
62 plan tests => 5;
63} else {
64 plan skip_all => "No check_nt compiled";
65}
66
67my $result;
68my $command = "./check_nt -H 127.0.0.1 -p $port";
69
70$result = NPTest->testCmd( "$command -v USEDDISKSPACE -l c" );
71is( $result->return_code, 0, "USEDDISKSPACE c");
72is( $result->output, q{c:\ - total: 0.93 Gb - used: 0.07 Gb (7%) - free 0.87 Gb (93%) | 'c:\ Used Space'=0.07Gb;0.00;0.00;0.00;0.93}, "Output right" );
73
74$result = NPTest->testCmd( "$command -v USEDDISKSPACE -l d" );
75is( $result->return_code, 3, "USEDDISKSPACE d - invalid");
76is( $result->output, "Free disk space : Invalid drive", "Output right" );
77
78$result = NPTest->testCmd( "./check_nt -v USEDDISKSPACE -l d" );
79is( $result->return_code, 3, "Fail if -H missing");
80
diff --git a/plugins/tests/check_snmp.t b/plugins/tests/check_snmp.t
index bfe42e16..26d67898 100755
--- a/plugins/tests/check_snmp.t
+++ b/plugins/tests/check_snmp.t
@@ -4,12 +4,13 @@
4# 4#
5 5
6use strict; 6use strict;
7use warnings;
7use Test::More; 8use Test::More;
8use NPTest; 9use NPTest;
9use FindBin qw($Bin); 10use FindBin qw($Bin);
10use POSIX qw/strftime/; 11use POSIX qw/strftime/;
11 12
12my $tests = 81; 13my $tests = 75;
13# Check that all dependent modules are available 14# Check that all dependent modules are available
14eval { 15eval {
15 require NetSNMP::OID; 16 require NetSNMP::OID;
@@ -76,49 +77,36 @@ my $res;
76 77
77$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0"); 78$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0");
78cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" ); 79cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" );
79like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); 80like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines");
80like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software |
81.1.3.6.1.4.1.8072.3.2.67.0:
82"Cisco Internetwork Operating System Software
83IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version
8412.2(20)EWA, RELEASE SOFTWARE (fc1)
85Technical Support: http://www.cisco.com/techsupport
86Copyright (c) 1986-2004 by cisco Systems, Inc.
87"').'/m', "String contains all lines");
88 81
89# sysContact.0 is "Alice" (from our snmpd.conf) 82# sysContact.0 is "Alice" (from our snmpd.conf)
90$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0 -o sysContact.0 -o .1.3.6.1.4.1.8072.3.2.67.1"); 83$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0 -o sysContact.0 -o .1.3.6.1.4.1.8072.3.2.67.1");
91cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); 84cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
92like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); 85# like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
93like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software ').'"?Alice"?'.quotemeta(' Kisco Outernetwork Oserating Gystem Totware | 86like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines with multiple OIDs");
94.1.3.6.1.4.1.8072.3.2.67.0: 87like($res->output, '/.*Alice.*/m', "String contains all lines with multiple OIDs");
95"Cisco Internetwork Operating System Software 88like($res->output, '/.*Kisco Outernetwork Oserating Gystem Totware.*/m', "String contains all lines with multiple OIDs");
96IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version
9712.2(20)EWA, RELEASE SOFTWARE (fc1)
98Technical Support: http://www.cisco.com/techsupport
99Copyright (c) 1986-2004 by cisco Systems, Inc.
100"
101.1.3.6.1.4.1.8072.3.2.67.1:
102"Kisco Outernetwork Oserating Gystem Totware
103Copyleft (c) 2400-2689 by kisco Systrems, Inc."').'/m', "String contains all lines with multiple OIDs");
104 89
105$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.2"); 90$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.2");
106like($res->output, '/'.quotemeta('SNMP OK - This should not confuse check_snmp \"parser\" | 91cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
107.1.3.6.1.4.1.8072.3.2.67.2: 92# like($res->output, '/'.quotemeta('This should not confuse check_snmp \"parser\" |
108"This should not confuse check_snmp \"parser\" 93# .1.3.6.1.4.1.8072.3.2.67.2:
109into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); 94# "This should not confuse check_snmp \"parser\"
95# into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1");
110 96
111$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.3"); 97$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.3");
112like($res->output, '/'.quotemeta('SNMP OK - It\'s getting even harder if the line | 98cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
113.1.3.6.1.4.1.8072.3.2.67.3: 99# like($res->output, '/'.quotemeta('It\'s getting even harder if the line |
114"It\'s getting even harder if the line 100# .1.3.6.1.4.1.8072.3.2.67.3:
115ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); 101# "It\'s getting even harder if the line
102# ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2");
116 103
117$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.4"); 104$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.4");
118like($res->output, '/'.quotemeta('SNMP OK - And now have fun with with this: \"C:\\\\\" | 105cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
119.1.3.6.1.4.1.8072.3.2.67.4: 106# like($res->output, '/'.quotemeta('And now have fun with with this: \"C:\\\\\" |
120"And now have fun with with this: \"C:\\\\\" 107# .1.3.6.1.4.1.8072.3.2.67.4:
121because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); 108# "And now have fun with with this: \"C:\\\\\"
109# because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3");
122 110
123system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*"); 111system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*");
124 112
@@ -131,156 +119,159 @@ SKIP: {
131 my $ts = time(); 119 my $ts = time();
132 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); 120 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" );
133 is($res->return_code, 0, "Returns OK"); 121 is($res->return_code, 0, "Returns OK");
134 is($res->output, "No previous data to calculate rate - assume okay"); 122 like($res->output, "/.*No previous data to calculate rate - assume okay.*/");
135 123
136 # test rate 1 second later 124 # test rate 1 second later
137 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); 125 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" );
138 is($res->return_code, 1, "WARNING - due to going above rate calculation" ); 126 is($res->return_code, 1, "WARNING - due to going above rate calculation" );
139 is($res->output, "SNMP RATE WARNING - *666* | iso.3.6.1.4.1.8072.3.2.67.10=666;600 "); 127 like($res->output, "/.*=666.*/");
140 128
141 # test rate with same time 129 # test rate with same time
142 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); 130 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" );
143 is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" ); 131 is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" );
144 is($res->output, "Time duration between plugin calls is invalid"); 132 like($res->output, "/.*Time duration between plugin calls is invalid.*/");
145 133
146 134
147 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); 135 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" );
148 is($res->return_code, 0, "OK for first call" ); 136 is($res->return_code, 0, "OK for first call" );
149 is($res->output, "No previous data to calculate rate - assume okay" ); 137 like($res->output, "/.*No previous data to calculate rate - assume okay.*/" );
150 138
151 # test rate 1 second later 139 # test rate 1 second later
152 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); 140 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" );
153 is($res->return_code, 0, "OK as no thresholds" ); 141 is($res->return_code, 0, "OK as no thresholds" );
154 is($res->output, "SNMP RATE OK - inoctets 666 | inoctets=666 ", "Check label"); 142 like($res->output, "/.*inoctets.*=666.*/m", "Check label");
155 143
156 # test rate 3 seconds later 144 # test rate 3 seconds later
157 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+3))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); 145 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+3))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" );
158 is($res->return_code, 0, "OK as no thresholds" ); 146 is($res->return_code, 0, "OK as no thresholds" );
159 is($res->output, "SNMP RATE OK - inoctets 333 | inoctets=333 ", "Check rate decreases due to longer interval"); 147 like($res->output, "/.*inoctets.*333.*/", "Check rate decreases due to longer interval");
160 148
161 149
162 # label performance data check 150 # label performance data check
163 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test" ); 151 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test" );
164 is($res->return_code, 0, "OK as no thresholds" ); 152 is($res->return_code, 0, "OK as no thresholds" );
165 is($res->output, "SNMP OK - test 67996 | test=67996c ", "Check label"); 153 like($res->output, "/.*test.?=67996c/", "Check label");
166 154
167 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" ); 155 # following test is deactivated since it was not valid due to the guidelines (no single quote in label allowed)
168 is($res->return_code, 0, "OK as no thresholds" ); 156 # $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" );
169 is($res->output, "SNMP OK - test'test 68662 | \"test'test\"=68662c ", "Check label"); 157 # is($res->return_code, 0, "OK as no thresholds" );
158 # is($res->output, "test'test 68662 | \"test'test\"=68662c ", "Check label");
170 159
171 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test\"test'" ); 160 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test\"test'" );
172 is($res->return_code, 0, "OK as no thresholds" ); 161 is($res->return_code, 0, "OK as no thresholds" );
173 is($res->output, "SNMP OK - test\"test 69328 | 'test\"test'=69328c ", "Check label"); 162 like($res->output, "/.*'test\"test'=68662c.*/", "Check label");
174 163
175 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test -O" ); 164 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test -O" );
176 is($res->return_code, 0, "OK as no thresholds" ); 165 is($res->return_code, 0, "OK as no thresholds" );
177 is($res->output, "SNMP OK - test 69994 | iso.3.6.1.4.1.8072.3.2.67.10=69994c ", "Check label"); 166 like($res->output, "/.*.67.10.?=69328c.*/", "Check label");
178 167
179 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10" ); 168 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10" );
180 is($res->return_code, 0, "OK as no thresholds" ); 169 is($res->return_code, 0, "OK as no thresholds" );
181 is($res->output, "SNMP OK - 70660 | iso.3.6.1.4.1.8072.3.2.67.10=70660c ", "Check label"); 170 like($res->output, "/.*8072.3.2.67.10.?=69994c.*/", "Check label");
182 171
183 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test test'" ); 172 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test test'" );
184 is($res->return_code, 0, "OK as no thresholds" ); 173 is($res->return_code, 0, "OK as no thresholds" );
185 is($res->output, "SNMP OK - test test 71326 | 'test test'=71326c ", "Check label"); 174 like($res->output, "/.*'test test'=70660c/", "Check label");
186 175
187 176
188 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); 177 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" );
189 is($res->return_code, 0, "OK for first call" ); 178 is($res->return_code, 0, "OK for first call" );
190 is($res->output, "No previous data to calculate rate - assume okay" ); 179 like($res->output, "/.*No previous data to calculate rate - assume okay.*/" );
191 180
192 # test 1 second later 181 # test 1 second later
193 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); 182 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" );
194 is($res->return_code, 0, "OK as no thresholds" ); 183 is($res->return_code, 0, "OK as no thresholds" );
195 is($res->output, "SNMP RATE OK - inoctets_per_minute 39960 | inoctets_per_minute=39960 ", "Checking multiplier"); 184 like($res->output, "/.*inoctets_per_minute.*=39960/", "Checking multiplier");
196}; 185};
197 186
198 187
199 188
200$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s '\"stringtests\"'" ); 189$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s 'stringtests'" );
201is($res->return_code, 0, "OK as string matches" ); 190is($res->return_code, 0, "OK as string matches" );
202is($res->output, 'SNMP OK - "stringtests" | ', "Good string match" ); 191like($res->output, '/.*stringtests.*/', "Good string match" );
203 192
204$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s ring" ); 193$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s ring" );
205is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" ); 194is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" );
206is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Failed string match" ); 195like($res->output, '/.*stringtests.*/', "Failed string match" );
207 196
208$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s '\"stringtests\"'" ); 197$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s 'stringtests'" );
209is($res->return_code, 2, "CRITICAL as string matches but inverted" ); 198is($res->return_code, 2, "CRITICAL as string matches but inverted" );
210is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Inverted string match" ); 199like($res->output, '/.*"stringtests".*/', "Inverted string match" );
211 200
212$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s ring" ); 201$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s ring" );
213is($res->return_code, 0, "OK as string doesn't match but inverted" ); 202is($res->return_code, 0, "OK as string doesn't match but inverted" );
214is($res->output, 'SNMP OK - "stringtests" | ', "OK as inverted string no match" ); 203like($res->output, '/.*"stringtests".*/', "OK as inverted string no match" );
215 204
216$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.12 -w 4:5" ); 205$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.12 -w 4:5" );
217is($res->return_code, 1, "Numeric in string test" ); 206# a string is a string and not a number
218is($res->output, 'SNMP WARNING - *3.5* | iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5 ', "WARNING threshold checks for string masquerading as number" ); 207is($res->return_code, 0, "Numeric in string test" );
208like($res->output, '/.*3.5.*| iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5/', "WARNING threshold checks for string masquerading as number" );
219 209
220$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.13" ); 210$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.13" );
221is($res->return_code, 0, "Not really numeric test" ); 211is($res->return_code, 0, "Not really numeric test" );
222is($res->output, 'SNMP OK - "87.4startswithnumberbutshouldbestring" | ', "Check string with numeric start is still string" ); 212like($res->output, '/.*"87.4startswithnumberbutshouldbestring".*/', "Check string with numeric start is still string" );
223 213
224$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.14" ); 214$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.14" );
225is($res->return_code, 0, "Not really numeric test (trying best to fool it)" ); 215is($res->return_code, 0, "Not really numeric test (trying best to fool it)" );
226is($res->output, 'SNMP OK - "555\"I said\"" | ', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" ); 216like($res->output, '/.*\'555"I said"\'.*/', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" );
227 217
228$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.15 -r 'CUSTOM CHECK OK'" ); 218$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.15 -r 'CUSTOM CHECK OK'" );
229is($res->return_code, 0, "String check should check whole string, not a parsed number" ); 219is($res->return_code, 0, "String check should check whole string, not a parsed number" );
230is($res->output, 'SNMP OK - "CUSTOM CHECK OK: foo is 12345" | ', "String check with numbers returns whole string"); 220like($res->output, '/.*CUSTOM CHECK OK: foo is 12345.*/', "String check with numbers returns whole string");
231 221
232$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); 222$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" );
233is($res->return_code, 0, "Negative integer check OK" ); 223is($res->return_code, 0, "Negative integer check OK" );
234is($res->output, 'SNMP OK - -2 | iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3: ', "Negative integer check OK output" ); 224like($res->output, '/.*-2.*| iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3:/', "Negative integer check OK output" );
235 225
236$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); 226$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" );
237is($res->return_code, 1, "Negative integer check WARNING" ); 227is($res->return_code, 1, "Negative integer check WARNING" );
238is($res->output, 'SNMP WARNING - *-3* | iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3: ', "Negative integer check WARNING output" ); 228like($res->output, '/.*-3.*| iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3:/', "Negative integer check WARNING output" );
239 229
240$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); 230$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" );
241is($res->return_code, 2, "Negative integer check CRITICAL" ); 231is($res->return_code, 2, "Negative integer check CRITICAL" );
242is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3: ', "Negative integer check CRITICAL output" ); 232like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3:/', "Negative integer check CRITICAL output" );
243 233
244$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -3: -c -6:" ); 234$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -3: -c -6:" );
245is($res->return_code, 1, "Negative integer as string, WARNING" ); 235is($res->return_code, 1, "Negative integer as string, WARNING" );
246is($res->output, 'SNMP WARNING - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6: ', "Negative integer as string, WARNING output" ); 236like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6:/', "Negative integer as string, WARNING output" );
247 237
248$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -2: -c -3:" ); 238$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -2: -c -3:" );
249is($res->return_code, 2, "Negative integer as string, CRITICAL" ); 239is($res->return_code, 2, "Negative integer as string, CRITICAL" );
250is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3: ', "Negative integer as string, CRITICAL output" ); 240like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3:/', "Negative integer as string, CRITICAL output" );
251 241
252$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" ); 242# deactivated since the perl agent api of snmpd really does not allow floats
253is($res->return_code, 0, "Negative float OK" ); 243# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" );
254is($res->output, 'SNMP OK - -6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" ); 244# is($res->return_code, 0, "Negative float OK" );
245# is($res->output, '-6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" );
255 246
256$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" ); 247# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" );
257is($res->return_code, 1, "Negative float WARNING" ); 248# is($res->return_code, 1, "Negative float WARNING" );
258is($res->output, 'SNMP WARNING - *-6.6* | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;@-6.65:~;@-6.55:~ ', "Negative float WARNING output" ); 249# like($res->output, '/-6.6.*| .*67.18=-6.6;@-6.65:~;@-6.55:~/', "Negative float WARNING output" );
259 250
260$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-10:20' -c '2:200000,-20:30'" ); 251$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-10:20' -c '2:200000,-20:30'" );
261is($res->return_code, 0, "Multiple OIDs with thresholds" ); 252is($res->return_code, 0, "Multiple OIDs with thresholds" );
262like($res->output, '/SNMP OK - \d+ -4 | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); 253like($res->output, '/-4.*| .*67.10=\d+c;1:100000;2:200000 .*67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" );
263 254
264$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-1:2' -c '2:200000,-20:30'" ); 255$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-1:2' -c '2:200000,-20:30'" );
265is($res->return_code, 1, "Multiple OIDs with thresholds" ); 256is($res->return_code, 1, "Multiple OIDs with thresholds" );
266like($res->output, '/SNMP WARNING - \d+ \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); 257like($res->output, '/-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" );
267 258
268$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1" ); 259$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1 -O" );
269is($res->return_code, 2, "Multiple OIDs with some thresholds" ); 260is($res->return_code, 2, "Multiple OIDs with some thresholds" );
270like($res->output, '/SNMP CRITICAL - \*\d+\* \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" ); 261like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" );
271 262
272$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19"); 263$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19");
273is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" ); 264is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" );
274is($res->output,'SNMP OK - 42 | iso.3.6.1.4.1.8072.3.2.67.19=42 ', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" ); 265like($res->output,'/.*42.*| iso.3.6.1.4.1.8072.3.2.67.19=42/', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" );
275 266
276$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 -M .1"); 267$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 -M .1");
277is($res->return_code, 0, "Test multiply RC" ); 268is($res->return_code, 0, "Test multiply RC" );
278is($res->output,'SNMP OK - 4.200000 | iso.3.6.1.4.1.8072.3.2.67.19=4.200000 ' , "Test multiply .1 output" ); 269like($res->output,'/.*4.200000.*| iso.3.6.1.4.1.8072.3.2.67.19=4.200000/' , "Test multiply .1 output" );
279 270
280$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' "); 271$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1");
281is($res->return_code, 0, "Test multiply RC + format" ); 272is($res->return_code, 0, "Test multiply RC" );
282is($res->output, 'SNMP OK - 4.20 | iso.3.6.1.4.1.8072.3.2.67.19=4.20 ', "Test multiply .1 output + format" ); 273like($res->output, '/.*4.20.*| iso.3.6.1.4.1.8072.3.2.67.19=4.20/', "Test multiply .1 output" );
283 274
284$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' -w 1"); 275$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -w 1");
285is($res->return_code, 1, "Test multiply RC + format + thresholds" ); 276is($res->return_code, 1, "Test multiply RC + thresholds" );
286is($res->output, 'SNMP WARNING - *4.20* | iso.3.6.1.4.1.8072.3.2.67.19=4.20;1 ', "Test multiply .1 output + format + thresholds" ); 277like($res->output, '/.*4.20.* | iso.3.6.1.4.1.8072.3.2.67.19=4.20+;1/', "Test multiply .1 output + thresholds" );
diff --git a/plugins/tests/check_snmp_agent.pl b/plugins/tests/check_snmp_agent.pl
index 38912e98..608b6f92 100644
--- a/plugins/tests/check_snmp_agent.pl
+++ b/plugins/tests/check_snmp_agent.pl
@@ -4,9 +4,10 @@
4# 4#
5 5
6#use strict; # Doesn't work 6#use strict; # Doesn't work
7use warnings;
7use NetSNMP::OID qw(:all); 8use NetSNMP::OID qw(:all);
8use NetSNMP::agent; 9use NetSNMP::agent;
9use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64); 10use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64 ASN_FLOAT);
10#use Math::Int64 qw(uint64); # Skip that module while we don't need it 11#use Math::Int64 qw(uint64); # Skip that module while we don't need it
11sub uint64 { return $_ } 12sub uint64 { return $_ }
12 13
@@ -22,21 +23,82 @@ IOS (tm) Catalyst 4000 "L3" Switch Software (cat4000-I9K91S-M), Version
22Technical Support: http://www.cisco.com/techsupport 23Technical Support: http://www.cisco.com/techsupport
23Copyright (c) 1986-2004 by cisco Systems, Inc. 24Copyright (c) 1986-2004 by cisco Systems, Inc.
24'; 25';
25my $multilin2 = "Kisco Outernetwork Oserating Gystem Totware 26my $multiline2 = "Kisco Outernetwork Oserating Gystem Totware
26Copyleft (c) 2400-2689 by kisco Systrems, Inc."; 27Copyleft (c) 2400-2689 by kisco Systrems, Inc.";
27my $multilin3 = 'This should not confuse check_snmp "parser" 28my $multiline3 = 'This should not confuse check_snmp "parser"
28into thinking there is no 2nd line'; 29into thinking there is no 2nd line';
29my $multilin4 = 'It\'s getting even harder if the line 30my $multiline4 = 'It\'s getting even harder if the line
30ends with with this: C:\\'; 31ends with with this: C:\\';
31my $multilin5 = 'And now have fun with with this: "C:\\" 32my $multiline5 = 'And now have fun with with this: "C:\\"
32because we\'re not done yet!'; 33because we\'re not done yet!';
33 34
34# Next are arrays of indexes (Type, initial value and increments) 35# Next are arrays of indexes (Type, initial value and increments)
35# 0..19 <---- please update comment when adding/removing fields 36# 0..19 <---- please update comment when adding/removing fields
36my @fields = (ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_UNSIGNED, ASN_UNSIGNED, ASN_COUNTER, ASN_COUNTER64, ASN_UNSIGNED, ASN_COUNTER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER ); 37my @fields = (ASN_OCTET_STR, # 0
37my @values = ($multiline, $multilin2, $multilin3, $multilin4, $multilin5, 4294965296, 1000, 4294965296, uint64("18446744073709351616"), int(rand(2**32)), 64000, "stringtests", "3.5", "87.4startswithnumberbutshouldbestring", '555"I said"', 'CUSTOM CHECK OK: foo is 12345', -2, '-4', '-6.6', 42 ); 38 ASN_OCTET_STR, # 1
39 ASN_OCTET_STR, # 2
40 ASN_OCTET_STR, # 3
41 ASN_OCTET_STR, # 4
42 ASN_UNSIGNED, # 5
43 ASN_UNSIGNED, # 6
44 ASN_COUNTER, # 7
45 ASN_COUNTER64, # 8
46 ASN_UNSIGNED, # 9
47 ASN_COUNTER, # 10
48 ASN_OCTET_STR, # 11
49 ASN_OCTET_STR, # 12
50 ASN_OCTET_STR, # 13
51 ASN_OCTET_STR, # 14
52 ASN_OCTET_STR, # 15
53 ASN_INTEGER, # 16
54 ASN_INTEGER, # 17
55 ASN_FLOAT, # 18
56 ASN_INTEGER # 19
57 );
58my @values = ($multiline, # 0
59 $multiline2, # 1
60 $multiline3, # 2
61 $multiline4, # 3
62 $multiline5, # 4
63 4294965296, # 5
64 1000, # 6
65 4294965296, # 7
66 uint64("18446744073709351616"), # 8
67 int(rand(2**32)), # 9
68 64000, # 10
69 "stringtests", # 11
70 "3.5", # 12
71 "87.4startswithnumberbutshouldbestring", # 13
72 '555"I said"', # 14
73 'CUSTOM CHECK OK: foo is 12345', # 15
74 '-2', # 16
75 '-4', # 17
76 '-6.6', # 18
77 42 # 19
78 );
38# undef increments are randomized 79# undef increments are randomized
39my @incrts = (undef, undef, undef, undef, undef, 1000, -500, 1000, 100000, undef, 666, undef, undef, undef, undef, undef, -1, undef, undef, 0 ); 80my @incrts = (
81 undef, # 0
82 undef, # 1
83 undef, # 2
84 undef, # 3
85 undef, # 4
86 1000, # 5
87 -500, # 6
88 1000, # 7
89 100000, # 8
90 undef, # 9
91 666, # 10
92 undef, # 11
93 undef, # 12
94 undef, # 13
95 undef, # 14
96 undef, # 15
97 -1, # 16
98 0, # 17
99 undef, # 18
100 0 # 19
101 );
40 102
41# Number of elements in our OID 103# Number of elements in our OID
42my $oidelts; 104my $oidelts;
diff --git a/plugins/tests/conf/snmpd.conf b/plugins/tests/conf/snmpd.conf
index eff5b0b3..1724c027 100644
--- a/plugins/tests/conf/snmpd.conf
+++ b/plugins/tests/conf/snmpd.conf
@@ -19,5 +19,5 @@ syscontact Alice
19# Embedded Subagents 19# Embedded Subagents
20############################################################################### 20###############################################################################
21 21
22perl do "tests/check_snmp_agent.pl"; 22perl do "./tests/check_snmp_agent.pl";
23 23
diff --git a/plugins/tests/test_check_disk.c b/plugins/tests/test_check_disk.c
new file mode 100644
index 00000000..32b46c2d
--- /dev/null
+++ b/plugins/tests/test_check_disk.c
@@ -0,0 +1,213 @@
1/*****************************************************************************
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 *
17 *****************************************************************************/
18
19#include "common.h"
20#include "../check_disk.d/utils_disk.h"
21#include "../../tap/tap.h"
22#include "regex.h"
23
24void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags,
25 int expect, char *desc);
26
27int main(int argc, char **argv) {
28 plan_tests(35);
29
30 struct name_list *exclude_filesystem = NULL;
31 ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list");
32 np_add_name(&exclude_filesystem, "/var/log");
33 ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now");
34 ok(np_find_name(exclude_filesystem, "/home") == false, "/home not in list");
35 np_add_name(&exclude_filesystem, "/home");
36 ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now");
37 ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list");
38
39 struct name_list *exclude_fstype = NULL;
40 ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list");
41 np_add_name(&exclude_fstype, "iso9660");
42 ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now");
43
44 ok(np_find_name(exclude_filesystem, "iso9660") == false, "Make sure no clashing in variables");
45
46 /*
47 for (temp_name = exclude_filesystem; temp_name; temp_name = temp_name->next) {
48 printf("Name: %s\n", temp_name->name);
49 }
50 */
51
52 struct mount_entry *dummy_mount_list;
53 struct mount_entry **mtail = &dummy_mount_list;
54 struct mount_entry *me = (struct mount_entry *)malloc(sizeof *me);
55 me->me_devname = strdup("/dev/c0t0d0s0");
56 me->me_mountdir = strdup("/");
57 *mtail = me;
58 mtail = &me->me_next;
59
60 me = (struct mount_entry *)malloc(sizeof *me);
61 me->me_devname = strdup("/dev/c1t0d1s0");
62 me->me_mountdir = strdup("/var");
63 *mtail = me;
64 mtail = &me->me_next;
65
66 me = (struct mount_entry *)malloc(sizeof *me);
67 me->me_devname = strdup("/dev/c2t0d0s0");
68 me->me_mountdir = strdup("/home");
69 *mtail = me;
70 mtail = &me->me_next;
71
72 int cflags = REG_NOSUB | REG_EXTENDED;
73 np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a"));
74 np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3,
75 strdup("regex on dev names:"));
76 np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0,
77 strdup("regex on non existent dev/path:"));
78 np_test_mount_entry_regex(dummy_mount_list, strdup("/Foo"), cflags | REG_ICASE, 0,
79 strdup("regi on non existent dev/path:"));
80 np_test_mount_entry_regex(dummy_mount_list, strdup("/c.t0"), cflags, 3,
81 strdup("partial devname regex match:"));
82 np_test_mount_entry_regex(dummy_mount_list, strdup("c0t0"), cflags, 1,
83 strdup("partial devname regex match:"));
84 np_test_mount_entry_regex(dummy_mount_list, strdup("C0t0"), cflags | REG_ICASE, 1,
85 strdup("partial devname regi match:"));
86 np_test_mount_entry_regex(dummy_mount_list, strdup("home"), cflags, 1,
87 strdup("partial pathname regex match:"));
88 np_test_mount_entry_regex(dummy_mount_list, strdup("hOme"), cflags | REG_ICASE, 1,
89 strdup("partial pathname regi match:"));
90 np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2,
91 strdup("grouped regex pathname match:"));
92 np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2,
93 strdup("grouped regi pathname match:"));
94
95 filesystem_list test_paths = filesystem_list_init();
96 mp_int_fs_list_append(&test_paths, "/home/groups");
97 mp_int_fs_list_append(&test_paths, "/var");
98 mp_int_fs_list_append(&test_paths, "/tmp");
99 mp_int_fs_list_append(&test_paths, "/home/tonvoon");
100 mp_int_fs_list_append(&test_paths, "/dev/c2t0d0s0");
101 ok(test_paths.length == 5, "List counter works correctly with appends");
102
103 mp_int_fs_list_set_best_match(test_paths, dummy_mount_list, false);
104 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
105 struct mount_entry *temp_me;
106 temp_me = p->best_match;
107 if (!strcmp(p->name, "/home/groups")) {
108 ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"),
109 "/home/groups got right best match: /home");
110 } else if (!strcmp(p->name, "/var")) {
111 ok(temp_me && !strcmp(temp_me->me_mountdir, "/var"), "/var got right best match: /var");
112 } else if (!strcmp(p->name, "/tmp")) {
113 ok(temp_me && !strcmp(temp_me->me_mountdir, "/"), "/tmp got right best match: /");
114 } else if (!strcmp(p->name, "/home/tonvoon")) {
115 ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"),
116 "/home/tonvoon got right best match: /home");
117 } else if (!strcmp(p->name, "/dev/c2t0d0s0")) {
118 ok(temp_me && !strcmp(temp_me->me_devname, "/dev/c2t0d0s0"),
119 "/dev/c2t0d0s0 got right best match: /dev/c2t0d0s0");
120 }
121 }
122
123 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
124 mp_int_fs_list_del(&test_paths, p);
125 }
126 ok(test_paths.length == 0, "List delete sets counter properly");
127
128 mp_int_fs_list_append(&test_paths, "/home/groups");
129 mp_int_fs_list_append(&test_paths, "/var");
130 mp_int_fs_list_append(&test_paths, "/tmp");
131 mp_int_fs_list_append(&test_paths, "/home/tonvoon");
132 mp_int_fs_list_append(&test_paths, "/home");
133
134 mp_int_fs_list_set_best_match(test_paths, dummy_mount_list, true);
135 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
136 if (!strcmp(p->name, "/home/groups")) {
137 ok(!p->best_match, "/home/groups correctly not found");
138 } else if (!strcmp(p->name, "/var")) {
139 ok(p->best_match, "/var found");
140 } else if (!strcmp(p->name, "/tmp")) {
141 ok(!p->best_match, "/tmp correctly not found");
142 } else if (!strcmp(p->name, "/home/tonvoon")) {
143 ok(!p->best_match, "/home/tonvoon not found");
144 } else if (!strcmp(p->name, "/home")) {
145 ok(p->best_match, "/home found");
146 }
147 }
148
149 bool found = false;
150 /* test deleting first element in paths */
151 mp_int_fs_list_del(&test_paths, NULL);
152 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
153 if (!strcmp(p->name, "/home/groups")) {
154 found = true;
155 }
156 }
157 ok(!found, "first element successfully deleted");
158 found = false;
159
160 parameter_list_elem *prev = NULL;
161 parameter_list_elem *p = NULL;
162 for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) {
163 if (!strcmp(path->name, "/tmp")) {
164 mp_int_fs_list_del(&test_paths, path);
165 }
166 p = path;
167 }
168
169 parameter_list_elem *last = NULL;
170 for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) {
171 if (!strcmp(path->name, "/tmp")) {
172 found = true;
173 }
174 if (path->next) {
175 prev = path;
176 } else {
177 last = path;
178 }
179 }
180 ok(!found, "/tmp element successfully deleted");
181
182 int count = 0;
183 mp_int_fs_list_del(&test_paths, p);
184 for (p = test_paths.first; p; p = p->next) {
185 if (!strcmp(p->name, "/home")) {
186 found = true;
187 }
188 last = p;
189 count++;
190 }
191 ok(!found, "last (/home) element successfully deleted");
192 ok(count == 2, "two elements remaining");
193
194 return exit_status();
195}
196
197void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags,
198 int expect, char *desc) {
199 regex_t regex;
200 if (regcomp(&regex, regstr, cflags) == 0) {
201 int matches = 0;
202 for (struct mount_entry *me = dummy_mount_list; me; me = me->me_next) {
203 if (np_regex_match_mount_entry(me, &regex)) {
204 matches++;
205 }
206 }
207 ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect,
208 matches);
209
210 } else {
211 ok(false, "regex '%s' not compilable", regstr);
212 }
213}
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/tests/test_check_snmp.c b/plugins/tests/test_check_snmp.c
new file mode 100644
index 00000000..d71706d0
--- /dev/null
+++ b/plugins/tests/test_check_snmp.c
@@ -0,0 +1,175 @@
1/*****************************************************************************
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 *
17 *****************************************************************************/
18
19#include "tap.h"
20#include "../../config.h"
21
22#include <unistd.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25
26#include "utils_base.c"
27#include "../check_snmp.d/check_snmp_helpers.h"
28
29char *_np_state_generate_key(int argc, char **argv);
30char *_np_state_calculate_location_prefix(void);
31
32int main(int argc, char **argv) {
33 char *temp_string = (char *)_np_state_generate_key(argc, argv);
34 ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"),
35 "Got hash with exe and no parameters") ||
36 diag("You are probably running in wrong directory. Must run as ./test_utils");
37
38 int fake_argc = 4;
39 char *fake_argv[] = {
40 "./test_utils",
41 "here",
42 "--and",
43 "now",
44 };
45 temp_string = (char *)_np_state_generate_key(fake_argc, fake_argv);
46 ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"),
47 "Got based on expected argv");
48
49 unsetenv("MP_STATE_PATH");
50 temp_string = (char *)_np_state_calculate_location_prefix();
51 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory");
52
53 setenv("MP_STATE_PATH", "", 1);
54 temp_string = (char *)_np_state_calculate_location_prefix();
55 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string");
56
57 setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1);
58 temp_string = (char *)_np_state_calculate_location_prefix();
59 ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory");
60
61 fake_argc = 1;
62 fake_argv[0] = "./test_utils";
63 state_key temp_state_key1 = np_enable_state(NULL, 51, "check_test", fake_argc, fake_argv);
64 ok(!strcmp(temp_state_key1.plugin_name, "check_test"), "Got plugin name");
65 ok(!strcmp(temp_state_key1.name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"),
66 "Got generated filename");
67
68 state_key temp_state_key2 =
69 np_enable_state("allowedchars_in_keyname", 77, "check_snmp", fake_argc, fake_argv);
70
71 char state_path[1024];
72 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname",
73 (unsigned long)geteuid());
74 ok(!strcmp(temp_state_key2.plugin_name, "check_test"), "Got plugin name");
75 ok(!strcmp(temp_state_key2.name, "allowedchars_in_keyname"), "Got key name with valid chars");
76 ok(!strcmp(temp_state_key2._filename, state_path), "Got internal filename");
77
78 /* Don't do this test just yet. Will die */
79 /*
80 np_enable_state("bad^chars$in@here", 77);
81 temp_state_key = this_monitoring_plugin->state;
82 ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced"
83 );
84 */
85
86 state_key temp_state_key3 =
87 np_enable_state("funnykeyname", 54, "check_snmp", fake_argc, fake_argv);
88 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname",
89 (unsigned long)geteuid());
90 ok(!strcmp(temp_state_key3.plugin_name, "check_test"), "Got plugin name");
91 ok(!strcmp(temp_state_key3.name, "funnykeyname"), "Got key name");
92
93 ok(!strcmp(temp_state_key3._filename, state_path), "Got internal filename");
94 ok(temp_state_key3.data_version == 54, "Version set");
95
96 state_data *temp_state_data = np_state_read(temp_state_key3);
97 ok(temp_state_data == NULL, "Got no state data as file does not exist");
98
99 /*
100 temp_fp = fopen("var/statefile", "r");
101 if (temp_fp==NULL)
102 printf("Error opening. errno=%d\n", errno);
103 printf("temp_fp=%s\n", temp_fp);
104 ok( _np_state_read_file(temp_fp) == true, "Can read state file" );
105 fclose(temp_fp);
106 */
107
108 temp_state_key3._filename = "var/statefile";
109 temp_state_data = np_state_read(temp_state_key3);
110 ok(temp_state_data != NULL, "Got state data now") ||
111 diag("Are you running in right directory? Will get coredump next if not");
112 ok(temp_state_data->time == 1234567890, "Got time");
113 ok(!strcmp((char *)temp_state_data->data, "String to read"), "Data as expected");
114
115 temp_state_key3.data_version = 53;
116 temp_state_data = np_state_read(temp_state_key3);
117 ok(temp_state_data == NULL, "Older data version gives NULL");
118 temp_state_key3.data_version = 54;
119
120 temp_state_key3._filename = "var/nonexistent";
121 temp_state_data = np_state_read(temp_state_key3);
122 ok(temp_state_data == NULL, "Missing file gives NULL");
123
124 temp_state_key3._filename = "var/oldformat";
125 temp_state_data = np_state_read(temp_state_key3);
126 ok(temp_state_data == NULL, "Old file format gives NULL");
127
128 temp_state_key3._filename = "var/baddate";
129 temp_state_data = np_state_read(temp_state_key3);
130 ok(temp_state_data == NULL, "Bad date gives NULL");
131
132 temp_state_key3._filename = "var/missingdataline";
133 temp_state_data = np_state_read(temp_state_key3);
134 ok(temp_state_data == NULL, "Missing data line gives NULL");
135
136 unlink("var/generated");
137 temp_state_key3._filename = "var/generated";
138
139 time_t current_time = 1234567890;
140 np_state_write_string(temp_state_key3, current_time, "String to read");
141 ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected");
142
143 unlink("var/generated_directory/statefile");
144 unlink("var/generated_directory");
145 temp_state_key3._filename = "var/generated_directory/statefile";
146 current_time = 1234567890;
147 np_state_write_string(temp_state_key3, current_time, "String to read");
148 ok(system("cmp var/generated_directory/statefile var/statefile") == 0,
149 "Have created directory");
150
151 /* This test to check cannot write to dir - can't automate yet */
152 /*
153 unlink("var/generated_bad_dir");
154 mkdir("var/generated_bad_dir", S_IRUSR);
155 np_state_write_string(current_time, "String to read");
156 */
157
158 temp_state_key3._filename = "var/generated";
159 time(&current_time);
160 np_state_write_string(temp_state_key3, 0, "String to read");
161 temp_state_data = np_state_read(temp_state_key3);
162 /* Check time is set to current_time */
163 ok(system("cmp var/generated var/statefile > /dev/null") != 0,
164 "Generated file should be different this time");
165 ok(temp_state_data->time - current_time <= 1, "Has time generated from current time");
166
167 /* Don't know how to automatically test this. Need to be able to redefine die and catch the
168 * error */
169 /*
170 temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write";
171 np_state_write_string(0, "Bad file");
172 */
173
174 np_cleanup();
175}
diff --git a/plugins/tests/test_check_snmp.t b/plugins/tests/test_check_snmp.t
new file mode 100755
index 00000000..967633e9
--- /dev/null
+++ b/plugins/tests/test_check_snmp.t
@@ -0,0 +1,6 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_check_snmp") {
4 plan skip_all => "./test_check_snmp not compiled - please enable libtap library to test";
5}
6exec "./test_check_snmp";
diff --git a/plugins/tests/test_check_swap.c b/plugins/tests/test_check_swap.c
new file mode 100644
index 00000000..94d56ce7
--- /dev/null
+++ b/plugins/tests/test_check_swap.c
@@ -0,0 +1,21 @@
1
2#include "../check_swap.d/check_swap.h"
3#include "../../tap/tap.h"
4
5int verbose = 0;
6
7void print_usage(void) {}
8void print_help(swap_config config) { (void)config; }
9
10const char *progname = "test_check_swap";
11
12int main(void) {
13 swap_result test_data = getSwapFromProcMeminfo("./var/proc_meminfo");
14
15 plan_tests(4);
16
17 ok(test_data.errorcode == 0, "Test whether we manage to retrieve swap data");
18 ok(test_data.metrics.total == 34233905152, "Is the total Swap correct");
19 ok(test_data.metrics.free == 34233905152, "Is the free Swap correct");
20 ok(test_data.metrics.used == 0, "Is the used Swap correct");
21}
diff --git a/plugins/tests/test_check_swap.t b/plugins/tests/test_check_swap.t
new file mode 100755
index 00000000..826fae01
--- /dev/null
+++ b/plugins/tests/test_check_swap.t
@@ -0,0 +1,6 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_check_swap") {
4 plan skip_all => "./test_check_swap not compiled - please enable libtap library to test";
5}
6exec "./test_check_swap";
diff --git a/plugins/tests/var/proc_meminfo b/plugins/tests/var/proc_meminfo
new file mode 100644
index 00000000..6c5a618d
--- /dev/null
+++ b/plugins/tests/var/proc_meminfo
@@ -0,0 +1,55 @@
1MemTotal: 32767776 kB
2MemFree: 1693508 kB
3MemAvailable: 23807480 kB
4Buffers: 438456 kB
5Cached: 19124976 kB
6SwapCached: 0 kB
7Active: 7860680 kB
8Inactive: 18886776 kB
9Active(anon): 6108756 kB
10Inactive(anon): 1364500 kB
11Active(file): 1751924 kB
12Inactive(file): 17522276 kB
13Unevictable: 8548 kB
14Mlocked: 8548 kB
15SwapTotal: 33431548 kB
16SwapFree: 33431548 kB
17Zswap: 0 kB
18Zswapped: 0 kB
19Dirty: 784 kB
20Writeback: 0 kB
21AnonPages: 7139968 kB
22Mapped: 1094916 kB
23Shmem: 284160 kB
24KReclaimable: 3303788 kB
25Slab: 3801908 kB
26SReclaimable: 3303788 kB
27SUnreclaim: 498120 kB
28KernelStack: 32992 kB
29PageTables: 68160 kB
30SecPageTables: 0 kB
31NFS_Unstable: 0 kB
32Bounce: 0 kB
33WritebackTmp: 0 kB
34CommitLimit: 49815436 kB
35Committed_AS: 16888536 kB
36VmallocTotal: 34359738367 kB
37VmallocUsed: 91200 kB
38VmallocChunk: 0 kB
39Percpu: 41472 kB
40HardwareCorrupted: 0 kB
41AnonHugePages: 1708032 kB
42ShmemHugePages: 0 kB
43ShmemPmdMapped: 0 kB
44FileHugePages: 0 kB
45FilePmdMapped: 0 kB
46Unaccepted: 0 kB
47HugePages_Total: 0
48HugePages_Free: 0
49HugePages_Rsvd: 0
50HugePages_Surp: 0
51Hugepagesize: 2048 kB
52Hugetlb: 0 kB
53DirectMap4k: 860468 kB
54DirectMap2M: 20023296 kB
55DirectMap1G: 12582912 kB
diff --git a/plugins/urlize.c b/plugins/urlize.c
index 6fda72d1..a8590fae 100644
--- a/plugins/urlize.c
+++ b/plugins/urlize.c
@@ -1,51 +1,49 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring urlize plugin 3 * Monitoring urlize plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 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 urlize plugin 10 * This file contains the urlize plugin
11* 11 *
12* This plugin wraps the text output of another command (plugin) in HTML <A> 12 * This plugin wraps the text output of another command (plugin) in HTML <A>
13* tags. This plugin returns the status of the invoked plugin. 13 * tags. This plugin returns the status of the invoked plugin.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 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 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 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 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/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "urlize"; 32const char *progname = "urlize";
33const char *copyright = "2000-2006"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "utils.h" 37#include "utils.h"
38#include "popen.h" 38#include "popen.h"
39 39
40#define PERF_CHARACTER "|" 40#define PERF_CHARACTER "|"
41#define NEWLINE_CHARACTER '\n' 41#define NEWLINE_CHARACTER '\n'
42 42
43void print_help (void); 43void print_help(void);
44void print_usage (void); 44void print_usage(void);
45 45
46int 46int main(int argc, char **argv) {
47main (int argc, char **argv)
48{
49 int found = 0, result = STATE_UNKNOWN; 47 int found = 0, result = STATE_UNKNOWN;
50 char *url = NULL; 48 char *url = NULL;
51 char *cmd; 49 char *cmd;
@@ -55,144 +53,141 @@ main (int argc, char **argv)
55 53
56 int c; 54 int c;
57 int option = 0; 55 int option = 0;
58 static struct option longopts[] = { 56 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
59 {"help", no_argument, 0, 'h'}, 57 {"version", no_argument, 0, 'V'},
60 {"version", no_argument, 0, 'V'}, 58 {"url", required_argument, 0, 'u'},
61 {"url", required_argument, 0, 'u'}, 59 {0, 0, 0, 0}};
62 {0, 0, 0, 0}
63 };
64 60
65 setlocale (LC_ALL, ""); 61 setlocale(LC_ALL, "");
66 bindtextdomain (PACKAGE, LOCALEDIR); 62 bindtextdomain(PACKAGE, LOCALEDIR);
67 textdomain (PACKAGE); 63 textdomain(PACKAGE);
68 64
69 /* Need at least 2 args */ 65 /* Need at least 2 args */
70 if (argc < 3) { 66 if (argc < 3) {
71 print_help(); 67 print_help();
72 exit (STATE_UNKNOWN); 68 exit(STATE_UNKNOWN);
73 } 69 }
74 70
75 while (1) { 71 while (1) {
76 c = getopt_long (argc, argv, "+hVu:", longopts, &option); 72 c = getopt_long(argc, argv, "+hVu:", longopts, &option);
77 73
78 if (c == -1 || c == EOF) 74 if (c == -1 || c == EOF) {
79 break; 75 break;
76 }
80 77
81 switch (c) { 78 switch (c) {
82 case 'h': /* help */ 79 case 'h': /* help */
83 print_help (); 80 print_help();
84 exit (EXIT_SUCCESS); 81 exit(EXIT_SUCCESS);
85 break; 82 break;
86 case 'V': /* version */ 83 case 'V': /* version */
87 print_revision (progname, NP_VERSION); 84 print_revision(progname, NP_VERSION);
88 exit (EXIT_SUCCESS); 85 exit(EXIT_SUCCESS);
89 break; 86 break;
90 case 'u': 87 case 'u':
91 url = strdup (argv[optind]); 88 url = strdup(argv[optind]);
92 break; 89 break;
93 case '?': 90 case '?':
94 default: 91 default:
95 usage5 (); 92 usage5();
96 } 93 }
97 } 94 }
98 95
99 if (url == NULL) 96 if (url == NULL) {
100 url = strdup (argv[optind++]); 97 url = strdup(argv[optind++]);
98 }
101 99
102 cmd = strdup (argv[optind++]); 100 cmd = strdup(argv[optind++]);
103 for (c = optind; c < argc; c++) { 101 for (c = optind; c < argc; c++) {
104 xasprintf (&cmd, "%s %s", cmd, argv[c]); 102 xasprintf(&cmd, "%s %s", cmd, argv[c]);
105 } 103 }
106 104
107 child_process = spopen (cmd); 105 child_process = spopen(cmd);
108 if (child_process == NULL) { 106 if (child_process == NULL) {
109 printf (_("Could not open pipe: %s\n"), cmd); 107 printf(_("Could not open pipe: %s\n"), cmd);
110 exit (STATE_UNKNOWN); 108 exit(STATE_UNKNOWN);
111 } 109 }
112 110
113 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 111 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
114 if (child_stderr == NULL) { 112 if (child_stderr == NULL) {
115 printf (_("Could not open stderr for %s\n"), cmd); 113 printf(_("Could not open stderr for %s\n"), cmd);
116 } 114 }
117 115
118 bzero(tstr, sizeof(tstr)); 116 bzero(tstr, sizeof(tstr));
119 buf = malloc(MAX_INPUT_BUFFER); 117 buf = malloc(MAX_INPUT_BUFFER);
120 printf ("<A href=\"%s\">", argv[1]); 118 printf("<A href=\"%s\">", argv[1]);
121 while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) { 119 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) {
122 found++; 120 found++;
123 /* Collect the string in temp str so we can tokenize */ 121 /* Collect the string in temp str so we can tokenize */
124 strcat(tstr, buf); 122 strcat(tstr, buf);
125 } 123 }
126 124
127 if (!found) 125 if (!found) {
128 die (STATE_UNKNOWN, 126 die(STATE_UNKNOWN, _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), argv[0],
129 _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), 127 cmd);
130 argv[0], cmd); 128 }
131
132 129
133 /* chop the newline character */ 130 /* chop the newline character */
134 if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) 131 if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) {
135 *nstr = '\0'; 132 *nstr = '\0';
133 }
136 134
137 /* tokenize the string for Perfdata if there is some */ 135 /* tokenize the string for Perfdata if there is some */
138 nstr = strtok(tstr, PERF_CHARACTER); 136 nstr = strtok(tstr, PERF_CHARACTER);
139 printf ("%s", nstr); 137 printf("%s", nstr);
140 printf ("</A>"); 138 printf("</A>");
141 nstr = strtok(NULL, PERF_CHARACTER); 139 nstr = strtok(NULL, PERF_CHARACTER);
142 if (nstr != NULL) 140 if (nstr != NULL) {
143 printf (" | %s", nstr); 141 printf(" | %s", nstr);
142 }
144 143
145 /* close the pipe */ 144 /* close the pipe */
146 result = spclose (child_process); 145 result = spclose(child_process);
147 146
148 /* WARNING if output found on stderr */ 147 /* WARNING if output found on stderr */
149 if (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr)) 148 if (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
150 result = max_state (result, STATE_WARNING); 149 result = max_state(result, STATE_WARNING);
150 }
151 151
152 /* close stderr */ 152 /* close stderr */
153 (void) fclose (child_stderr); 153 (void)fclose(child_stderr);
154 154
155 return result; 155 return result;
156} 156}
157 157
158void print_help(void) {
159 print_revision(progname, NP_VERSION);
158 160
161 printf("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
162 printf(COPYRIGHT, copyright, email);
159 163
160void 164 printf("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>"));
161print_help (void) 165 printf("%s\n",
162{ 166 _("tags, thus displaying the child plugin's output as a clickable link in compatible"));
163 print_revision (progname, NP_VERSION); 167 printf("%s\n",
164 168 _("monitoring status screen. This plugin returns the status of the invoked plugin."));
165 printf ("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
166 printf (COPYRIGHT, copyright, email);
167
168 printf ("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>"));
169 printf ("%s\n", _("tags, thus displaying the child plugin's output as a clickable link in compatible"));
170 printf ("%s\n", _("monitoring status screen. This plugin returns the status of the invoked plugin."));
171 169
172 printf ("\n\n"); 170 printf("\n\n");
173 171
174 print_usage (); 172 print_usage();
175 173
176 printf (UT_HELP_VRSN); 174 printf(UT_HELP_VRSN);
177 175
178 printf ("\n"); 176 printf("\n");
179 printf ("%s\n", _("Examples:")); 177 printf("%s\n", _("Examples:"));
180 printf ("%s\n", _("Pay close attention to quoting to ensure that the shell passes the expected")); 178 printf("%s\n",
181 printf ("%s\n\n", _("data to the plugin. For example, in:")); 179 _("Pay close attention to quoting to ensure that the shell passes the expected"));
182 printf (" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'")); 180 printf("%s\n\n", _("data to the plugin. For example, in:"));
183 printf (" %s\n", _("the shell will remove the single quotes and urlize will see:")); 181 printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'"));
184 printf (" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r two words")); 182 printf(" %s\n", _("the shell will remove the single quotes and urlize will see:"));
185 printf (" %s\n\n", _("You probably want:")); 183 printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r two words"));
186 printf (" %s\n", _("urlize http://example.com/ \"check_http -H example.com -r 'two words'\"")); 184 printf(" %s\n\n", _("You probably want:"));
185 printf(" %s\n", _("urlize http://example.com/ \"check_http -H example.com -r 'two words'\""));
187 186
188 printf (UT_SUPPORT); 187 printf(UT_SUPPORT);
189} 188}
190 189
191 190void print_usage(void) {
192 191 printf("%s\n", _("Usage:"));
193void 192 printf("%s <url> <plugin> <arg1> ... <argN>\n", progname);
194print_usage (void)
195{
196 printf ("%s\n", _("Usage:"));
197 printf ("%s <url> <plugin> <arg1> ... <argN>\n", progname);
198} 193}
diff --git a/plugins/utils.c b/plugins/utils.c
index 77d6a6f9..dc6f5a85 100644
--- a/plugins/utils.c
+++ b/plugins/utils.c
@@ -1,26 +1,26 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Library of useful functions for plugins 3 * Library of useful functions for plugins
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000 Karl DeBisschop (karl@debisschop.net) 6 * Copyright (c) 2000 Karl DeBisschop (karl@debisschop.net)
7* Copyright (c) 2002-2007 Monitoring Plugins Development Team 7 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
8* 8 *
9* This program is free software: you can redistribute it and/or modify 9 * This program is free software: you can redistribute it and/or modify
10* it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
11* the Free Software Foundation, either version 3 of the License, or 11 * the Free Software Foundation, either version 3 of the License, or
12* (at your option) any later version. 12 * (at your option) any later version.
13* 13 *
14* This program is distributed in the hope that it will be useful, 14 * This program is distributed in the hope that it will be useful,
15* but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17* GNU General Public License for more details. 17 * GNU General Public License for more details.
18* 18 *
19* You should have received a copy of the GNU General Public License 19 * You should have received a copy of the GNU General Public License
20* along with this program. If not, see <http://www.gnu.org/licenses/>. 20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21* 21 *
22* 22 *
23*****************************************************************************/ 23 *****************************************************************************/
24 24
25#include "common.h" 25#include "common.h"
26#include "./utils.h" 26#include "./utils.h"
@@ -34,181 +34,121 @@
34 34
35#include <arpa/inet.h> 35#include <arpa/inet.h>
36 36
37extern void print_usage (void); 37extern void print_usage(void);
38extern const char *progname; 38extern const char *progname;
39 39
40#define STRLEN 64 40#define STRLEN 64
41#define TXTBLK 128 41#define TXTBLK 128
42 42
43time_t start_time, end_time; 43
44 44void usage(const char *msg) {
45/* ************************************************************************** 45 printf("%s\n", msg);
46 * max_state(STATE_x, STATE_y) 46 print_usage();
47 * compares STATE_x to STATE_y and returns result based on the following 47 exit(STATE_UNKNOWN);
48 * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL 48}
49 * 49
50 * Note that numerically the above does not hold 50void usage_va(const char *fmt, ...) {
51 ****************************************************************************/
52
53int
54max_state (int a, int b)
55{
56 if (a == STATE_CRITICAL || b == STATE_CRITICAL)
57 return STATE_CRITICAL;
58 else if (a == STATE_WARNING || b == STATE_WARNING)
59 return STATE_WARNING;
60 else if (a == STATE_OK || b == STATE_OK)
61 return STATE_OK;
62 else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
63 return STATE_UNKNOWN;
64 else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
65 return STATE_DEPENDENT;
66 else
67 return max (a, b);
68}
69
70/* **************************************************************************
71 * max_state_alt(STATE_x, STATE_y)
72 * compares STATE_x to STATE_y and returns result based on the following
73 * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
74 *
75 * The main difference between max_state_alt and max_state it that it doesn't
76 * allow setting a default to UNKNOWN. It will instead prioritixe any valid
77 * non-OK state.
78 ****************************************************************************/
79
80int
81max_state_alt (int a, int b)
82{
83 if (a == STATE_CRITICAL || b == STATE_CRITICAL)
84 return STATE_CRITICAL;
85 else if (a == STATE_WARNING || b == STATE_WARNING)
86 return STATE_WARNING;
87 else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
88 return STATE_UNKNOWN;
89 else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
90 return STATE_DEPENDENT;
91 else if (a == STATE_OK || b == STATE_OK)
92 return STATE_OK;
93 else
94 return max (a, b);
95}
96
97void usage (const char *msg)
98{
99 printf ("%s\n", msg);
100 print_usage ();
101 exit (STATE_UNKNOWN);
102}
103
104void usage_va (const char *fmt, ...)
105{
106 va_list ap; 51 va_list ap;
107 printf("%s: ", progname); 52 printf("%s: ", progname);
108 va_start(ap, fmt); 53 va_start(ap, fmt);
109 vprintf(fmt, ap); 54 vprintf(fmt, ap);
110 va_end(ap); 55 va_end(ap);
111 printf("\n"); 56 printf("\n");
112 exit (STATE_UNKNOWN); 57 exit(STATE_UNKNOWN);
113} 58}
114 59
115void usage2(const char *msg, const char *arg) 60void usage2(const char *msg, const char *arg) {
116{ 61 printf("%s: %s - %s\n", progname, msg, arg ? arg : "(null)");
117 printf ("%s: %s - %s\n", progname, msg, arg?arg:"(null)" ); 62 print_usage();
118 print_usage (); 63 exit(STATE_UNKNOWN);
119 exit (STATE_UNKNOWN);
120} 64}
121 65
122void 66void usage3(const char *msg, int arg) {
123usage3 (const char *msg, int arg) 67 printf("%s: %s - %c\n", progname, msg, arg);
124{
125 printf ("%s: %s - %c\n", progname, msg, arg);
126 print_usage(); 68 print_usage();
127 exit (STATE_UNKNOWN); 69 exit(STATE_UNKNOWN);
128} 70}
129 71
130void 72void usage4(const char *msg) {
131usage4 (const char *msg) 73 printf("%s: %s\n", progname, msg);
132{
133 printf ("%s: %s\n", progname, msg);
134 print_usage(); 74 print_usage();
135 exit (STATE_UNKNOWN); 75 exit(STATE_UNKNOWN);
136} 76}
137 77
138void 78void usage5(void) {
139usage5 (void)
140{
141 print_usage(); 79 print_usage();
142 exit (STATE_UNKNOWN); 80 exit(STATE_UNKNOWN);
143} 81}
144 82
145void 83void print_revision(const char *command_name, const char *revision) {
146print_revision (const char *command_name, const char *revision) 84 printf("%s v%s (%s %s)\n", command_name, revision, PACKAGE, VERSION);
147{
148 printf ("%s v%s (%s %s)\n",
149 command_name, revision, PACKAGE, VERSION);
150} 85}
151 86
152bool is_numeric (char *number) { 87bool is_numeric(char *number) {
153 char tmp[1]; 88 char tmp[1];
154 float x; 89 float x;
155 90
156 if (!number) 91 if (!number) {
157 return false; 92 return false;
158 else if (sscanf (number, "%f%c", &x, tmp) == 1) 93 } else if (sscanf(number, "%f%c", &x, tmp) == 1) {
159 return true; 94 return true;
160 else 95 } else {
161 return false; 96 return false;
97 }
162} 98}
163 99
164bool is_positive (char *number) { 100bool is_positive(char *number) {
165 if (is_numeric (number) && atof (number) > 0.0) 101 if (is_numeric(number) && atof(number) > 0.0) {
166 return true; 102 return true;
167 else 103 } else {
168 return false; 104 return false;
105 }
169} 106}
170 107
171bool is_negative (char *number) { 108bool is_negative(char *number) {
172 if (is_numeric (number) && atof (number) < 0.0) 109 if (is_numeric(number) && atof(number) < 0.0) {
173 return true; 110 return true;
174 else 111 } else {
175 return false; 112 return false;
113 }
176} 114}
177 115
178bool is_nonnegative (char *number) { 116bool is_nonnegative(char *number) {
179 if (is_numeric (number) && atof (number) >= 0.0) 117 if (is_numeric(number) && atof(number) >= 0.0) {
180 return true; 118 return true;
181 else 119 } else {
182 return false; 120 return false;
121 }
183} 122}
184 123
185bool is_percentage (char *number) { 124bool is_percentage(char *number) {
186 int x; 125 int x;
187 if (is_numeric (number) && (x = atof (number)) >= 0 && x <= 100) 126 if (is_numeric(number) && (x = atof(number)) >= 0 && x <= 100) {
188 return true; 127 return true;
189 else 128 } else {
190 return false; 129 return false;
130 }
191} 131}
192 132
193bool is_percentage_expression (const char str[]) { 133bool is_percentage_expression(const char str[]) {
194 if (!str) { 134 if (!str) {
195 return false; 135 return false;
196 } 136 }
197 137
198 size_t len = strlen(str); 138 size_t len = strlen(str);
199 139
200 if (str[len-1] != '%') { 140 if (str[len - 1] != '%') {
201 return false; 141 return false;
202 } 142 }
203 143
204 char *foo = calloc(sizeof(char), len + 1); 144 char *foo = calloc(len + 1, sizeof(char));
205 145
206 if (!foo) { 146 if (!foo) {
207 die (STATE_UNKNOWN, _("calloc failed \n")); 147 die(STATE_UNKNOWN, _("calloc failed \n"));
208 } 148 }
209 149
210 strcpy(foo, str); 150 strcpy(foo, str);
211 foo[len-1] = '\0'; 151 foo[len - 1] = '\0';
212 152
213 bool result = is_numeric(foo); 153 bool result = is_numeric(foo);
214 154
@@ -217,39 +157,44 @@ bool is_percentage_expression (const char str[]) {
217 return result; 157 return result;
218} 158}
219 159
220bool is_integer (char *number) { 160bool is_integer(char *number) {
221 long int n; 161 long int n;
222 162
223 if (!number || (strspn (number, "-0123456789 ") != strlen (number))) 163 if (!number || (strspn(number, "-0123456789 ") != strlen(number))) {
224 return false; 164 return false;
165 }
225 166
226 n = strtol (number, NULL, 10); 167 n = strtol(number, NULL, 10);
227 168
228 if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX) 169 if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX) {
229 return true; 170 return true;
230 else 171 } else {
231 return false; 172 return false;
173 }
232} 174}
233 175
234bool is_intpos (char *number) { 176bool is_intpos(char *number) {
235 if (is_integer (number) && atoi (number) > 0) 177 if (is_integer(number) && atoi(number) > 0) {
236 return true; 178 return true;
237 else 179 } else {
238 return false; 180 return false;
181 }
239} 182}
240 183
241bool is_intneg (char *number) { 184bool is_intneg(char *number) {
242 if (is_integer (number) && atoi (number) < 0) 185 if (is_integer(number) && atoi(number) < 0) {
243 return true; 186 return true;
244 else 187 } else {
245 return false; 188 return false;
189 }
246} 190}
247 191
248bool is_intnonneg (char *number) { 192bool is_intnonneg(char *number) {
249 if (is_integer (number) && atoi (number) >= 0) 193 if (is_integer(number) && atoi(number) >= 0) {
250 return true; 194 return true;
251 else 195 } else {
252 return false; 196 return false;
197 }
253} 198}
254 199
255/* 200/*
@@ -259,7 +204,7 @@ bool is_intnonneg (char *number) {
259 */ 204 */
260bool is_int64(char *number, int64_t *target) { 205bool is_int64(char *number, int64_t *target) {
261 errno = 0; 206 errno = 0;
262 char *endptr = { 0 }; 207 char *endptr = {0};
263 208
264 int64_t tmp = strtoll(number, &endptr, 10); 209 int64_t tmp = strtoll(number, &endptr, 10);
265 if (errno != 0) { 210 if (errno != 0) {
@@ -287,7 +232,7 @@ bool is_int64(char *number, int64_t *target) {
287 */ 232 */
288bool is_uint64(char *number, uint64_t *target) { 233bool is_uint64(char *number, uint64_t *target) {
289 errno = 0; 234 errno = 0;
290 char *endptr = { 0 }; 235 char *endptr = {0};
291 unsigned long long tmp = strtoull(number, &endptr, 10); 236 unsigned long long tmp = strtoull(number, &endptr, 10);
292 237
293 if (errno != 0) { 238 if (errno != 0) {
@@ -309,74 +254,61 @@ bool is_uint64(char *number, uint64_t *target) {
309 return true; 254 return true;
310} 255}
311 256
312bool is_intpercent (char *number) { 257bool is_intpercent(char *number) {
313 int i; 258 int i;
314 if (is_integer (number) && (i = atoi (number)) >= 0 && i <= 100) 259 if (is_integer(number) && (i = atoi(number)) >= 0 && i <= 100) {
315 return true; 260 return true;
316 else 261 } else {
317 return false; 262 return false;
263 }
318} 264}
319 265
320bool is_option (char *str) { 266bool is_option(char *str) {
321 if (!str) 267 if (!str) {
322 return false; 268 return false;
323 else if (strspn (str, "-") == 1 || strspn (str, "-") == 2) 269 } else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) {
324 return true; 270 return true;
325 else 271 } else {
326 return false; 272 return false;
273 }
327} 274}
328 275
329#ifdef NEED_GETTIMEOFDAY 276#ifdef NEED_GETTIMEOFDAY
330int 277int gettimeofday(struct timeval *tv, struct timezone *tz) {
331gettimeofday (struct timeval *tv, struct timezone *tz)
332{
333 tv->tv_usec = 0; 278 tv->tv_usec = 0;
334 tv->tv_sec = (long) time ((time_t) 0); 279 tv->tv_sec = (long)time((time_t)0);
335} 280}
336#endif 281#endif
337 282
338 283double delta_time(struct timeval tv) {
339
340double
341delta_time (struct timeval tv)
342{
343 struct timeval now; 284 struct timeval now;
344 285
345 gettimeofday (&now, NULL); 286 gettimeofday(&now, NULL);
346 return ((double)(now.tv_sec - tv.tv_sec) + (double)(now.tv_usec - tv.tv_usec) / (double)1000000); 287 return ((double)(now.tv_sec - tv.tv_sec) +
288 (double)(now.tv_usec - tv.tv_usec) / (double)1000000);
347} 289}
348 290
349 291long deltime(struct timeval tv) {
350
351long
352deltime (struct timeval tv)
353{
354 struct timeval now; 292 struct timeval now;
355 gettimeofday (&now, NULL); 293 gettimeofday(&now, NULL);
356 return (now.tv_sec - tv.tv_sec)*1000000 + now.tv_usec - tv.tv_usec; 294 return (now.tv_sec - tv.tv_sec) * 1000000 + now.tv_usec - tv.tv_usec;
357} 295}
358 296
359 297void strip(char *buffer) {
360
361
362void
363strip (char *buffer)
364{
365 size_t x; 298 size_t x;
366 int i; 299 int i;
367 300
368 for (x = strlen (buffer); x >= 1; x--) { 301 for (x = strlen(buffer); x >= 1; x--) {
369 i = x - 1; 302 i = x - 1;
370 if (buffer[i] == ' ' || 303 if (buffer[i] == ' ' || buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t') {
371 buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t')
372 buffer[i] = '\0'; 304 buffer[i] = '\0';
373 else 305 } else {
374 break; 306 break;
307 }
375 } 308 }
376 return; 309 return;
377} 310}
378 311
379
380/****************************************************************************** 312/******************************************************************************
381 * 313 *
382 * Copies one string to another. Any previously existing data in 314 * Copies one string to another. Any previously existing data in
@@ -389,19 +321,16 @@ strip (char *buffer)
389 * 321 *
390 *****************************************************************************/ 322 *****************************************************************************/
391 323
392char * 324char *strscpy(char *dest, const char *src) {
393strscpy (char *dest, const char *src) 325 if (src == NULL) {
394{
395 if (src == NULL)
396 return NULL; 326 return NULL;
327 }
397 328
398 xasprintf (&dest, "%s", src); 329 xasprintf(&dest, "%s", src);
399 330
400 return dest; 331 return dest;
401} 332}
402 333
403
404
405/****************************************************************************** 334/******************************************************************************
406 * 335 *
407 * Returns a pointer to the next line of a multiline string buffer 336 * Returns a pointer to the next line of a multiline string buffer
@@ -418,7 +347,7 @@ strscpy (char *dest, const char *src)
418 * This 347 * This
419 * is 348 * is
420 * a 349 * a
421 * 350 *
422 * multiline string buffer 351 * multiline string buffer
423 * ============================== 352 * ==============================
424 * 353 *
@@ -431,7 +360,7 @@ strscpy (char *dest, const char *src)
431 * printf("%d %s",i++,firstword(ptr)); 360 * printf("%d %s",i++,firstword(ptr));
432 * ptr = strnl(ptr); 361 * ptr = strnl(ptr);
433 * } 362 * }
434 * 363 *
435 * Produces the following: 364 * Produces the following:
436 * 365 *
437 * 1 This 366 * 1 This
@@ -452,25 +381,26 @@ strscpy (char *dest, const char *src)
452 * 381 *
453 *****************************************************************************/ 382 *****************************************************************************/
454 383
455char * 384char *strnl(char *str) {
456strnl (char *str)
457{
458 size_t len; 385 size_t len;
459 if (str == NULL) 386 if (str == NULL) {
460 return NULL; 387 return NULL;
461 str = strpbrk (str, "\r\n"); 388 }
462 if (str == NULL) 389 str = strpbrk(str, "\r\n");
390 if (str == NULL) {
463 return NULL; 391 return NULL;
464 len = strspn (str, "\r\n"); 392 }
465 if (str[len] == '\0') 393 len = strspn(str, "\r\n");
394 if (str[len] == '\0') {
466 return NULL; 395 return NULL;
396 }
467 str += len; 397 str += len;
468 if (strlen (str) == 0) 398 if (strlen(str) == 0) {
469 return NULL; 399 return NULL;
400 }
470 return str; 401 return str;
471} 402}
472 403
473
474/****************************************************************************** 404/******************************************************************************
475 * 405 *
476 * Like strscpy, except only the portion of the source string up to 406 * Like strscpy, except only the portion of the source string up to
@@ -487,29 +417,28 @@ strnl (char *str)
487 * 417 *
488 *****************************************************************************/ 418 *****************************************************************************/
489 419
490char * 420char *strpcpy(char *dest, const char *src, const char *str) {
491strpcpy (char *dest, const char *src, const char *str)
492{
493 size_t len; 421 size_t len;
494 422
495 if (src) 423 if (src) {
496 len = strcspn (src, str); 424 len = strcspn(src, str);
497 else 425 } else {
498 return NULL; 426 return NULL;
427 }
499 428
500 if (dest == NULL || strlen (dest) < len) 429 if (dest == NULL || strlen(dest) < len) {
501 dest = realloc (dest, len + 1); 430 dest = realloc(dest, len + 1);
502 if (dest == NULL) 431 }
503 die (STATE_UNKNOWN, _("failed realloc in strpcpy\n")); 432 if (dest == NULL) {
433 die(STATE_UNKNOWN, _("failed realloc in strpcpy\n"));
434 }
504 435
505 strncpy (dest, src, len); 436 strncpy(dest, src, len);
506 dest[len] = '\0'; 437 dest[len] = '\0';
507 438
508 return dest; 439 return dest;
509} 440}
510 441
511
512
513/****************************************************************************** 442/******************************************************************************
514 * 443 *
515 * Like strscat, except only the portion of the source string up to 444 * Like strscat, except only the portion of the source string up to
@@ -518,62 +447,57 @@ strpcpy (char *dest, const char *src, const char *str)
518 * str = strpcpy(str,"This is a line of text with no trailing newline","x"); 447 * str = strpcpy(str,"This is a line of text with no trailing newline","x");
519 * str = strpcat(str,"This is a line of text with no trailing newline","x"); 448 * str = strpcat(str,"This is a line of text with no trailing newline","x");
520 * printf("%s\n",str); 449 * printf("%s\n",str);
521 * 450 *
522 *This is a line of texThis is a line of tex 451 *This is a line of texThis is a line of tex
523 * 452 *
524 *****************************************************************************/ 453 *****************************************************************************/
525 454
526char * 455char *strpcat(char *dest, const char *src, const char *str) {
527strpcat (char *dest, const char *src, const char *str)
528{
529 size_t len, l2; 456 size_t len, l2;
530 457
531 if (dest) 458 if (dest) {
532 len = strlen (dest); 459 len = strlen(dest);
533 else 460 } else {
534 len = 0; 461 len = 0;
462 }
535 463
536 if (src) { 464 if (src) {
537 l2 = strcspn (src, str); 465 l2 = strcspn(src, str);
538 } 466 } else {
539 else {
540 return dest; 467 return dest;
541 } 468 }
542 469
543 dest = realloc (dest, len + l2 + 1); 470 dest = realloc(dest, len + l2 + 1);
544 if (dest == NULL) 471 if (dest == NULL) {
545 die (STATE_UNKNOWN, _("failed malloc in strscat\n")); 472 die(STATE_UNKNOWN, _("failed malloc in strscat\n"));
473 }
546 474
547 strncpy (dest + len, src, l2); 475 strncpy(dest + len, src, l2);
548 dest[len + l2] = '\0'; 476 dest[len + l2] = '\0';
549 477
550 return dest; 478 return dest;
551} 479}
552 480
553
554/****************************************************************************** 481/******************************************************************************
555 * 482 *
556 * asprintf, but die on failure 483 * asprintf, but die on failure
557 * 484 *
558 ******************************************************************************/ 485 ******************************************************************************/
559 486
560int 487int xvasprintf(char **strp, const char *fmt, va_list ap) {
561xvasprintf (char **strp, const char *fmt, va_list ap) 488 int result = vasprintf(strp, fmt, ap);
562{ 489 if (result == -1 || *strp == NULL) {
563 int result = vasprintf (strp, fmt, ap); 490 die(STATE_UNKNOWN, _("failed malloc in xvasprintf\n"));
564 if (result == -1 || *strp == NULL) 491 }
565 die (STATE_UNKNOWN, _("failed malloc in xvasprintf\n"));
566 return result; 492 return result;
567} 493}
568 494
569int 495int xasprintf(char **strp, const char *fmt, ...) {
570xasprintf (char **strp, const char *fmt, ...)
571{
572 va_list ap; 496 va_list ap;
573 int result; 497 int result;
574 va_start (ap, fmt); 498 va_start(ap, fmt);
575 result = xvasprintf (strp, fmt, ap); 499 result = xvasprintf(strp, fmt, ap);
576 va_end (ap); 500 va_end(ap);
577 return result; 501 return result;
578} 502}
579 503
@@ -583,247 +507,223 @@ xasprintf (char **strp, const char *fmt, ...)
583 * 507 *
584 ******************************************************************************/ 508 ******************************************************************************/
585 509
586char *perfdata (const char *label, 510char *perfdata(const char *label, long int val, const char *uom, bool warnp, long int warn,
587 long int val, 511 bool critp, long int crit, bool minp, long int minv, bool maxp, long int maxv) {
588 const char *uom,
589 int warnp,
590 long int warn,
591 int critp,
592 long int crit,
593 int minp,
594 long int minv,
595 int maxp,
596 long int maxv)
597{
598 char *data = NULL; 512 char *data = NULL;
599 513
600 if (strpbrk (label, "'= ")) 514 if (strpbrk(label, "'= ")) {
601 xasprintf (&data, "'%s'=%ld%s;", label, val, uom); 515 xasprintf(&data, "'%s'=%ld%s;", label, val, uom);
602 else 516 } else {
603 xasprintf (&data, "%s=%ld%s;", label, val, uom); 517 xasprintf(&data, "%s=%ld%s;", label, val, uom);
518 }
604 519
605 if (warnp) 520 if (warnp) {
606 xasprintf (&data, "%s%ld;", data, warn); 521 xasprintf(&data, "%s%ld;", data, warn);
607 else 522 } else {
608 xasprintf (&data, "%s;", data); 523 xasprintf(&data, "%s;", data);
524 }
609 525
610 if (critp) 526 if (critp) {
611 xasprintf (&data, "%s%ld;", data, crit); 527 xasprintf(&data, "%s%ld;", data, crit);
612 else 528 } else {
613 xasprintf (&data, "%s;", data); 529 xasprintf(&data, "%s;", data);
530 }
614 531
615 if (minp) 532 if (minp) {
616 xasprintf (&data, "%s%ld;", data, minv); 533 xasprintf(&data, "%s%ld;", data, minv);
617 else 534 } else {
618 xasprintf (&data, "%s;", data); 535 xasprintf(&data, "%s;", data);
536 }
619 537
620 if (maxp) 538 if (maxp) {
621 xasprintf (&data, "%s%ld", data, maxv); 539 xasprintf(&data, "%s%ld", data, maxv);
540 }
622 541
623 return data; 542 return data;
624} 543}
625 544
626 545char *perfdata_uint64(const char *label, uint64_t val, const char *uom,
627char *perfdata_uint64 (const char *label, 546 bool warnp, /* Warning present */
628 uint64_t val, 547 uint64_t warn, bool critp, /* Critical present */
629 const char *uom, 548 uint64_t crit, bool minp, /* Minimum present */
630 int warnp, /* Warning present */ 549 uint64_t minv, bool maxp, /* Maximum present */
631 uint64_t warn, 550 uint64_t maxv) {
632 int critp, /* Critical present */
633 uint64_t crit,
634 int minp, /* Minimum present */
635 uint64_t minv,
636 int maxp, /* Maximum present */
637 uint64_t maxv)
638{
639 char *data = NULL; 551 char *data = NULL;
640 552
641 if (strpbrk (label, "'= ")) 553 if (strpbrk(label, "'= ")) {
642 xasprintf (&data, "'%s'=%" PRIu64 "%s;", label, val, uom); 554 xasprintf(&data, "'%s'=%" PRIu64 "%s;", label, val, uom);
643 else 555 } else {
644 xasprintf (&data, "%s=%" PRIu64 "%s;", label, val, uom); 556 xasprintf(&data, "%s=%" PRIu64 "%s;", label, val, uom);
557 }
645 558
646 if (warnp) 559 if (warnp) {
647 xasprintf (&data, "%s%" PRIu64 ";", data, warn); 560 xasprintf(&data, "%s%" PRIu64 ";", data, warn);
648 else 561 } else {
649 xasprintf (&data, "%s;", data); 562 xasprintf(&data, "%s;", data);
563 }
650 564
651 if (critp) 565 if (critp) {
652 xasprintf (&data, "%s%" PRIu64 ";", data, crit); 566 xasprintf(&data, "%s%" PRIu64 ";", data, crit);
653 else 567 } else {
654 xasprintf (&data, "%s;", data); 568 xasprintf(&data, "%s;", data);
569 }
655 570
656 if (minp) 571 if (minp) {
657 xasprintf (&data, "%s%" PRIu64 ";", data, minv); 572 xasprintf(&data, "%s%" PRIu64 ";", data, minv);
658 else 573 } else {
659 xasprintf (&data, "%s;", data); 574 xasprintf(&data, "%s;", data);
575 }
660 576
661 if (maxp) 577 if (maxp) {
662 xasprintf (&data, "%s%" PRIu64, data, maxv); 578 xasprintf(&data, "%s%" PRIu64, data, maxv);
579 }
663 580
664 return data; 581 return data;
665} 582}
666 583
667 584char *perfdata_int64(const char *label, int64_t val, const char *uom,
668char *perfdata_int64 (const char *label, 585 bool warnp, /* Warning present */
669 int64_t val, 586 int64_t warn, bool critp, /* Critical present */
670 const char *uom, 587 int64_t crit, bool minp, /* Minimum present */
671 int warnp, /* Warning present */ 588 int64_t minv, bool maxp, /* Maximum present */
672 int64_t warn, 589 int64_t maxv) {
673 int critp, /* Critical present */
674 int64_t crit,
675 int minp, /* Minimum present */
676 int64_t minv,
677 int maxp, /* Maximum present */
678 int64_t maxv)
679{
680 char *data = NULL; 590 char *data = NULL;
681 591
682 if (strpbrk (label, "'= ")) 592 if (strpbrk(label, "'= ")) {
683 xasprintf (&data, "'%s'=%" PRId64 "%s;", label, val, uom); 593 xasprintf(&data, "'%s'=%" PRId64 "%s;", label, val, uom);
684 else 594 } else {
685 xasprintf (&data, "%s=%" PRId64 "%s;", label, val, uom); 595 xasprintf(&data, "%s=%" PRId64 "%s;", label, val, uom);
596 }
686 597
687 if (warnp) 598 if (warnp) {
688 xasprintf (&data, "%s%" PRId64 ";", data, warn); 599 xasprintf(&data, "%s%" PRId64 ";", data, warn);
689 else 600 } else {
690 xasprintf (&data, "%s;", data); 601 xasprintf(&data, "%s;", data);
602 }
691 603
692 if (critp) 604 if (critp) {
693 xasprintf (&data, "%s%" PRId64 ";", data, crit); 605 xasprintf(&data, "%s%" PRId64 ";", data, crit);
694 else 606 } else {
695 xasprintf (&data, "%s;", data); 607 xasprintf(&data, "%s;", data);
608 }
696 609
697 if (minp) 610 if (minp) {
698 xasprintf (&data, "%s%" PRId64 ";", data, minv); 611 xasprintf(&data, "%s%" PRId64 ";", data, minv);
699 else 612 } else {
700 xasprintf (&data, "%s;", data); 613 xasprintf(&data, "%s;", data);
614 }
701 615
702 if (maxp) 616 if (maxp) {
703 xasprintf (&data, "%s%" PRId64, data, maxv); 617 xasprintf(&data, "%s%" PRId64, data, maxv);
618 }
704 619
705 return data; 620 return data;
706} 621}
707 622
708 623char *fperfdata(const char *label, double val, const char *uom, bool warnp, double warn, bool critp,
709char *fperfdata (const char *label, 624 double crit, bool minp, double minv, bool maxp, double maxv) {
710 double val,
711 const char *uom,
712 int warnp,
713 double warn,
714 int critp,
715 double crit,
716 int minp,
717 double minv,
718 int maxp,
719 double maxv)
720{
721 char *data = NULL; 625 char *data = NULL;
722 626
723 if (strpbrk (label, "'= ")) 627 if (strpbrk(label, "'= ")) {
724 xasprintf (&data, "'%s'=", label); 628 xasprintf(&data, "'%s'=", label);
725 else 629 } else {
726 xasprintf (&data, "%s=", label); 630 xasprintf(&data, "%s=", label);
631 }
727 632
728 xasprintf (&data, "%s%f", data, val); 633 xasprintf(&data, "%s%f", data, val);
729 xasprintf (&data, "%s%s;", data, uom); 634 xasprintf(&data, "%s%s;", data, uom);
730 635
731 if (warnp) 636 if (warnp) {
732 xasprintf (&data, "%s%f", data, warn); 637 xasprintf(&data, "%s%f", data, warn);
638 }
733 639
734 xasprintf (&data, "%s;", data); 640 xasprintf(&data, "%s;", data);
735 641
736 if (critp) 642 if (critp) {
737 xasprintf (&data, "%s%f", data, crit); 643 xasprintf(&data, "%s%f", data, crit);
644 }
738 645
739 xasprintf (&data, "%s;", data); 646 xasprintf(&data, "%s;", data);
740 647
741 if (minp) 648 if (minp) {
742 xasprintf (&data, "%s%f", data, minv); 649 xasprintf(&data, "%s%f", data, minv);
650 }
743 651
744 if (maxp) { 652 if (maxp) {
745 xasprintf (&data, "%s;", data); 653 xasprintf(&data, "%s;", data);
746 xasprintf (&data, "%s%f", data, maxv); 654 xasprintf(&data, "%s%f", data, maxv);
747 } 655 }
748 656
749 return data; 657 return data;
750} 658}
751 659
752char *sperfdata (const char *label, 660char *sperfdata(const char *label, double val, const char *uom, char *warn, char *crit, bool minp,
753 double val, 661 double minv, bool maxp, double maxv) {
754 const char *uom,
755 char *warn,
756 char *crit,
757 int minp,
758 double minv,
759 int maxp,
760 double maxv)
761{
762 char *data = NULL; 662 char *data = NULL;
763 if (strpbrk (label, "'= ")) 663 if (strpbrk(label, "'= ")) {
764 xasprintf (&data, "'%s'=", label); 664 xasprintf(&data, "'%s'=", label);
765 else 665 } else {
766 xasprintf (&data, "%s=", label); 666 xasprintf(&data, "%s=", label);
667 }
767 668
768 xasprintf (&data, "%s%f", data, val); 669 xasprintf(&data, "%s%f", data, val);
769 xasprintf (&data, "%s%s;", data, uom); 670 xasprintf(&data, "%s%s;", data, uom);
770 671
771 if (warn!=NULL) 672 if (warn != NULL) {
772 xasprintf (&data, "%s%s", data, warn); 673 xasprintf(&data, "%s%s", data, warn);
674 }
773 675
774 xasprintf (&data, "%s;", data); 676 xasprintf(&data, "%s;", data);
775 677
776 if (crit!=NULL) 678 if (crit != NULL) {
777 xasprintf (&data, "%s%s", data, crit); 679 xasprintf(&data, "%s%s", data, crit);
680 }
778 681
779 xasprintf (&data, "%s;", data); 682 xasprintf(&data, "%s;", data);
780 683
781 if (minp) 684 if (minp) {
782 xasprintf (&data, "%s%f", data, minv); 685 xasprintf(&data, "%s%f", data, minv);
686 }
783 687
784 if (maxp) { 688 if (maxp) {
785 xasprintf (&data, "%s;", data); 689 xasprintf(&data, "%s;", data);
786 xasprintf (&data, "%s%f", data, maxv); 690 xasprintf(&data, "%s%f", data, maxv);
787 } 691 }
788 692
789 return data; 693 return data;
790} 694}
791 695
792char *sperfdata_int (const char *label, 696char *sperfdata_int(const char *label, int val, const char *uom, char *warn, char *crit, bool minp,
793 int val, 697 int minv, bool maxp, int maxv) {
794 const char *uom,
795 char *warn,
796 char *crit,
797 int minp,
798 int minv,
799 int maxp,
800 int maxv)
801{
802 char *data = NULL; 698 char *data = NULL;
803 if (strpbrk (label, "'= ")) 699 if (strpbrk(label, "'= ")) {
804 xasprintf (&data, "'%s'=", label); 700 xasprintf(&data, "'%s'=", label);
805 else 701 } else {
806 xasprintf (&data, "%s=", label); 702 xasprintf(&data, "%s=", label);
703 }
807 704
808 xasprintf (&data, "%s%d", data, val); 705 xasprintf(&data, "%s%d", data, val);
809 xasprintf (&data, "%s%s;", data, uom); 706 xasprintf(&data, "%s%s;", data, uom);
810 707
811 if (warn!=NULL) 708 if (warn != NULL) {
812 xasprintf (&data, "%s%s", data, warn); 709 xasprintf(&data, "%s%s", data, warn);
710 }
813 711
814 xasprintf (&data, "%s;", data); 712 xasprintf(&data, "%s;", data);
815 713
816 if (crit!=NULL) 714 if (crit != NULL) {
817 xasprintf (&data, "%s%s", data, crit); 715 xasprintf(&data, "%s%s", data, crit);
716 }
818 717
819 xasprintf (&data, "%s;", data); 718 xasprintf(&data, "%s;", data);
820 719
821 if (minp) 720 if (minp) {
822 xasprintf (&data, "%s%d", data, minv); 721 xasprintf(&data, "%s%d", data, minv);
722 }
823 723
824 if (maxp) { 724 if (maxp) {
825 xasprintf (&data, "%s;", data); 725 xasprintf(&data, "%s;", data);
826 xasprintf (&data, "%s%d", data, maxv); 726 xasprintf(&data, "%s%d", data, maxv);
827 } 727 }
828 728
829 return data; 729 return data;
diff --git a/plugins/utils.h b/plugins/utils.h
index f939e337..68ff1630 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -13,51 +13,49 @@ in order to resist overflow attacks. In addition, a few functions are
13provided to standardize version and error reporting across the entire 13provided to standardize version and error reporting across the entire
14suite of plugins. */ 14suite of plugins. */
15 15
16/* now some functions etc are being defined in ../lib/utils_base.c */ 16#include "../config.h"
17#include "utils_base.h"
18
19#include <stdbool.h> 17#include <stdbool.h>
20 18#include <stdint.h>
19#include <stdio.h>
20#include <time.h>
21 21
22#ifdef NP_EXTRA_OPTS 22#ifdef NP_EXTRA_OPTS
23/* Include extra-opts functions if compiled in */ 23/* Include extra-opts functions if compiled in */
24#include "extra_opts.h" 24# include "extra_opts.h"
25#else 25#else
26/* else, fake np_extra_opts */ 26/* else, fake np_extra_opts */
27#define np_extra_opts(acptr,av,pr) av 27# define np_extra_opts(acptr, av, pr) av
28#endif 28#endif
29 29
30/* Standardize version information, termination */ 30/* Standardize version information, termination */
31 31
32void support (void); 32void support(void);
33void print_revision (const char *, const char *); 33void print_revision(const char *, const char *);
34
35extern time_t start_time, end_time;
36 34
37/* Test input types */ 35/* Test input types */
38 36
39bool is_integer (char *); 37bool is_integer(char *);
40bool is_intpos (char *); 38bool is_intpos(char *);
41bool is_intneg (char *); 39bool is_intneg(char *);
42bool is_intnonneg (char *); 40bool is_intnonneg(char *);
43bool is_intpercent (char *); 41bool is_intpercent(char *);
44bool is_uint64(char *number, uint64_t *target); 42bool is_uint64(char *number, uint64_t *target);
45bool is_int64(char *number, int64_t *target); 43bool is_int64(char *number, int64_t *target);
46 44
47bool is_numeric (char *); 45bool is_numeric(char *);
48bool is_positive (char *); 46bool is_positive(char *);
49bool is_negative (char *); 47bool is_negative(char *);
50bool is_nonnegative (char *); 48bool is_nonnegative(char *);
51bool is_percentage (char *); 49bool is_percentage(char *);
52bool is_percentage_expression (const char[]); 50bool is_percentage_expression(const char[]);
53 51
54bool is_option (char *); 52bool is_option(char *);
55 53
56/* Generalized timer that will do milliseconds if available */ 54/* Generalized timer that will do milliseconds if available */
57#ifndef HAVE_STRUCT_TIMEVAL 55#ifndef HAVE_STRUCT_TIMEVAL
58struct timeval { 56struct timeval {
59 long tv_sec; /* seconds */ 57 long tv_sec; /* seconds */
60 long tv_usec; /* microseconds */ 58 long tv_usec; /* microseconds */
61}; 59};
62#endif 60#endif
63 61
@@ -65,137 +63,148 @@ struct timeval {
65int gettimeofday(struct timeval *, struct timezone *); 63int gettimeofday(struct timeval *, struct timezone *);
66#endif 64#endif
67 65
68double delta_time (struct timeval tv); 66double delta_time(struct timeval tv);
69long deltime (struct timeval tv); 67long deltime(struct timeval tv);
70 68
71/* Handle strings safely */ 69/* Handle strings safely */
72 70
73void strip (char *); 71void strip(char *);
74char *strscpy (char *, const char *); 72char *strscpy(char *, const char *);
75char *strnl (char *); 73char *strnl(char *);
76char *strpcpy (char *, const char *, const char *); 74char *strpcpy(char *, const char *, const char *);
77char *strpcat (char *, const char *, const char *); 75char *strpcat(char *, const char *, const char *);
78int xvasprintf (char **strp, const char *fmt, va_list ap); 76int xvasprintf(char **strp, const char *fmt, va_list ap);
79int xasprintf (char **strp, const char *fmt, ...); 77int xasprintf(char **strp, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
80 78
81int max_state (int a, int b); 79void usage(const char *) __attribute__((noreturn));
82int max_state_alt (int a, int b);
83
84void usage (const char *) __attribute__((noreturn));
85void usage2(const char *, const char *) __attribute__((noreturn)); 80void usage2(const char *, const char *) __attribute__((noreturn));
86void usage3(const char *, int) __attribute__((noreturn)); 81void usage3(const char *, int) __attribute__((noreturn));
87void usage4(const char *) __attribute__((noreturn)); 82void usage4(const char *) __attribute__((noreturn));
88void usage5(void) __attribute__((noreturn)); 83void usage5(void) __attribute__((noreturn));
89void usage_va(const char *fmt, ...) __attribute__((noreturn)); 84void usage_va(const char *fmt, ...) __attribute__((noreturn));
90 85
91#define max(a,b) (((a)>(b))?(a):(b)) 86#define max(a, b) (((a) > (b)) ? (a) : (b))
92#define min(a,b) (((a)<(b))?(a):(b)) 87#define min(a, b) (((a) < (b)) ? (a) : (b))
93 88
94char *perfdata (const char *, long int, const char *, int, long int, 89char *perfdata(const char *, long int, const char *, bool, long int, bool, long int, bool, long int,
95 int, long int, int, long int, int, long int); 90 bool, long int);
96 91
97char *perfdata_uint64 (const char *, uint64_t , const char *, int, uint64_t, 92char *perfdata_uint64(const char *, uint64_t, const char *, bool, uint64_t, bool, uint64_t, bool,
98 int, uint64_t, int, uint64_t, int, uint64_t); 93 uint64_t, bool, uint64_t);
99 94
100char *perfdata_int64 (const char *, int64_t, const char *, int, int64_t, 95char *perfdata_int64(const char *, int64_t, const char *, bool, int64_t, bool, int64_t, bool,
101 int, int64_t, int, int64_t, int, int64_t); 96 int64_t, bool, int64_t);
102 97
103char *fperfdata (const char *, double, const char *, int, double, 98char *fperfdata(const char *, double, const char *, bool, double, bool, double, bool, double, bool,
104 int, double, int, double, int, double); 99 double);
105 100
106char *sperfdata (const char *, double, const char *, char *, char *, 101char *sperfdata(const char *, double, const char *, char *, char *, bool, double, bool, double);
107 int, double, int, double);
108 102
109char *sperfdata_int (const char *, int, const char *, char *, char *, 103char *sperfdata_int(const char *, int, const char *, char *, char *, bool, int, bool, int);
110 int, int, int, int);
111 104
112/* The idea here is that, although not every plugin will use all of these, 105/* The idea here is that, although not every plugin will use all of these,
113 most will or should. Therefore, for consistency, these very common 106 most will or should. Therefore, for consistency, these very common
114 options should have only these meanings throughout the overall suite */ 107 options should have only these meanings throughout the overall suite */
115 108
116#define STD_LONG_OPTS \ 109#define STD_LONG_OPTS \
117{"version",no_argument,0,'V'},\ 110 {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, \
118{"verbose",no_argument,0,'v'},\ 111 {"help", no_argument, 0, 'h'}, {"timeout", required_argument, 0, 't'}, \
119{"help",no_argument,0,'h'},\ 112 {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, \
120{"timeout",required_argument,0,'t'},\ 113 {"hostname", required_argument, 0, 'H'}
121{"critical",required_argument,0,'c'},\
122{"warning",required_argument,0,'w'},\
123{"hostname",required_argument,0,'H'}
124 114
125#define COPYRIGHT "Copyright (c) %s Monitoring Plugins Development Team\n\ 115#define COPYRIGHT \
116 "Copyright (c) %s Monitoring Plugins Development Team\n\
126\t<%s>\n\n" 117\t<%s>\n\n"
127 118
128#define UT_HLP_VRS _("\ 119#define UT_HLP_VRS \
120 _("\
129 %s (-h | --help) for detailed help\n\ 121 %s (-h | --help) for detailed help\n\
130 %s (-V | --version) for version information\n") 122 %s (-V | --version) for version information\n")
131 123
132#define UT_HELP_VRSN _("\ 124#define UT_HELP_VRSN \
125 _("\
133\nOptions:\n\ 126\nOptions:\n\
134 -h, --help\n\ 127 -h, --help\n\
135 Print detailed help screen\n\ 128 Print detailed help screen\n\
136 -V, --version\n\ 129 -V, --version\n\
137 Print version information\n") 130 Print version information\n")
138 131
139#define UT_HOST_PORT _("\ 132#define UT_HOST_PORT \
133 _("\
140 -H, --hostname=ADDRESS\n\ 134 -H, --hostname=ADDRESS\n\
141 Host name, IP Address, or unix socket (must be an absolute path)\n\ 135 Host name, IP Address, or unix socket (must be an absolute path)\n\
142 -%c, --port=INTEGER\n\ 136 -%c, --port=INTEGER\n\
143 Port number (default: %s)\n") 137 Port number (default: %s)\n")
144 138
145#define UT_IPv46 _("\ 139#define UT_IPv46 \
140 _("\
146 -4, --use-ipv4\n\ 141 -4, --use-ipv4\n\
147 Use IPv4 connection\n\ 142 Use IPv4 connection\n\
148 -6, --use-ipv6\n\ 143 -6, --use-ipv6\n\
149 Use IPv6 connection\n") 144 Use IPv6 connection\n")
150 145
151#define UT_VERBOSE _("\ 146#define UT_VERBOSE \
147 _("\
152 -v, --verbose\n\ 148 -v, --verbose\n\
153 Show details for command-line debugging (output may be truncated by\n\ 149 Show details for command-line debugging (output may be truncated by\n\
154 the monitoring system)\n") 150 the monitoring system)\n")
155 151
156#define UT_WARN_CRIT _("\ 152#define UT_WARN_CRIT \
153 _("\
157 -w, --warning=DOUBLE\n\ 154 -w, --warning=DOUBLE\n\
158 Response time to result in warning status (seconds)\n\ 155 Response time to result in warning status (seconds)\n\
159 -c, --critical=DOUBLE\n\ 156 -c, --critical=DOUBLE\n\
160 Response time to result in critical status (seconds)\n") 157 Response time to result in critical status (seconds)\n")
161 158
162#define UT_WARN_CRIT_RANGE _("\ 159#define UT_WARN_CRIT_RANGE \
160 _("\
163 -w, --warning=RANGE\n\ 161 -w, --warning=RANGE\n\
164 Warning range (format: start:end). Alert if outside this range\n\ 162 Warning range (format: start:end). Alert if outside this range\n\
165 -c, --critical=RANGE\n\ 163 -c, --critical=RANGE\n\
166 Critical range\n") 164 Critical range\n")
167 165
168#define UT_CONN_TIMEOUT _("\ 166#define UT_CONN_TIMEOUT \
167 _("\
169 -t, --timeout=INTEGER\n\ 168 -t, --timeout=INTEGER\n\
170 Seconds before connection times out (default: %d)\n") 169 Seconds before connection times out (default: %d)\n")
171 170
172#define UT_PLUG_TIMEOUT _("\ 171#define UT_PLUG_TIMEOUT \
172 _("\
173 -t, --timeout=INTEGER\n\ 173 -t, --timeout=INTEGER\n\
174 Seconds before plugin times out (default: %d)\n") 174 Seconds before plugin times out (default: %d)\n")
175 175
176#ifdef NP_EXTRA_OPTS 176#ifdef NP_EXTRA_OPTS
177#define UT_EXTRA_OPTS _("\ 177# define UT_EXTRA_OPTS \
178 _("\
178 --extra-opts=[section][@file]\n\ 179 --extra-opts=[section][@file]\n\
179 Read options from an ini file. See\n\ 180 Read options from an ini file. See\n\
180 https://www.monitoring-plugins.org/doc/extra-opts.html\n\ 181 https://www.monitoring-plugins.org/doc/extra-opts.html\n\
181 for usage and examples.\n") 182 for usage and examples.\n")
182#else 183#else
183#define UT_EXTRA_OPTS " \b" 184# define UT_EXTRA_OPTS " \b"
184#endif 185#endif
185 186
186#define UT_THRESHOLDS_NOTES _("\ 187#define UT_THRESHOLDS_NOTES \
188 _("\
187 See:\n\ 189 See:\n\
188 https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT\n\ 190 https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT\n\
189 for THRESHOLD format and examples.\n") 191 for THRESHOLD format and examples.\n")
190 192
191#define UT_SUPPORT _("\n\ 193#define UT_SUPPORT \
194 _("\n\
192Send email to help@monitoring-plugins.org if you have questions regarding\n\ 195Send email to help@monitoring-plugins.org if you have questions regarding\n\
193use of this software. To submit patches or suggest improvements, send email\n\ 196use of this software. To submit patches or suggest improvements, send email\n\
194to devel@monitoring-plugins.org\n\n") 197to devel@monitoring-plugins.org\n\n")
195 198
196#define UT_NOWARRANTY _("\n\ 199#define UT_NOWARRANTY \
200 _("\n\
197The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\ 201The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\
198copies of the plugins under the terms of the GNU General Public License.\n\ 202copies of the plugins under the terms of the GNU General Public License.\n\
199For more information about these matters, see the file named COPYING.\n") 203For more information about these matters, see the file named COPYING.\n")
200 204
205#define UT_OUTPUT_FORMAT \
206 _("\
207 --output-format=OUTPUT_FORMAT\n\
208 Select output format. Valid values: \"multi-line\", \"mp-test-json\"\n")
209
201#endif /* NP_UTILS_H */ 210#endif /* NP_UTILS_H */