summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am71
-rw-r--r--plugins/check_apt.c159
-rw-r--r--plugins/check_apt.d/config.h41
-rw-r--r--plugins/check_by_ssh.c351
-rw-r--r--plugins/check_by_ssh.d/config.h56
-rw-r--r--plugins/check_cluster.c169
-rw-r--r--plugins/check_cluster.d/config.h27
-rw-r--r--plugins/check_curl.c2886
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c1267
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h124
-rw-r--r--plugins/check_curl.d/config.h116
-rw-r--r--plugins/check_dbi.c108
-rw-r--r--plugins/check_dig.c189
-rw-r--r--plugins/check_dig.d/config.h40
-rw-r--r--plugins/check_disk.c1447
-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.c133
-rw-r--r--plugins/check_dummy.c15
-rw-r--r--plugins/check_fping.c376
-rw-r--r--plugins/check_fping.d/config.h81
-rw-r--r--plugins/check_game.c68
-rw-r--r--plugins/check_hpjd.c186
-rw-r--r--plugins/check_hpjd.d/config.h25
-rw-r--r--plugins/check_http.c3581
-rw-r--r--plugins/check_ide_smart.c90
-rw-r--r--plugins/check_ldap.c616
-rw-r--r--plugins/check_ldap.d/config.h60
-rw-r--r--plugins/check_load.c662
-rw-r--r--plugins/check_load.d/config.h30
-rw-r--r--plugins/check_mrtg.c226
-rw-r--r--plugins/check_mrtg.d/config.h36
-rw-r--r--plugins/check_mrtgtraf.c146
-rw-r--r--plugins/check_mrtgtraf.d/config.h30
-rw-r--r--plugins/check_mysql.c293
-rw-r--r--plugins/check_mysql.d/config.h58
-rw-r--r--plugins/check_mysql_query.c156
-rw-r--r--plugins/check_mysql_query.d/config.h36
-rw-r--r--plugins/check_nagios.c168
-rw-r--r--plugins/check_nagios.d/config.h19
-rw-r--r--plugins/check_nt.c618
-rw-r--r--plugins/check_nt.d/config.h53
-rw-r--r--plugins/check_ntp.c756
-rw-r--r--plugins/check_ntp_peer.c487
-rw-r--r--plugins/check_ntp_peer.d/config.h67
-rw-r--r--plugins/check_ntp_time.c299
-rw-r--r--plugins/check_ntp_time.d/config.h28
-rw-r--r--plugins/check_nwstat.c1527
-rw-r--r--plugins/check_overcr.c417
-rw-r--r--plugins/check_pgsql.c275
-rw-r--r--plugins/check_pgsql.d/config.h61
-rw-r--r--plugins/check_ping.c580
-rw-r--r--plugins/check_ping.d/config.h46
-rw-r--r--plugins/check_procs.c1178
-rw-r--r--plugins/check_procs.d/config.h75
-rw-r--r--plugins/check_radius.c553
-rw-r--r--plugins/check_radius.d/config.h42
-rw-r--r--plugins/check_real.c269
-rw-r--r--plugins/check_real.d/config.h37
-rw-r--r--plugins/check_smtp.c1190
-rw-r--r--plugins/check_smtp.d/config.h92
-rw-r--r--plugins/check_snmp.c1767
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.c934
-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.c89
-rw-r--r--plugins/check_swap.c31
-rw-r--r--plugins/check_swap.d/check_swap.h8
-rw-r--r--plugins/check_swap.d/swap.c118
-rw-r--r--plugins/check_tcp.c872
-rw-r--r--plugins/check_tcp.d/config.h84
-rw-r--r--plugins/check_time.c198
-rw-r--r--plugins/check_time.d/config.h42
-rw-r--r--plugins/check_ups.c309
-rw-r--r--plugins/check_ups.d/config.h54
-rw-r--r--plugins/check_users.c266
-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.h181
-rw-r--r--plugins/negate.c138
-rw-r--r--plugins/negate.d/config.h24
-rw-r--r--plugins/netutils.c224
-rw-r--r--plugins/netutils.h152
-rw-r--r--plugins/picohttpparser/picohttpparser.c243
-rw-r--r--plugins/picohttpparser/picohttpparser.h31
-rw-r--r--plugins/popen.c68
-rw-r--r--plugins/popen.h20
-rw-r--r--plugins/runcmd.c77
-rw-r--r--plugins/runcmd.h47
-rw-r--r--plugins/sslutils.c255
-rw-r--r--plugins/t/check_curl.t49
-rw-r--r--plugins/t/check_disk.t220
-rw-r--r--plugins/t/check_ftp.t2
-rw-r--r--plugins/t/check_jabber.t4
-rw-r--r--plugins/t/check_load.t18
-rw-r--r--plugins/t/check_snmp.t103
-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.t89
-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.c4
-rw-r--r--plugins/urlize.c36
-rw-r--r--plugins/utils.c35
-rw-r--r--plugins/utils.h51
112 files changed, 17645 insertions, 13696 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index d40a0937..1a9399f0 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,33 +33,70 @@ 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 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_nt 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 \
42 \ 49 \
43 tests/test_check_swap 50 tests/test_check_swap \
51 tests/test_check_snmp \
52 tests/test_check_disk
44 53
45SUBDIRS = picohttpparser 54SUBDIRS = picohttpparser
46 55
47np_test_scripts = tests/test_check_swap.t 56np_test_scripts = tests/test_check_swap.t \
57 tests/test_check_snmp.t \
58 tests/test_check_disk.t
48 59
49EXTRA_DIST = t \ 60EXTRA_DIST = t \
50 tests \ 61 tests \
51 $(np_test_scripts) \ 62 $(np_test_scripts) \
63 negate.d \
52 check_swap.d \ 64 check_swap.d \
65 check_ldap.d \
66 check_hpjd.d \
53 check_game.d \ 67 check_game.d \
68 check_radius.d \
69 check_curl.d \
70 check_disk.d \
71 check_time.d \
72 check_users.d \
73 check_load.d \
74 check_nagios.d \
54 check_dbi.d \ 75 check_dbi.d \
76 check_tcp.d \
77 check_real.d \
55 check_ssh.d \ 78 check_ssh.d \
56 check_dns.d 79 check_nt.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
57 100
58PLUGINHDRS = common.h 101PLUGINHDRS = common.h
59 102
@@ -92,9 +135,11 @@ check_cluster_LDADD = $(BASEOBJS)
92check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 135check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
93check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 136check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
94check_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
95check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 139check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
96check_dig_LDADD = $(NETLIBS) 140check_dig_LDADD = $(NETLIBS)
97check_disk_LDADD = $(BASEOBJS) 141check_disk_LDADD = $(BASEOBJS)
142check_disk_SOURCES = check_disk.c check_disk.d/utils_disk.c
98check_dns_LDADD = $(NETLIBS) 143check_dns_LDADD = $(NETLIBS)
99check_dummy_LDADD = $(BASEOBJS) 144check_dummy_LDADD = $(BASEOBJS)
100check_fping_LDADD = $(NETLIBS) 145check_fping_LDADD = $(NETLIBS)
@@ -115,14 +160,15 @@ check_nagios_LDADD = $(BASEOBJS)
115check_nt_LDADD = $(NETLIBS) 160check_nt_LDADD = $(NETLIBS)
116check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) 161check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
117check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 162check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
118check_nwstat_LDADD = $(NETLIBS)
119check_overcr_LDADD = $(NETLIBS)
120check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) 163check_pgsql_LDADD = $(NETLIBS) $(PGLIBS)
121check_ping_LDADD = $(NETLIBS) 164check_ping_LDADD = $(NETLIBS)
122check_procs_LDADD = $(BASEOBJS) 165check_procs_LDADD = $(BASEOBJS)
123check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) 166check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS)
124check_real_LDADD = $(NETLIBS) 167check_real_LDADD = $(NETLIBS)
168check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c
125check_snmp_LDADD = $(BASEOBJS) 169check_snmp_LDADD = $(BASEOBJS)
170check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs`
171check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags`
126check_smtp_LDADD = $(SSLOBJS) 172check_smtp_LDADD = $(SSLOBJS)
127check_ssh_LDADD = $(NETLIBS) 173check_ssh_LDADD = $(NETLIBS)
128check_swap_SOURCES = check_swap.c check_swap.d/swap.c 174check_swap_SOURCES = check_swap.c check_swap.d/swap.c
@@ -131,6 +177,7 @@ check_tcp_LDADD = $(SSLOBJS)
131check_time_LDADD = $(NETLIBS) 177check_time_LDADD = $(NETLIBS)
132check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) 178check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS)
133check_ups_LDADD = $(NETLIBS) 179check_ups_LDADD = $(NETLIBS)
180check_users_SOURCES = check_users.c check_users.d/users.c
134check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) 181check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS)
135check_by_ssh_LDADD = $(NETLIBS) 182check_by_ssh_LDADD = $(NETLIBS)
136check_ide_smart_LDADD = $(BASEOBJS) 183check_ide_smart_LDADD = $(BASEOBJS)
@@ -143,6 +190,10 @@ endif
143 190
144tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap 191tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
145tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c 192tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c
193tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
194tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c
195tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap
196tests_test_check_disk_SOURCES = tests/test_check_disk.c
146 197
147############################################################################## 198##############################################################################
148# secondary dependencies 199# secondary dependencies
diff --git a/plugins/check_apt.c b/plugins/check_apt.c
index 1eda45dd..ab66a8d2 100644
--- a/plugins/check_apt.c
+++ b/plugins/check_apt.c
@@ -29,6 +29,7 @@
29 * 29 *
30 *****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "states.h"
32const char *progname = "check_apt"; 33const char *progname = "check_apt";
33const char *copyright = "2006-2024"; 34const char *copyright = "2006-2024";
34const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
@@ -37,13 +38,7 @@ const char *email = "devel@monitoring-plugins.org";
37#include "runcmd.h" 38#include "runcmd.h"
38#include "utils.h" 39#include "utils.h"
39#include "regex.h" 40#include "regex.h"
40 41#include "check_apt.d/config.h"
41/* some constants */
42typedef enum {
43 UPGRADE,
44 DIST_UPGRADE,
45 NO_UPGRADE
46} upgrade_type;
47 42
48/* Character for hidden input file option (for testing). */ 43/* Character for hidden input file option (for testing). */
49#define INPUT_FILE_OPT CHAR_MAX + 1 44#define INPUT_FILE_OPT CHAR_MAX + 1
@@ -61,14 +56,18 @@ typedef enum {
61#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" 56#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)"
62 57
63/* some standard functions */ 58/* some standard functions */
64static int process_arguments(int /*argc*/, char ** /*argv*/); 59typedef struct {
60 int errorcode;
61 check_apt_config config;
62} check_apt_config_wrapper;
63static check_apt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
65static void print_help(void); 64static void print_help(void);
66void print_usage(void); 65void print_usage(void);
67 66
68/* construct the appropriate apt-get cmdline */ 67/* construct the appropriate apt-get cmdline */
69static char *construct_cmdline(upgrade_type u, const char *opts); 68static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/);
70/* run an apt-get update */ 69/* run an apt-get update */
71static int run_update(void); 70static int run_update(char * /*update_opts*/);
72 71
73typedef struct { 72typedef struct {
74 int errorcode; 73 int errorcode;
@@ -79,42 +78,35 @@ typedef struct {
79} run_upgrade_result; 78} run_upgrade_result;
80 79
81/* run an apt-get upgrade */ 80/* run an apt-get upgrade */
82static run_upgrade_result run_upgrade(void); 81run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude, const char *do_critical,
82 const char *upgrade_opts, const char *input_filename);
83 83
84/* add another clause to a regexp */ 84/* add another clause to a regexp */
85static char *add_to_regexp(char *expr, const char *next); 85static char *add_to_regexp(char * /*expr*/, const char * /*next*/);
86/* extract package name from Inst line */ 86/* extract package name from Inst line */
87static char *pkg_name(char *line); 87static char *pkg_name(char * /*line*/);
88/* string comparison function for qsort */ 88/* string comparison function for qsort */
89static int cmpstringp(const void *p1, const void *p2); 89static int cmpstringp(const void * /*p1*/, const void * /*p2*/);
90 90
91/* configuration variables */ 91/* configuration variables */
92static int verbose = 0; /* -v */ 92static int verbose = 0; /* -v */
93static bool list = false; /* list packages available for upgrade */
94static bool do_update = false; /* whether to call apt-get update */
95static bool only_critical = false; /* whether to warn about non-critical updates */
96static upgrade_type upgrade = UPGRADE; /* which type of upgrade to do */
97static char *upgrade_opts = NULL; /* options to override defaults for upgrade */
98static char *update_opts = NULL; /* options to override defaults for update */
99static char *do_include = NULL; /* regexp to only include certain packages */
100static char *do_exclude = NULL; /* regexp to only exclude certain packages */
101static char *do_critical = NULL; /* regexp specifying critical packages */
102static char *input_filename = NULL; /* input filename for testing */
103/* number of packages available for upgrade to return WARNING status */
104static int packages_warning = 1;
105 93
106/* other global variables */ 94/* other global variables */
107static int stderr_warning = 0; /* if a cmd issued output on stderr */ 95static bool stderr_warning = false; /* if a cmd issued output on stderr */
108static int exec_warning = 0; /* if a cmd exited non-zero */ 96static bool exec_warning = false; /* if a cmd exited non-zero */
109 97
110int main(int argc, char **argv) { 98int main(int argc, char **argv) {
111 /* Parse extra opts if any */ 99 /* Parse extra opts if any */
112 argv = np_extra_opts(&argc, argv, progname); 100 argv = np_extra_opts(&argc, argv, progname);
113 101
114 if (process_arguments(argc, argv) == ERROR) { 102 check_apt_config_wrapper tmp_config = process_arguments(argc, argv);
103
104 if (tmp_config.errorcode == ERROR) {
115 usage_va(_("Could not parse arguments")); 105 usage_va(_("Could not parse arguments"));
116 } 106 }
117 107
108 const check_apt_config config = tmp_config.config;
109
118 /* Set signal handling and alarm timeout */ 110 /* Set signal handling and alarm timeout */
119 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 111 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
120 usage_va(_("Cannot catch SIGALRM")); 112 usage_va(_("Cannot catch SIGALRM"));
@@ -123,14 +115,15 @@ int main(int argc, char **argv) {
123 /* handle timeouts gracefully... */ 115 /* handle timeouts gracefully... */
124 alarm(timeout_interval); 116 alarm(timeout_interval);
125 117
126 int result = STATE_UNKNOWN; 118 mp_state_enum result = STATE_UNKNOWN;
127 /* if they want to run apt-get update first... */ 119 /* if they want to run apt-get update first... */
128 if (do_update) { 120 if (config.do_update) {
129 result = run_update(); 121 result = run_update(config.update_opts);
130 } 122 }
131 123
132 /* apt-get upgrade */ 124 /* apt-get upgrade */
133 run_upgrade_result upgrad_res = run_upgrade(); 125 run_upgrade_result upgrad_res =
126 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical, config.upgrade_opts, config.input_filename);
134 127
135 result = max_state(result, upgrad_res.errorcode); 128 result = max_state(result, upgrad_res.errorcode);
136 int packages_available = upgrad_res.package_count; 129 int packages_available = upgrad_res.package_count;
@@ -140,18 +133,18 @@ int main(int argc, char **argv) {
140 133
141 if (sec_count > 0) { 134 if (sec_count > 0) {
142 result = max_state(result, STATE_CRITICAL); 135 result = max_state(result, STATE_CRITICAL);
143 } else if (packages_available >= packages_warning && only_critical == false) { 136 } else if (packages_available >= config.packages_warning && !config.only_critical) {
144 result = max_state(result, STATE_WARNING); 137 result = max_state(result, STATE_WARNING);
145 } else if (result > STATE_UNKNOWN) { 138 } else if (result > STATE_UNKNOWN) {
146 result = STATE_UNKNOWN; 139 result = STATE_UNKNOWN;
147 } 140 }
148 141
149 printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), 142 printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"),
150 state_text(result), packages_available, (upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count, 143 state_text(result), packages_available, (config.upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count,
151 (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "", 144 (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "",
152 (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count); 145 (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count);
153 146
154 if (list) { 147 if (config.list) {
155 qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp); 148 qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp);
156 qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp); 149 qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp);
157 150
@@ -159,7 +152,7 @@ int main(int argc, char **argv) {
159 printf("%s (security)\n", secpackages_list[i]); 152 printf("%s (security)\n", secpackages_list[i]);
160 } 153 }
161 154
162 if (only_critical == false) { 155 if (!config.only_critical) {
163 for (int i = 0; i < packages_available - sec_count; i++) { 156 for (int i = 0; i < packages_available - sec_count; i++) {
164 printf("%s\n", packages_list[i]); 157 printf("%s\n", packages_list[i]);
165 } 158 }
@@ -170,7 +163,7 @@ int main(int argc, char **argv) {
170} 163}
171 164
172/* process command-line arguments */ 165/* process command-line arguments */
173int process_arguments(int argc, char **argv) { 166check_apt_config_wrapper process_arguments(int argc, char **argv) {
174 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 167 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
175 {"help", no_argument, 0, 'h'}, 168 {"help", no_argument, 0, 'h'},
176 {"verbose", no_argument, 0, 'v'}, 169 {"verbose", no_argument, 0, 'v'},
@@ -179,7 +172,7 @@ int process_arguments(int argc, char **argv) {
179 {"upgrade", optional_argument, 0, 'U'}, 172 {"upgrade", optional_argument, 0, 'U'},
180 {"no-upgrade", no_argument, 0, 'n'}, 173 {"no-upgrade", no_argument, 0, 'n'},
181 {"dist-upgrade", optional_argument, 0, 'd'}, 174 {"dist-upgrade", optional_argument, 0, 'd'},
182 {"list", no_argument, false, 'l'}, 175 {"list", no_argument, 0, 'l'},
183 {"include", required_argument, 0, 'i'}, 176 {"include", required_argument, 0, 'i'},
184 {"exclude", required_argument, 0, 'e'}, 177 {"exclude", required_argument, 0, 'e'},
185 {"critical", required_argument, 0, 'c'}, 178 {"critical", required_argument, 0, 'c'},
@@ -188,6 +181,11 @@ int process_arguments(int argc, char **argv) {
188 {"packages-warning", required_argument, 0, 'w'}, 181 {"packages-warning", required_argument, 0, 'w'},
189 {0, 0, 0, 0}}; 182 {0, 0, 0, 0}};
190 183
184 check_apt_config_wrapper result = {
185 .errorcode = OK,
186 .config = check_apt_config_init(),
187 };
188
191 while (true) { 189 while (true) {
192 int option_char = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL); 190 int option_char = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL);
193 191
@@ -209,55 +207,55 @@ int process_arguments(int argc, char **argv) {
209 timeout_interval = atoi(optarg); 207 timeout_interval = atoi(optarg);
210 break; 208 break;
211 case 'd': 209 case 'd':
212 upgrade = DIST_UPGRADE; 210 result.config.upgrade = DIST_UPGRADE;
213 if (optarg != NULL) { 211 if (optarg != NULL) {
214 upgrade_opts = strdup(optarg); 212 result.config.upgrade_opts = strdup(optarg);
215 if (upgrade_opts == NULL) { 213 if (result.config.upgrade_opts == NULL) {
216 die(STATE_UNKNOWN, "strdup failed"); 214 die(STATE_UNKNOWN, "strdup failed");
217 } 215 }
218 } 216 }
219 break; 217 break;
220 case 'U': 218 case 'U':
221 upgrade = UPGRADE; 219 result.config.upgrade = UPGRADE;
222 if (optarg != NULL) { 220 if (optarg != NULL) {
223 upgrade_opts = strdup(optarg); 221 result.config.upgrade_opts = strdup(optarg);
224 if (upgrade_opts == NULL) { 222 if (result.config.upgrade_opts == NULL) {
225 die(STATE_UNKNOWN, "strdup failed"); 223 die(STATE_UNKNOWN, "strdup failed");
226 } 224 }
227 } 225 }
228 break; 226 break;
229 case 'n': 227 case 'n':
230 upgrade = NO_UPGRADE; 228 result.config.upgrade = NO_UPGRADE;
231 break; 229 break;
232 case 'u': 230 case 'u':
233 do_update = true; 231 result.config.do_update = true;
234 if (optarg != NULL) { 232 if (optarg != NULL) {
235 update_opts = strdup(optarg); 233 result.config.update_opts = strdup(optarg);
236 if (update_opts == NULL) { 234 if (result.config.update_opts == NULL) {
237 die(STATE_UNKNOWN, "strdup failed"); 235 die(STATE_UNKNOWN, "strdup failed");
238 } 236 }
239 } 237 }
240 break; 238 break;
241 case 'l': 239 case 'l':
242 list = true; 240 result.config.list = true;
243 break; 241 break;
244 case 'i': 242 case 'i':
245 do_include = add_to_regexp(do_include, optarg); 243 result.config.do_include = add_to_regexp(result.config.do_include, optarg);
246 break; 244 break;
247 case 'e': 245 case 'e':
248 do_exclude = add_to_regexp(do_exclude, optarg); 246 result.config.do_exclude = add_to_regexp(result.config.do_exclude, optarg);
249 break; 247 break;
250 case 'c': 248 case 'c':
251 do_critical = add_to_regexp(do_critical, optarg); 249 result.config.do_critical = add_to_regexp(result.config.do_critical, optarg);
252 break; 250 break;
253 case 'o': 251 case 'o':
254 only_critical = true; 252 result.config.only_critical = true;
255 break; 253 break;
256 case INPUT_FILE_OPT: 254 case INPUT_FILE_OPT:
257 input_filename = optarg; 255 result.config.input_filename = optarg;
258 break; 256 break;
259 case 'w': 257 case 'w':
260 packages_warning = atoi(optarg); 258 result.config.packages_warning = atoi(optarg);
261 break; 259 break;
262 default: 260 default:
263 /* print short usage statement if args not parsable */ 261 /* print short usage statement if args not parsable */
@@ -265,11 +263,12 @@ int process_arguments(int argc, char **argv) {
265 } 263 }
266 } 264 }
267 265
268 return OK; 266 return result;
269} 267}
270 268
271/* run an apt-get upgrade */ 269/* run an apt-get upgrade */
272run_upgrade_result run_upgrade(void) { 270run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include, const char *do_exclude, const char *do_critical,
271 const char *upgrade_opts, const char *input_filename) {
273 regex_t ereg; 272 regex_t ereg;
274 /* initialize ereg as it is possible it is printed while uninitialized */ 273 /* initialize ereg as it is possible it is printed while uninitialized */
275 memset(&ereg, '\0', sizeof(ereg.buffer)); 274 memset(&ereg, '\0', sizeof(ereg.buffer));
@@ -311,8 +310,8 @@ run_upgrade_result run_upgrade(void) {
311 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 310 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
312 } 311 }
313 312
314 struct output chld_out; 313 output chld_out;
315 struct output chld_err; 314 output chld_err;
316 char *cmdline = NULL; 315 char *cmdline = NULL;
317 cmdline = construct_cmdline(upgrade, upgrade_opts); 316 cmdline = construct_cmdline(upgrade, upgrade_opts);
318 if (input_filename != NULL) { 317 if (input_filename != NULL) {
@@ -332,7 +331,7 @@ run_upgrade_result run_upgrade(void) {
332 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); 331 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
333 } 332 }
334 333
335 char **pkglist = malloc(sizeof(char *) * chld_out.lines); 334 char **pkglist = malloc(sizeof(char *) * chld_out.lines);
336 if (!pkglist) { 335 if (!pkglist) {
337 die(STATE_UNKNOWN, "malloc failed!\n"); 336 die(STATE_UNKNOWN, "malloc failed!\n");
338 } 337 }
@@ -385,7 +384,7 @@ run_upgrade_result run_upgrade(void) {
385 384
386 /* If we get anything on stderr, at least set warning */ 385 /* If we get anything on stderr, at least set warning */
387 if (input_filename == NULL && chld_err.buflen) { 386 if (input_filename == NULL && chld_err.buflen) {
388 stderr_warning = 1; 387 stderr_warning = true;
389 result.errorcode = max_state(result.errorcode, STATE_WARNING); 388 result.errorcode = max_state(result.errorcode, STATE_WARNING);
390 if (verbose) { 389 if (verbose) {
391 for (size_t i = 0; i < chld_err.lines; i++) { 390 for (size_t i = 0; i < chld_err.lines; i++) {
@@ -405,20 +404,20 @@ run_upgrade_result run_upgrade(void) {
405} 404}
406 405
407/* run an apt-get update (needs root) */ 406/* run an apt-get update (needs root) */
408int run_update(void) { 407int run_update(char *update_opts) {
409 int result = STATE_UNKNOWN; 408 int result = STATE_UNKNOWN;
410 char *cmdline; 409 char *cmdline;
411 /* run the update */ 410 /* run the update */
412 cmdline = construct_cmdline(NO_UPGRADE, update_opts); 411 cmdline = construct_cmdline(NO_UPGRADE, update_opts);
413 412
414 struct output chld_out; 413 output chld_out;
415 struct output chld_err; 414 output chld_err;
416 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 415 result = np_runcmd(cmdline, &chld_out, &chld_err, 0);
417 /* apt-get update changes exit status if it can't fetch packages. 416 /* apt-get update changes exit status if it can't fetch packages.
418 * since we were explicitly asked to do so, this is treated as 417 * since we were explicitly asked to do so, this is treated as
419 * a critical error. */ 418 * a critical error. */
420 if (result != 0) { 419 if (result != 0) {
421 exec_warning = 1; 420 exec_warning = true;
422 result = STATE_CRITICAL; 421 result = STATE_CRITICAL;
423 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); 422 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
424 } 423 }
@@ -446,7 +445,7 @@ int run_update(void) {
446char *pkg_name(char *line) { 445char *pkg_name(char *line) {
447 char *start = line + strlen(PKGINST_PREFIX); 446 char *start = line + strlen(PKGINST_PREFIX);
448 447
449 int len = strlen(start); 448 size_t len = strlen(start);
450 449
451 char *space = index(start, ' '); 450 char *space = index(start, ' ');
452 if (space != NULL) { 451 if (space != NULL) {
@@ -464,35 +463,37 @@ char *pkg_name(char *line) {
464 return pkg; 463 return pkg;
465} 464}
466 465
467int cmpstringp(const void *p1, const void *p2) { return strcmp(*(char *const *)p1, *(char *const *)p2); } 466int cmpstringp(const void *left_string, const void *right_string) {
467 return strcmp(*(char *const *)left_string, *(char *const *)right_string);
468}
468 469
469char *add_to_regexp(char *expr, const char *next) { 470char *add_to_regexp(char *expr, const char *next) {
470 char *re = NULL; 471 char *regex_string = NULL;
471 472
472 if (expr == NULL) { 473 if (expr == NULL) {
473 re = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1)); 474 regex_string = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1));
474 if (!re) { 475 if (!regex_string) {
475 die(STATE_UNKNOWN, "malloc failed!\n"); 476 die(STATE_UNKNOWN, "malloc failed!\n");
476 } 477 }
477 sprintf(re, "(%s)", next); 478 sprintf(regex_string, "(%s)", next);
478 } else { 479 } else {
479 /* resize it, adding an extra char for the new '|' separator */ 480 /* resize it, adding an extra char for the new '|' separator */
480 re = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1)); 481 regex_string = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1));
481 if (!re) { 482 if (!regex_string) {
482 die(STATE_UNKNOWN, "realloc failed!\n"); 483 die(STATE_UNKNOWN, "realloc failed!\n");
483 } 484 }
484 /* append it starting at ')' in the old re */ 485 /* append it starting at ')' in the old re */
485 sprintf((char *)(re + strlen(re) - 1), "|%s)", next); 486 sprintf((char *)(regex_string + strlen(regex_string) - 1), "|%s)", next);
486 } 487 }
487 488
488 return re; 489 return regex_string;
489} 490}
490 491
491char *construct_cmdline(upgrade_type u, const char *opts) { 492char *construct_cmdline(upgrade_type upgrade, const char *opts) {
492 const char *opts_ptr = NULL; 493 const char *opts_ptr = NULL;
493 const char *aptcmd = NULL; 494 const char *aptcmd = NULL;
494 495
495 switch (u) { 496 switch (upgrade) {
496 case UPGRADE: 497 case UPGRADE:
497 if (opts == NULL) { 498 if (opts == NULL) {
498 opts_ptr = UPGRADE_DEFAULT_OPTS; 499 opts_ptr = UPGRADE_DEFAULT_OPTS;
diff --git a/plugins/check_apt.d/config.h b/plugins/check_apt.d/config.h
new file mode 100644
index 00000000..981f4f42
--- /dev/null
+++ b/plugins/check_apt.d/config.h
@@ -0,0 +1,41 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6/* some constants */
7typedef enum {
8 UPGRADE,
9 DIST_UPGRADE,
10 NO_UPGRADE
11} upgrade_type;
12
13typedef struct {
14 bool do_update; /* whether to call apt-get update */
15 upgrade_type upgrade; /* which type of upgrade to do */
16 bool only_critical; /* whether to warn about non-critical updates */
17 bool list; /* list packages available for upgrade */
18 /* number of packages available for upgrade to return WARNING status */
19 int packages_warning;
20
21 char *upgrade_opts; /* options to override defaults for upgrade */
22 char *update_opts; /* options to override defaults for update */
23 char *do_include; /* regexp to only include certain packages */
24 char *do_exclude; /* regexp to only exclude certain packages */
25 char *do_critical; /* regexp specifying critical packages */
26 char *input_filename; /* input filename for testing */
27} check_apt_config;
28
29check_apt_config check_apt_config_init() {
30 check_apt_config tmp = {.do_update = false,
31 .upgrade = UPGRADE,
32 .only_critical = false,
33 .list = false,
34 .packages_warning = 1,
35 .update_opts = NULL,
36 .do_include = NULL,
37 .do_exclude = NULL,
38 .do_critical = NULL,
39 .input_filename = NULL};
40 return tmp;
41}
diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c
index 2ac7805d..74b7a46f 100644
--- a/plugins/check_by_ssh.c
+++ b/plugins/check_by_ssh.c
@@ -32,48 +32,29 @@ const char *email = "devel@monitoring-plugins.org";
32 32
33#include "common.h" 33#include "common.h"
34#include "utils.h" 34#include "utils.h"
35#include "netutils.h"
36#include "utils_cmd.h" 35#include "utils_cmd.h"
36#include "check_by_ssh.d/config.h"
37#include "states.h"
37 38
38#ifndef NP_MAXARGS 39#ifndef NP_MAXARGS
39# define NP_MAXARGS 1024 40# define NP_MAXARGS 1024
40#endif 41#endif
41 42
42static int process_arguments(int /*argc*/, char ** /*argv*/); 43typedef struct {
43static int validate_arguments(void); 44 int errorcode;
44static void comm_append(const char * /*str*/); 45 check_by_ssh_config config;
46} check_by_ssh_config_wrapper;
47static check_by_ssh_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
48static check_by_ssh_config_wrapper
49 validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/);
50
51static command_construct comm_append(command_construct /*cmd*/, const char * /*str*/);
45static void print_help(void); 52static void print_help(void);
46void print_usage(void); 53void print_usage(void);
47 54
48static unsigned int commands = 0;
49static unsigned int services = 0;
50static int skip_stdout = 0;
51static int skip_stderr = 0;
52static int warn_on_stderr = 0;
53static bool unknown_timeout = false;
54static char *remotecmd = NULL;
55static char **commargv = NULL;
56static int commargc = 0;
57static char *hostname = NULL;
58static char *outputfile = NULL;
59static char *host_shortname = NULL;
60static char **service;
61static bool passive = false;
62static bool verbose = false; 55static bool verbose = false;
63 56
64int main(int argc, char **argv) { 57int main(int argc, char **argv) {
65
66 char *status_text;
67 int cresult;
68 int result = STATE_UNKNOWN;
69 time_t local_time;
70 FILE *file_pointer = NULL;
71 output chld_out;
72 output chld_err;
73
74 remotecmd = "";
75 comm_append(SSH_COMMAND);
76
77 setlocale(LC_ALL, ""); 58 setlocale(LC_ALL, "");
78 bindtextdomain(PACKAGE, LOCALEDIR); 59 bindtextdomain(PACKAGE, LOCALEDIR);
79 textdomain(PACKAGE); 60 textdomain(PACKAGE);
@@ -81,11 +62,15 @@ int main(int argc, char **argv) {
81 /* Parse extra opts if any */ 62 /* Parse extra opts if any */
82 argv = np_extra_opts(&argc, argv, progname); 63 argv = np_extra_opts(&argc, argv, progname);
83 64
65 check_by_ssh_config_wrapper tmp_config = process_arguments(argc, argv);
66
84 /* process arguments */ 67 /* process arguments */
85 if (process_arguments(argc, argv) == ERROR) { 68 if (tmp_config.errorcode == ERROR) {
86 usage_va(_("Could not parse arguments")); 69 usage_va(_("Could not parse arguments"));
87 } 70 }
88 71
72 const check_by_ssh_config config = tmp_config.config;
73
89 /* Set signal handling and alarm timeout */ 74 /* Set signal handling and alarm timeout */
90 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 75 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
91 usage_va(_("Cannot catch SIGALRM")); 76 usage_va(_("Cannot catch SIGALRM"));
@@ -94,17 +79,20 @@ int main(int argc, char **argv) {
94 79
95 /* run the command */ 80 /* run the command */
96 if (verbose) { 81 if (verbose) {
97 printf("Command: %s\n", commargv[0]); 82 printf("Command: %s\n", config.cmd.commargv[0]);
98 for (int i = 1; i < commargc; i++) { 83 for (int i = 1; i < config.cmd.commargc; i++) {
99 printf("Argument %i: %s\n", i, commargv[i]); 84 printf("Argument %i: %s\n", i, config.cmd.commargv[i]);
100 } 85 }
101 } 86 }
102 87
103 result = cmd_run_array(commargv, &chld_out, &chld_err, 0); 88 output chld_out;
89 output chld_err;
90 mp_state_enum result = cmd_run_array(config.cmd.commargv, &chld_out, &chld_err, 0);
104 91
105 /* SSH returns 255 if connection attempt fails; include the first line of error output */ 92 /* SSH returns 255 if connection attempt fails; include the first line of error output */
106 if (result == 255 && unknown_timeout) { 93 if (result == 255 && config.unknown_timeout) {
107 printf(_("SSH connection failed: %s\n"), chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); 94 printf(_("SSH connection failed: %s\n"),
95 chld_err.lines > 0 ? chld_err.line[0] : "(no error output)");
108 return STATE_UNKNOWN; 96 return STATE_UNKNOWN;
109 } 97 }
110 98
@@ -117,17 +105,24 @@ int main(int argc, char **argv) {
117 } 105 }
118 } 106 }
119 107
120 if (skip_stdout == -1) { /* --skip-stdout specified without argument */ 108 size_t skip_stdout = 0;
109 if (config.skip_stdout == -1) { /* --skip-stdout specified without argument */
121 skip_stdout = chld_out.lines; 110 skip_stdout = chld_out.lines;
111 } else {
112 skip_stdout = config.skip_stdout;
122 } 113 }
123 if (skip_stderr == -1) { /* --skip-stderr specified without argument */ 114
115 size_t skip_stderr = 0;
116 if (config.skip_stderr == -1) { /* --skip-stderr specified without argument */
124 skip_stderr = chld_err.lines; 117 skip_stderr = chld_err.lines;
118 } else {
119 skip_stderr = config.skip_stderr;
125 } 120 }
126 121
127 /* UNKNOWN or worse if (non-skipped) output found on stderr */ 122 /* UNKNOWN or worse if (non-skipped) output found on stderr */
128 if (chld_err.lines > (size_t)skip_stderr) { 123 if (chld_err.lines > (size_t)skip_stderr) {
129 printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); 124 printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]);
130 if (warn_on_stderr) { 125 if (config.warn_on_stderr) {
131 return max_state_alt(result, STATE_WARNING); 126 return max_state_alt(result, STATE_WARNING);
132 } 127 }
133 return max_state_alt(result, STATE_UNKNOWN); 128 return max_state_alt(result, STATE_UNKNOWN);
@@ -135,13 +130,14 @@ int main(int argc, char **argv) {
135 130
136 /* this is simple if we're not supposed to be passive. 131 /* this is simple if we're not supposed to be passive.
137 * Wrap up quickly and keep the tricks below */ 132 * Wrap up quickly and keep the tricks below */
138 if (!passive) { 133 if (!config.passive) {
139 if (chld_out.lines > (size_t)skip_stdout) { 134 if (chld_out.lines > (size_t)skip_stdout) {
140 for (size_t i = skip_stdout; i < chld_out.lines; i++) { 135 for (size_t i = skip_stdout; i < chld_out.lines; i++) {
141 puts(chld_out.line[i]); 136 puts(chld_out.line[i]);
142 } 137 }
143 } else { 138 } else {
144 printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), state_text(result), remotecmd, result); 139 printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"),
140 state_text(result), config.remotecmd, result);
145 } 141 }
146 return result; /* return error status from remote command */ 142 return result; /* return error status from remote command */
147 } 143 }
@@ -151,83 +147,94 @@ int main(int argc, char **argv) {
151 */ 147 */
152 148
153 /* process output */ 149 /* process output */
154 if (!(file_pointer = fopen(outputfile, "a"))) { 150 FILE *file_pointer = NULL;
155 printf(_("SSH WARNING: could not open %s\n"), outputfile); 151 if (!(file_pointer = fopen(config.outputfile, "a"))) {
152 printf(_("SSH WARNING: could not open %s\n"), config.outputfile);
156 exit(STATE_UNKNOWN); 153 exit(STATE_UNKNOWN);
157 } 154 }
158 155
159 local_time = time(NULL); 156 time_t local_time = time(NULL);
160 commands = 0; 157 unsigned int commands = 0;
158 char *status_text;
159 int cresult;
161 for (size_t i = skip_stdout; i < chld_out.lines; i++) { 160 for (size_t i = skip_stdout; i < chld_out.lines; i++) {
162 status_text = chld_out.line[i++]; 161 status_text = chld_out.line[i++];
163 if (i == chld_out.lines || strstr(chld_out.line[i], "STATUS CODE: ") == NULL) { 162 if (i == chld_out.lines || strstr(chld_out.line[i], "STATUS CODE: ") == NULL) {
164 die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); 163 die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname);
165 } 164 }
166 165
167 if (service[commands] && status_text && sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { 166 if (config.service[commands] && status_text &&
168 fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, host_shortname, service[commands++], 167 sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) {
169 cresult, status_text); 168 fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
169 (int)local_time, config.host_shortname, config.service[commands++], cresult,
170 status_text);
170 } 171 }
171 } 172 }
172 173
173 /* Multiple commands and passive checking should always return OK */ 174 /* Multiple commands and passive checking should always return OK */
174 return result; 175 exit(result);
175} 176}
176 177
177/* process command-line arguments */ 178/* process command-line arguments */
178int process_arguments(int argc, char **argv) { 179check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
179 int c; 180 static struct option longopts[] = {
180 char *p1; 181 {"version", no_argument, 0, 'V'},
181 char *p2; 182 {"help", no_argument, 0, 'h'},
182 183 {"verbose", no_argument, 0, 'v'},
183 int option = 0; 184 {"fork", no_argument, 0, 'f'},
184 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 185 {"timeout", required_argument, 0, 't'},
185 {"help", no_argument, 0, 'h'}, 186 {"unknown-timeout", no_argument, 0, 'U'},
186 {"verbose", no_argument, 0, 'v'}, 187 {"host", required_argument, 0, 'H'}, /* backward compatibility */
187 {"fork", no_argument, 0, 'f'}, 188 {"hostname", required_argument, 0, 'H'},
188 {"timeout", required_argument, 0, 't'}, 189 {"port", required_argument, 0, 'p'},
189 {"unknown-timeout", no_argument, 0, 'U'}, 190 {"output", required_argument, 0, 'O'},
190 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 191 {"name", required_argument, 0, 'n'},
191 {"hostname", required_argument, 0, 'H'}, 192 {"services", required_argument, 0, 's'},
192 {"port", required_argument, 0, 'p'}, 193 {"identity", required_argument, 0, 'i'},
193 {"output", required_argument, 0, 'O'}, 194 {"user", required_argument, 0, 'u'},
194 {"name", required_argument, 0, 'n'}, 195 {"logname", required_argument, 0, 'l'},
195 {"services", required_argument, 0, 's'}, 196 {"command", required_argument, 0, 'C'},
196 {"identity", required_argument, 0, 'i'}, 197 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */
197 {"user", required_argument, 0, 'u'}, 198 {"skip-stdout", optional_argument, 0, 'S'},
198 {"logname", required_argument, 0, 'l'}, 199 {"skip-stderr", optional_argument, 0, 'E'},
199 {"command", required_argument, 0, 'C'}, 200 {"warn-on-stderr", no_argument, 0, 'W'},
200 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ 201 {"proto1", no_argument, 0, '1'},
201 {"skip-stdout", optional_argument, 0, 'S'}, 202 {"proto2", no_argument, 0, '2'},
202 {"skip-stderr", optional_argument, 0, 'E'}, 203 {"use-ipv4", no_argument, 0, '4'},
203 {"warn-on-stderr", no_argument, 0, 'W'}, 204 {"use-ipv6", no_argument, 0, '6'},
204 {"proto1", no_argument, 0, '1'}, 205 {"ssh-option", required_argument, 0, 'o'},
205 {"proto2", no_argument, 0, '2'}, 206 {"quiet", no_argument, 0, 'q'},
206 {"use-ipv4", no_argument, 0, '4'}, 207 {"configfile", optional_argument, 0, 'F'},
207 {"use-ipv6", no_argument, 0, '6'}, 208 {0, 0, 0, 0}};
208 {"ssh-option", required_argument, 0, 'o'}, 209
209 {"quiet", no_argument, 0, 'q'}, 210 check_by_ssh_config_wrapper result = {
210 {"configfile", optional_argument, 0, 'F'}, 211 .errorcode = OK,
211 {0, 0, 0, 0}}; 212 .config = check_by_ssh_config_init(),
213 };
212 214
213 if (argc < 2) { 215 if (argc < 2) {
214 return ERROR; 216 result.errorcode = ERROR;
217 return result;
215 } 218 }
216 219
217 for (c = 1; c < argc; c++) { 220 for (int index = 1; index < argc; index++) {
218 if (strcmp("-to", argv[c]) == 0) { 221 if (strcmp("-to", argv[index]) == 0) {
219 strcpy(argv[c], "-t"); 222 strcpy(argv[index], "-t");
220 } 223 }
221 } 224 }
222 225
223 while (1) { 226 result.config.cmd = comm_append(result.config.cmd, SSH_COMMAND);
224 c = getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option);
225 227
226 if (c == -1 || c == EOF) { 228 int option = 0;
229 while (true) {
230 int opt_index =
231 getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option);
232
233 if (opt_index == -1 || opt_index == EOF) {
227 break; 234 break;
228 } 235 }
229 236
230 switch (c) { 237 switch (opt_index) {
231 case 'V': /* version */ 238 case 'V': /* version */
232 print_revision(progname, NP_VERSION); 239 print_revision(progname, NP_VERSION);
233 exit(STATE_UNKNOWN); 240 exit(STATE_UNKNOWN);
@@ -245,169 +252,192 @@ int process_arguments(int argc, char **argv) {
245 } 252 }
246 break; 253 break;
247 case 'U': 254 case 'U':
248 unknown_timeout = true; 255 result.config.unknown_timeout = true;
249 break; 256 break;
250 case 'H': /* host */ 257 case 'H': /* host */
251 hostname = optarg; 258 result.config.hostname = optarg;
252 break; 259 break;
253 case 'p': /* port number */ 260 case 'p': /* port number */
254 if (!is_integer(optarg)) { 261 if (!is_integer(optarg)) {
255 usage_va(_("Port must be a positive integer")); 262 usage_va(_("Port must be a positive integer"));
256 } 263 }
257 comm_append("-p"); 264 result.config.cmd = comm_append(result.config.cmd, "-p");
258 comm_append(optarg); 265 result.config.cmd = comm_append(result.config.cmd, optarg);
259 break; 266 break;
260 case 'O': /* output file */ 267 case 'O': /* output file */
261 outputfile = optarg; 268 result.config.outputfile = optarg;
262 passive = true; 269 result.config.passive = true;
263 break; 270 break;
264 case 's': /* description of service to check */ 271 case 's': /* description of service to check */ {
272 char *p1;
273 char *p2;
274
265 p1 = optarg; 275 p1 = optarg;
266 service = realloc(service, (++services) * sizeof(char *)); 276 result.config.service = realloc(result.config.service,
277 (++result.config.number_of_services) * sizeof(char *));
267 while ((p2 = index(p1, ':'))) { 278 while ((p2 = index(p1, ':'))) {
268 *p2 = '\0'; 279 *p2 = '\0';
269 service[services - 1] = p1; 280 result.config.service[result.config.number_of_services - 1] = p1;
270 service = realloc(service, (++services) * sizeof(char *)); 281 result.config.service = realloc(
282 result.config.service, (++result.config.number_of_services) * sizeof(char *));
271 p1 = p2 + 1; 283 p1 = p2 + 1;
272 } 284 }
273 service[services - 1] = p1; 285 result.config.service[result.config.number_of_services - 1] = p1;
274 break; 286 break;
275 case 'n': /* short name of host in the monitoring configuration */ 287 case 'n': /* short name of host in the monitoring configuration */
276 host_shortname = optarg; 288 result.config.host_shortname = optarg;
277 break; 289 } break;
278
279 case 'u': 290 case 'u':
280 comm_append("-l"); 291 result.config.cmd = comm_append(result.config.cmd, "-l");
281 comm_append(optarg); 292 result.config.cmd = comm_append(result.config.cmd, optarg);
282 break; 293 break;
283 case 'l': /* login name */ 294 case 'l': /* login name */
284 comm_append("-l"); 295 result.config.cmd = comm_append(result.config.cmd, "-l");
285 comm_append(optarg); 296 result.config.cmd = comm_append(result.config.cmd, optarg);
286 break; 297 break;
287 case 'i': /* identity */ 298 case 'i': /* identity */
288 comm_append("-i"); 299 result.config.cmd = comm_append(result.config.cmd, "-i");
289 comm_append(optarg); 300 result.config.cmd = comm_append(result.config.cmd, optarg);
290 break; 301 break;
291 302
292 case '1': /* Pass these switches directly to ssh */ 303 case '1': /* Pass these switches directly to ssh */
293 comm_append("-1"); 304 result.config.cmd = comm_append(result.config.cmd, "-1");
294 break; 305 break;
295 case '2': /* 1 to force version 1, 2 to force version 2 */ 306 case '2': /* 1 to force version 1, 2 to force version 2 */
296 comm_append("-2"); 307 result.config.cmd = comm_append(result.config.cmd, "-2");
297 break; 308 break;
298 case '4': /* -4 for IPv4 */ 309 case '4': /* -4 for IPv4 */
299 comm_append("-4"); 310 result.config.cmd = comm_append(result.config.cmd, "-4");
300 break; 311 break;
301 case '6': /* -6 for IPv6 */ 312 case '6': /* -6 for IPv6 */
302 comm_append("-6"); 313 result.config.cmd = comm_append(result.config.cmd, "-6");
303 break; 314 break;
304 case 'f': /* fork to background */ 315 case 'f': /* fork to background */
305 comm_append("-f"); 316 result.config.cmd = comm_append(result.config.cmd, "-f");
306 break; 317 break;
307 case 'C': /* Command for remote machine */ 318 case 'C': /* Command for remote machine */
308 commands++; 319 result.config.commands++;
309 if (commands > 1) { 320 if (result.config.commands > 1) {
310 xasprintf(&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); 321 xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;",
322 result.config.remotecmd);
311 } 323 }
312 xasprintf(&remotecmd, "%s%s", remotecmd, optarg); 324 xasprintf(&result.config.remotecmd, "%s%s", result.config.remotecmd, optarg);
313 break; 325 break;
314 case 'S': /* skip n (or all) lines on stdout */ 326 case 'S': /* skip n (or all) lines on stdout */
315 if (optarg == NULL) { 327 if (optarg == NULL) {
316 skip_stdout = -1; /* skip all output on stdout */ 328 result.config.skip_stdout = -1; /* skip all output on stdout */
317 } else if (!is_integer(optarg)) { 329 } else if (!is_integer(optarg)) {
318 usage_va(_("skip-stdout argument must be an integer")); 330 usage_va(_("skip-stdout argument must be an integer"));
319 } else { 331 } else {
320 skip_stdout = atoi(optarg); 332 result.config.skip_stdout = atoi(optarg);
321 } 333 }
322 break; 334 break;
323 case 'E': /* skip n (or all) lines on stderr */ 335 case 'E': /* skip n (or all) lines on stderr */
324 if (optarg == NULL) { 336 if (optarg == NULL) {
325 skip_stderr = -1; /* skip all output on stderr */ 337 result.config.skip_stderr = -1; /* skip all output on stderr */
326 } else if (!is_integer(optarg)) { 338 } else if (!is_integer(optarg)) {
327 usage_va(_("skip-stderr argument must be an integer")); 339 usage_va(_("skip-stderr argument must be an integer"));
328 } else { 340 } else {
329 skip_stderr = atoi(optarg); 341 result.config.skip_stderr = atoi(optarg);
330 } 342 }
331 break; 343 break;
332 case 'W': /* exit with warning if there is an output on stderr */ 344 case 'W': /* exit with warning if there is an output on stderr */
333 warn_on_stderr = 1; 345 result.config.warn_on_stderr = true;
334 break; 346 break;
335 case 'o': /* Extra options for the ssh command */ 347 case 'o': /* Extra options for the ssh command */
336 comm_append("-o"); 348 result.config.cmd = comm_append(result.config.cmd, "-o");
337 comm_append(optarg); 349 result.config.cmd = comm_append(result.config.cmd, optarg);
338 break; 350 break;
339 case 'q': /* Tell the ssh command to be quiet */ 351 case 'q': /* Tell the ssh command to be quiet */
340 comm_append("-q"); 352 result.config.cmd = comm_append(result.config.cmd, "-q");
341 break; 353 break;
342 case 'F': /* ssh configfile */ 354 case 'F': /* ssh configfile */
343 comm_append("-F"); 355 result.config.cmd = comm_append(result.config.cmd, "-F");
344 comm_append(optarg); 356 result.config.cmd = comm_append(result.config.cmd, optarg);
345 break; 357 break;
346 default: /* help */ 358 default: /* help */
347 usage5(); 359 usage5();
348 } 360 }
349 } 361 }
350 362
351 c = optind; 363 int c = optind;
352 if (hostname == NULL) { 364 if (result.config.hostname == NULL) {
353 if (c <= argc) { 365 if (c <= argc) {
354 die(STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname); 366 die(STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
355 } 367 }
356 hostname = argv[c++]; 368 result.config.hostname = argv[c++];
357 } 369 }
358 370
359 if (strlen(remotecmd) == 0) { 371 if (strlen(result.config.remotecmd) == 0) {
360 for (; c < argc; c++) { 372 for (; c < argc; c++) {
361 if (strlen(remotecmd) > 0) { 373 if (strlen(result.config.remotecmd) > 0) {
362 xasprintf(&remotecmd, "%s %s", remotecmd, argv[c]); 374 xasprintf(&result.config.remotecmd, "%s %s", result.config.remotecmd, argv[c]);
363 } else { 375 } else {
364 xasprintf(&remotecmd, "%s", argv[c]); 376 xasprintf(&result.config.remotecmd, "%s", argv[c]);
365 } 377 }
366 } 378 }
367 } 379 }
368 380
369 if (commands > 1 || passive) { 381 if (result.config.commands > 1 || result.config.passive) {
370 xasprintf(&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); 382 xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", result.config.remotecmd);
371 } 383 }
372 384
373 if (remotecmd == NULL || strlen(remotecmd) <= 1) { 385 if (result.config.remotecmd == NULL || strlen(result.config.remotecmd) <= 1) {
374 usage_va(_("No remotecmd")); 386 usage_va(_("No remotecmd"));
375 } 387 }
376 388
377 comm_append(hostname); 389 result.config.cmd = comm_append(result.config.cmd, result.config.hostname);
378 comm_append(remotecmd); 390 result.config.cmd = comm_append(result.config.cmd, result.config.remotecmd);
379 391
380 return validate_arguments(); 392 return validate_arguments(result);
381} 393}
382 394
383void comm_append(const char *str) { 395command_construct comm_append(command_construct cmd, const char *str) {
396
397 if (verbose) {
398 for (int i = 0; i < cmd.commargc; i++) {
399 printf("Current command: [%i] %s\n", i, cmd.commargv[i]);
400 }
384 401
385 if (++commargc > NP_MAXARGS) { 402 printf("Appending: %s\n", str);
403 }
404
405 if (++cmd.commargc > NP_MAXARGS) {
386 die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS); 406 die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS);
387 } 407 }
388 408
389 if ((commargv = (char **)realloc(commargv, (commargc + 1) * sizeof(char *))) == NULL) { 409 if ((cmd.commargv = (char **)realloc(cmd.commargv, (cmd.commargc + 1) * sizeof(char *))) ==
410 NULL) {
390 die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n")); 411 die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n"));
391 } 412 }
392 413
393 commargv[commargc - 1] = strdup(str); 414 cmd.commargv[cmd.commargc - 1] = strdup(str);
394 commargv[commargc] = NULL; 415 cmd.commargv[cmd.commargc] = NULL;
416
417 return cmd;
395} 418}
396 419
397int validate_arguments(void) { 420check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper config_wrapper) {
398 if (remotecmd == NULL || hostname == NULL) { 421 if (config_wrapper.config.remotecmd == NULL || config_wrapper.config.hostname == NULL) {
399 return ERROR; 422 config_wrapper.errorcode = ERROR;
423 return config_wrapper;
400 } 424 }
401 425
402 if (passive && commands != services) { 426 if (config_wrapper.config.passive &&
403 die(STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname); 427 config_wrapper.config.commands != config_wrapper.config.number_of_services) {
428 die(STATE_UNKNOWN,
429 _("%s: In passive mode, you must provide a service name for each command.\n"),
430 progname);
404 } 431 }
405 432
406 if (passive && host_shortname == NULL) { 433 if (config_wrapper.config.passive && config_wrapper.config.host_shortname == NULL) {
407 die(STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the monitoring configs.\n"), progname); 434 die(STATE_UNKNOWN,
435 _("%s: In passive mode, you must provide the host short name from the monitoring "
436 "configs.\n"),
437 progname);
408 } 438 }
409 439
410 return OK; 440 return config_wrapper;
411} 441}
412 442
413void print_help(void) { 443void print_help(void) {
@@ -441,7 +471,8 @@ void print_help(void) {
441 printf(" %s\n", "-W, --warn-on-stderr]"); 471 printf(" %s\n", "-W, --warn-on-stderr]");
442 printf(" %s\n", _("Exit with an warning, if there is an output on STDERR")); 472 printf(" %s\n", _("Exit with an warning, if there is an output on STDERR"));
443 printf(" %s\n", "-f"); 473 printf(" %s\n", "-f");
444 printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always return OK if ssh is executed")); 474 printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always "
475 "return OK if ssh is executed"));
445 printf(" %s\n", "-C, --command='COMMAND STRING'"); 476 printf(" %s\n", "-C, --command='COMMAND STRING'");
446 printf(" %s\n", _("command to execute on the remote machine")); 477 printf(" %s\n", _("command to execute on the remote machine"));
447 printf(" %s\n", "-l, --logname=USERNAME"); 478 printf(" %s\n", "-l, --logname=USERNAME");
@@ -477,7 +508,9 @@ void print_help(void) {
477 printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); 508 printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)"));
478 printf("\n"); 509 printf("\n");
479 printf("%s\n", _("Examples:")); 510 printf("%s\n", _("Examples:"));
480 printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); 511 printf(
512 " %s\n",
513 "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo");
481 printf(" %s\n", "$ cat /tmp/foo"); 514 printf(" %s\n", "$ cat /tmp/foo");
482 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); 515 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days");
483 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); 516 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days");
diff --git a/plugins/check_by_ssh.d/config.h b/plugins/check_by_ssh.d/config.h
new file mode 100644
index 00000000..05435def
--- /dev/null
+++ b/plugins/check_by_ssh.d/config.h
@@ -0,0 +1,56 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6typedef struct {
7 int commargc;
8 char **commargv;
9} command_construct;
10
11typedef struct {
12 char *hostname;
13 char *host_shortname;
14
15 char **service;
16 unsigned int number_of_services;
17
18 unsigned int commands; // Not needed during actual test run
19 char *remotecmd;
20
21 command_construct cmd;
22
23 bool unknown_timeout;
24 bool warn_on_stderr;
25 int skip_stdout;
26 int skip_stderr;
27 bool passive;
28 char *outputfile;
29} check_by_ssh_config;
30
31check_by_ssh_config check_by_ssh_config_init() {
32 check_by_ssh_config tmp = {
33 .hostname = NULL,
34 .host_shortname = NULL,
35
36 .service = NULL,
37 .number_of_services = 0,
38
39 .commands = 0,
40 .remotecmd = "",
41
42 .cmd =
43 {
44 .commargc = 0,
45 .commargv = NULL,
46 },
47
48 .unknown_timeout = false,
49 .warn_on_stderr = false,
50 .skip_stderr = 0,
51 .skip_stdout = 0,
52 .passive = false,
53 .outputfile = NULL,
54 };
55 return tmp;
56}
diff --git a/plugins/check_cluster.c b/plugins/check_cluster.c
index b40c38c7..373520ee 100644
--- a/plugins/check_cluster.c
+++ b/plugins/check_cluster.c
@@ -29,42 +29,20 @@ const char *email = "devel@monitoring-plugins.org";
29#include "common.h" 29#include "common.h"
30#include "utils.h" 30#include "utils.h"
31#include "utils_base.h" 31#include "utils_base.h"
32 32#include "check_cluster.d/config.h"
33enum {
34 CHECK_SERVICES = 1,
35 CHECK_HOSTS = 2
36};
37 33
38static void print_help(void); 34static void print_help(void);
39void print_usage(void); 35void print_usage(void);
40 36
41static int total_services_ok = 0;
42static int total_services_warning = 0;
43static int total_services_unknown = 0;
44static int total_services_critical = 0;
45
46static int total_hosts_up = 0;
47static int total_hosts_down = 0;
48static int total_hosts_unreachable = 0;
49
50static char *warn_threshold;
51static char *crit_threshold;
52
53static int check_type = CHECK_SERVICES;
54
55static char *data_vals = NULL;
56static char *label = NULL;
57
58static int verbose = 0; 37static int verbose = 0;
59 38
60static int process_arguments(int /*argc*/, char ** /*argv*/); 39typedef struct {
40 int errorcode;
41 check_cluster_config config;
42} check_cluster_config_wrapper;
43static check_cluster_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
61 44
62int main(int argc, char **argv) { 45int 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, ""); 46 setlocale(LC_ALL, "");
69 bindtextdomain(PACKAGE, LOCALEDIR); 47 bindtextdomain(PACKAGE, LOCALEDIR);
70 textdomain(PACKAGE); 48 textdomain(PACKAGE);
@@ -72,20 +50,32 @@ int main(int argc, char **argv) {
72 /* Parse extra opts if any */ 50 /* Parse extra opts if any */
73 argv = np_extra_opts(&argc, argv, progname); 51 argv = np_extra_opts(&argc, argv, progname);
74 52
75 if (process_arguments(argc, argv) == ERROR) 53 check_cluster_config_wrapper tmp_config = process_arguments(argc, argv);
54 if (tmp_config.errorcode == ERROR) {
76 usage(_("Could not parse arguments")); 55 usage(_("Could not parse arguments"));
56 }
57
58 const check_cluster_config config = tmp_config.config;
77 59
78 /* Initialize the thresholds */ 60 /* Initialize the thresholds */
79 set_thresholds(&thresholds, warn_threshold, crit_threshold); 61 if (verbose) {
80 if (verbose) 62 print_thresholds("check_cluster", config.thresholds);
81 print_thresholds("check_cluster", thresholds); 63 }
82 64
65 int data_val;
66 int total_services_ok = 0;
67 int total_services_warning = 0;
68 int total_services_unknown = 0;
69 int total_services_critical = 0;
70 int total_hosts_up = 0;
71 int total_hosts_down = 0;
72 int total_hosts_unreachable = 0;
83 /* check the data values */ 73 /* check the data values */
84 for (ptr = strtok(data_vals, ","); ptr != NULL; ptr = strtok(NULL, ",")) { 74 for (char *ptr = strtok(config.data_vals, ","); ptr != NULL; ptr = strtok(NULL, ",")) {
85 75
86 data_val = atoi(ptr); 76 data_val = atoi(ptr);
87 77
88 if (check_type == CHECK_SERVICES) { 78 if (config.check_type == CHECK_SERVICES) {
89 switch (data_val) { 79 switch (data_val) {
90 case 0: 80 case 0:
91 total_services_ok++; 81 total_services_ok++;
@@ -119,101 +109,113 @@ int main(int argc, char **argv) {
119 } 109 }
120 } 110 }
121 111
112 int return_code = STATE_OK;
122 /* return the status of the cluster */ 113 /* return the status of the cluster */
123 if (check_type == CHECK_SERVICES) { 114 if (config.check_type == CHECK_SERVICES) {
124 return_code = get_status(total_services_warning + total_services_unknown + total_services_critical, thresholds); 115 return_code =
125 printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n", state_text(return_code), 116 get_status(total_services_warning + total_services_unknown + total_services_critical,
126 (label == NULL) ? "Service cluster" : label, total_services_ok, total_services_warning, total_services_unknown, 117 config.thresholds);
118 printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n",
119 state_text(return_code), (config.label == NULL) ? "Service cluster" : config.label,
120 total_services_ok, total_services_warning, total_services_unknown,
127 total_services_critical); 121 total_services_critical);
128 } else { 122 } else {
129 return_code = get_status(total_hosts_down + total_hosts_unreachable, thresholds); 123 return_code = get_status(total_hosts_down + total_hosts_unreachable, config.thresholds);
130 printf("CLUSTER %s: %s: %d up, %d down, %d unreachable\n", state_text(return_code), (label == NULL) ? "Host cluster" : label, 124 printf("CLUSTER %s: %s: %d up, %d down, %d unreachable\n", state_text(return_code),
131 total_hosts_up, total_hosts_down, total_hosts_unreachable); 125 (config.label == NULL) ? "Host cluster" : config.label, total_hosts_up,
126 total_hosts_down, total_hosts_unreachable);
132 } 127 }
133 128
134 return return_code; 129 exit(return_code);
135} 130}
136 131
137int process_arguments(int argc, char **argv) { 132check_cluster_config_wrapper process_arguments(int argc, char **argv) {
138 int c; 133 static struct option longopts[] = {
139 char *ptr; 134 {"data", required_argument, 0, 'd'}, {"warning", required_argument, 0, 'w'},
140 int option = 0; 135 {"critical", required_argument, 0, 'c'}, {"label", required_argument, 0, 'l'},
141 static struct option longopts[] = {{"data", required_argument, 0, 'd'}, {"warning", required_argument, 0, 'w'}, 136 {"host", no_argument, 0, 'h'}, {"service", no_argument, 0, 's'},
142 {"critical", required_argument, 0, 'c'}, {"label", required_argument, 0, 'l'}, 137 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
143 {"host", no_argument, 0, 'h'}, {"service", no_argument, 0, 's'}, 138 {"help", no_argument, 0, 'H'}, {0, 0, 0, 0}};
144 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
145 {"help", no_argument, 0, 'H'}, {0, 0, 0, 0}};
146 139
147 /* no options were supplied */ 140 check_cluster_config_wrapper result = {
148 if (argc < 2) 141 .errorcode = OK,
149 return ERROR; 142 .config = check_cluster_config_init(),
143 };
150 144
151 while (1) { 145 /* no options were supplied */
146 if (argc < 2) {
147 result.errorcode = ERROR;
148 return result;
149 }
152 150
153 c = getopt_long(argc, argv, "hHsvVw:c:d:l:", longopts, &option); 151 int option = 0;
152 char *warn_threshold = NULL;
153 char *crit_threshold = NULL;
154 while (true) {
155 int option_index = getopt_long(argc, argv, "hHsvVw:c:d:l:", longopts, &option);
154 156
155 if (c == -1 || c == EOF || c == 1) 157 if (option_index == -1 || option_index == EOF || option_index == 1) {
156 break; 158 break;
159 }
157 160
158 switch (c) { 161 switch (option_index) {
159
160 case 'h': /* host cluster */ 162 case 'h': /* host cluster */
161 check_type = CHECK_HOSTS; 163 result.config.check_type = CHECK_HOSTS;
162 break; 164 break;
163
164 case 's': /* service cluster */ 165 case 's': /* service cluster */
165 check_type = CHECK_SERVICES; 166 result.config.check_type = CHECK_SERVICES;
166 break; 167 break;
167
168 case 'w': /* warning threshold */ 168 case 'w': /* warning threshold */
169 warn_threshold = strdup(optarg); 169 warn_threshold = strdup(optarg);
170 break; 170 break;
171
172 case 'c': /* warning threshold */ 171 case 'c': /* warning threshold */
173 crit_threshold = strdup(optarg); 172 crit_threshold = strdup(optarg);
174 break; 173 break;
175
176 case 'd': /* data values */ 174 case 'd': /* data values */
177 data_vals = (char *)strdup(optarg); 175 result.config.data_vals = strdup(optarg);
178 /* validate data */ 176 /* validate data */
179 for (ptr = data_vals; ptr != NULL; ptr += 2) { 177 for (char *ptr = result.config.data_vals; ptr != NULL; ptr += 2) {
180 if (ptr[0] < '0' || ptr[0] > '3') 178 if (ptr[0] < '0' || ptr[0] > '3') {
181 return ERROR; 179 result.errorcode = ERROR;
182 if (ptr[1] == '\0') 180 return result;
181 }
182 if (ptr[1] == '\0') {
183 break; 183 break;
184 if (ptr[1] != ',') 184 }
185 return ERROR; 185 if (ptr[1] != ',') {
186 result.errorcode = ERROR;
187 return result;
188 }
186 } 189 }
187 break; 190 break;
188
189 case 'l': /* text label */ 191 case 'l': /* text label */
190 label = (char *)strdup(optarg); 192 result.config.label = strdup(optarg);
191 break; 193 break;
192
193 case 'v': /* verbose */ 194 case 'v': /* verbose */
194 verbose++; 195 verbose++;
195 break; 196 break;
196
197 case 'V': /* version */ 197 case 'V': /* version */
198 print_revision(progname, NP_VERSION); 198 print_revision(progname, NP_VERSION);
199 exit(STATE_UNKNOWN); 199 exit(STATE_UNKNOWN);
200 break; 200 break;
201
202 case 'H': /* help */ 201 case 'H': /* help */
203 print_help(); 202 print_help();
204 exit(STATE_UNKNOWN); 203 exit(STATE_UNKNOWN);
205 break; 204 break;
206
207 default: 205 default:
208 return ERROR; 206 result.errorcode = ERROR;
207 return result;
209 break; 208 break;
210 } 209 }
211 } 210 }
212 211
213 if (data_vals == NULL) 212 if (result.config.data_vals == NULL) {
214 return ERROR; 213 result.errorcode = ERROR;
214 return result;
215 }
215 216
216 return OK; 217 set_thresholds(&result.config.thresholds, warn_threshold, crit_threshold);
218 return result;
217} 219}
218 220
219void print_help(void) { 221void print_help(void) {
@@ -254,7 +256,8 @@ void print_help(void) {
254 printf("\n"); 256 printf("\n");
255 printf("%s\n", _("Examples:")); 257 printf("%s\n", _("Examples:"));
256 printf(" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:"); 258 printf(" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:");
257 printf(" %s\n", _("Will alert critical if there are 3 or more service data points in a non-OK")); 259 printf(" %s\n",
260 _("Will alert critical if there are 3 or more service data points in a non-OK"));
258 printf(" %s\n", _("state.")); 261 printf(" %s\n", _("state."));
259 262
260 printf(UT_SUPPORT); 263 printf(UT_SUPPORT);
diff --git a/plugins/check_cluster.d/config.h b/plugins/check_cluster.d/config.h
new file mode 100644
index 00000000..fc386415
--- /dev/null
+++ b/plugins/check_cluster.d/config.h
@@ -0,0 +1,27 @@
1#pragma once
2
3#include "../../config.h"
4#include "../../lib/thresholds.h"
5#include <stddef.h>
6
7enum {
8 CHECK_SERVICES = 1,
9 CHECK_HOSTS = 2
10};
11
12typedef struct {
13 char *data_vals;
14 thresholds *thresholds;
15 int check_type;
16 char *label;
17} check_cluster_config;
18
19check_cluster_config check_cluster_config_init() {
20 check_cluster_config tmp = {
21 .data_vals = NULL,
22 .thresholds = NULL,
23 .check_type = CHECK_SERVICES,
24 .label = NULL,
25 };
26 return tmp;
27}
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index 748201e8..fc704171 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -32,16 +32,23 @@
32 * 32 *
33 * 33 *
34 *****************************************************************************/ 34 *****************************************************************************/
35const char *progname = "check_curl";
36 35
36const char *progname = "check_curl";
37const char *copyright = "2006-2024"; 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
@@ -50,8 +57,6 @@ const char *email = "devel@monitoring-plugins.org";
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>
@@ -63,207 +68,62 @@ const char *email = "devel@monitoring-plugins.org";
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 MAX_IPV4_HOSTLENGTH = 255,
74 HTTP_PORT = 80,
75 HTTPS_PORT = 443,
76 MAX_PORT = 65535,
77 DEFAULT_MAX_REDIRS = 15
78}; 73};
79 74
80enum { 75enum {
81 STICKY_NONE = 0, 76 REGS = 2,
82 STICKY_HOST = 1,
83 STICKY_PORT = 2
84};
85
86enum {
87 FOLLOW_HTTP_CURL = 0,
88 FOLLOW_LIBCURL = 1
89}; 77};
90 78
91/* for buffers for header and body */ 79#include "regex.h"
92typedef struct {
93 char *buf;
94 size_t buflen;
95 size_t bufsize;
96} curlhelp_write_curlbuf;
97 80
98/* for buffering the data sent in PUT */ 81// Globals
99typedef struct { 82int verbose = 0;
100 char *buf;
101 size_t buflen;
102 off_t pos;
103} curlhelp_read_curlbuf;
104 83
105/* for parsing the HTTP status line */ 84extern char errbuf[MAX_INPUT_BUFFER];
106typedef struct { 85extern bool is_openssl_callback;
107 int http_major; /* major version of the protocol, always 1 (HTTP/0.9 86extern bool add_sslctx_verify_fun;
108 * never reached the big internet most likely) */
109 int http_minor; /* minor version of the protocol, usually 0 or 1 */
110 int http_code; /* HTTP return code as in RFC 2145 */
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 87
126enum {
127 REGS = 2,
128 MAX_RE_SIZE = 1024
129};
130#include "regex.h"
131static regex_t preg;
132static regmatch_t pmatch[REGS];
133static char regexp[MAX_RE_SIZE];
134static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
135static int errcode;
136static bool invert_regex = false;
137static int state_regex = STATE_CRITICAL;
138
139static char *server_address = NULL;
140static char *host_name = NULL;
141static char *server_url = 0;
142static struct curl_slist *server_ips = NULL;
143static bool specify_port = false;
144static unsigned short server_port = HTTP_PORT;
145static unsigned short virtual_port = 0;
146static int host_name_length;
147static char output_header_search[30] = "";
148static char output_string_search[30] = "";
149static char *warning_thresholds = NULL;
150static char *critical_thresholds = NULL;
151static int days_till_exp_warn, days_till_exp_crit;
152static thresholds *thlds;
153static char user_agent[DEFAULT_BUFFER_SIZE];
154static int verbose = 0;
155static bool show_extended_perfdata = false;
156static bool show_body = false;
157static int min_page_len = 0;
158static int max_page_len = 0;
159static int redir_depth = 0;
160static int max_depth = DEFAULT_MAX_REDIRS;
161static char *http_method = NULL;
162static char *http_post_data = NULL;
163static char *http_content_type = NULL;
164static CURL *curl;
165static bool curl_global_initialized = false;
166static bool curl_easy_initialized = false;
167static struct curl_slist *header_list = NULL;
168static bool body_buf_initialized = false;
169static curlhelp_write_curlbuf body_buf;
170static bool header_buf_initialized = false;
171static curlhelp_write_curlbuf header_buf;
172static bool status_line_initialized = false;
173static curlhelp_statusline status_line;
174static bool put_buf_initialized = false;
175static curlhelp_read_curlbuf put_buf;
176static char http_header[DEFAULT_BUFFER_SIZE];
177static long code;
178static long socket_timeout = DEFAULT_SOCKET_TIMEOUT;
179static double total_time;
180static double time_connect;
181static double time_appconnect;
182static double time_headers;
183static double time_firstbyte;
184static char errbuf[MAX_INPUT_BUFFER];
185static CURLcode res;
186static char url[DEFAULT_BUFFER_SIZE];
187static char msg[DEFAULT_BUFFER_SIZE];
188static char perfstring[DEFAULT_BUFFER_SIZE];
189static char header_expect[MAX_INPUT_BUFFER] = "";
190static char string_expect[MAX_INPUT_BUFFER] = "";
191static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
192static int server_expect_yn = 0;
193static char user_auth[MAX_INPUT_BUFFER] = "";
194static char proxy_auth[MAX_INPUT_BUFFER] = "";
195static char **http_opt_headers;
196static int http_opt_headers_count = 0;
197static bool display_html = false;
198static int onredirect = STATE_OK;
199static int followmethod = FOLLOW_HTTP_CURL;
200static int followsticky = STICKY_NONE;
201static bool use_ssl = false;
202static bool check_cert = false;
203static bool continue_after_check_cert = false;
204typedef union {
205 struct curl_slist *to_info;
206 struct curl_certinfo *to_certinfo;
207} cert_ptr_union;
208static cert_ptr_union cert_ptr;
209static int ssl_version = CURL_SSLVERSION_DEFAULT;
210static char *client_cert = NULL;
211static char *client_privkey = NULL;
212static char *ca_cert = NULL;
213static bool verify_peer_and_host = false;
214static bool is_openssl_callback = false;
215static bool add_sslctx_verify_fun = false;
216#if defined(HAVE_SSL) && defined(USE_OPENSSL) 88#if defined(HAVE_SSL) && defined(USE_OPENSSL)
217static X509 *cert = NULL; 89static X509 *cert = NULL;
218#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ 90#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
219static bool no_body = false; 91
220static int maximum_age = -1; 92typedef struct {
221static int address_family = AF_UNSPEC; 93 int errorcode;
222static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; 94 check_curl_config config;
223static int curl_http_version = CURL_HTTP_VERSION_NONE; 95} check_curl_config_wrapper;
224static bool automatic_decompression = false; 96static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
225static char *cookie_jar_file = NULL; 97
226static bool haproxy_protocol = false; 98static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState,
227 99 int redir_depth);
228static bool process_arguments(int /*argc*/, char ** /*argv*/); 100
229static void handle_curl_option_return_code(CURLcode res, const char *option); 101typedef struct {
230static int check_http(void); 102 int redir_depth;
231static void redir(curlhelp_write_curlbuf * /*header_buf*/); 103 check_curl_working_state working_state;
232static char *perfd_time(double elapsed_time); 104 int error_code;
233static char *perfd_time_connect(double elapsed_time_connect); 105 check_curl_global_state curl_state;
234static char *perfd_time_ssl(double elapsed_time_ssl); 106} redir_wrapper;
235static char *perfd_time_firstbyte(double elapsed_time_firstbyte); 107static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/,
236static char *perfd_time_headers(double elapsed_time_headers); 108 int redir_depth, check_curl_working_state working_state);
237static char *perfd_time_transfer(double elapsed_time_transfer); 109
238static char *perfd_size(int page_len);
239static void print_help(void); 110static void print_help(void);
240void print_usage(void); 111void print_usage(void);
112
241static void print_curl_version(void); 113static void print_curl_version(void);
242static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); 114
243static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); 115// typedef struct {
244static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); 116// int errorcode;
245static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, size_t /*datalen*/); 117// } check_curl_evaluation_wrapper;
246static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); 118// check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config,
247static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); 119// mp_check overall[static 1]) {}
248static curlhelp_ssl_library curlhelp_get_ssl_library(void);
249static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/);
250int net_noopenssl_check_certificate(cert_ptr_union *, int, int);
251
252static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/);
253static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/);
254static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header);
255static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE]);
256static int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf);
257 120
258#if defined(HAVE_SSL) && defined(USE_OPENSSL) 121#if defined(HAVE_SSL) && defined(USE_OPENSSL)
259int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); 122mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
123 int days_till_exp_crit);
260#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ 124#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
261 125
262static void test_file(char * /*path*/);
263
264int main(int argc, char **argv) { 126int main(int argc, char **argv) {
265 int result = STATE_UNKNOWN;
266
267 setlocale(LC_ALL, ""); 127 setlocale(LC_ALL, "");
268 bindtextdomain(PACKAGE, LOCALEDIR); 128 bindtextdomain(PACKAGE, LOCALEDIR);
269 textdomain(PACKAGE); 129 textdomain(PACKAGE);
@@ -271,24 +131,30 @@ int main(int argc, char **argv) {
271 /* Parse extra opts if any */ 131 /* Parse extra opts if any */
272 argv = np_extra_opts(&argc, argv, progname); 132 argv = np_extra_opts(&argc, argv, progname);
273 133
274 /* set defaults */
275 snprintf(user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version());
276
277 /* parse arguments */ 134 /* parse arguments */
278 if (process_arguments(argc, argv) == false) 135 check_curl_config_wrapper tmp_config = process_arguments(argc, argv);
136 if (tmp_config.errorcode == ERROR) {
279 usage4(_("Could not parse arguments")); 137 usage4(_("Could not parse arguments"));
138 }
280 139
281 if (display_html) 140 const check_curl_config config = tmp_config.config;
282 printf("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", use_ssl ? "https" : "http", host_name ? host_name : server_address,
283 virtual_port ? virtual_port : server_port, server_url);
284 141
285 result = check_http(); 142 if (config.output_format_is_set) {
286 return result; 143 mp_set_format(config.output_format);
144 }
145
146 check_curl_working_state working_state = config.initial_config;
147
148 mp_check overall = mp_check_init();
149 mp_subcheck sc_test = check_http(config, working_state, 0);
150
151 mp_add_subcheck_to_check(&overall, sc_test);
152
153 mp_exit(overall);
287} 154}
288 155
289#ifdef HAVE_SSL 156#ifdef HAVE_SSL
290# ifdef USE_OPENSSL 157# ifdef USE_OPENSSL
291
292int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { 158int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
293 (void)preverify_ok; 159 (void)preverify_ok;
294 /* TODO: we get all certificates of the chain, so which ones 160 /* TODO: we get all certificates of the chain, so which ones
@@ -301,19 +167,21 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
301# endif 167# endif
302 if (verbose >= 2) { 168 if (verbose >= 2) {
303 puts("* SSL verify callback with certificate:"); 169 puts("* SSL verify callback with certificate:");
304 X509_NAME *subject;
305 X509_NAME *issuer;
306 printf("* issuer:\n"); 170 printf("* issuer:\n");
307 issuer = X509_get_issuer_name(cert); 171 X509_NAME *issuer = X509_get_issuer_name(cert);
308 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); 172 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE);
309 printf("* curl verify_callback:\n* subject:\n"); 173 printf("* curl verify_callback:\n* subject:\n");
310 subject = X509_get_subject_name(cert); 174 X509_NAME *subject = X509_get_subject_name(cert);
311 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); 175 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE);
312 puts(""); 176 puts("");
313 } 177 }
314 return 1; 178 return 1;
315} 179}
180# endif /* USE_OPENSSL */
181#endif /* HAVE_SSL */
316 182
183#ifdef HAVE_SSL
184# ifdef USE_OPENSSL
317CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { 185CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
318 (void)curl; // ignore unused parameter 186 (void)curl; // ignore unused parameter
319 (void)parm; // ignore unused parameter 187 (void)parm; // ignore unused parameter
@@ -330,877 +198,503 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
330 198
331 return CURLE_OK; 199 return CURLE_OK;
332} 200}
333
334# endif /* USE_OPENSSL */ 201# endif /* USE_OPENSSL */
335#endif /* HAVE_SSL */ 202#endif /* HAVE_SSL */
336 203
337/* returns a string "HTTP/1.x" or "HTTP/2" */ 204mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState,
338static char *string_statuscode(int major, int minor) { 205 int redir_depth) {
339 static char buf[10];
340
341 switch (major) {
342 case 1:
343 snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor);
344 break;
345 case 2:
346 case 3:
347 snprintf(buf, sizeof(buf), "HTTP/%d", major);
348 break;
349 default:
350 /* assuming here HTTP/N with N>=4 */
351 snprintf(buf, sizeof(buf), "HTTP/%d", major);
352 break;
353 }
354 206
355 return buf; 207 // =======================
356} 208 // Initialisation for curl
209 // =======================
210 check_curl_configure_curl_wrapper conf_curl_struct = check_curl_configure_curl(
211 config.curl_config, workingState, config.check_cert, config.on_redirect_dependent,
212 config.followmethod, config.max_depth);
357 213
358/* Checks if the server 'reply' is one of the expected 'statuscodes' */ 214 check_curl_global_state curl_state = conf_curl_struct.curl_state;
359static int expected_statuscode(const char *reply, const char *statuscodes) { 215 workingState = conf_curl_struct.working_state;
360 char *expected;
361 char *code;
362 int result = 0;
363 216
364 if ((expected = strdup(statuscodes)) == NULL) 217 mp_subcheck sc_result = mp_subcheck_init();
365 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
366 218
367 for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) 219 char *url = fmt_url(workingState);
368 if (strstr(reply, code) != NULL) { 220 xasprintf(&sc_result.output, "Testing %s", url);
369 result = 1; 221 // TODO add some output here URL or something
370 break; 222 free(url);
371 }
372
373 free(expected);
374 return result;
375}
376 223
377void handle_curl_option_return_code(CURLcode res, const char *option) { 224 // ==============
378 if (res != CURLE_OK) { 225 // do the request
379 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, 226 // ==============
380 curl_easy_strerror(res)); 227 CURLcode res = curl_easy_perform(curl_state.curl);
381 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
382 }
383}
384
385int lookup_host(const char *host, char *buf, size_t buflen) {
386 struct addrinfo hints, *res, *result;
387 char addrstr[100];
388 size_t addrstr_len;
389 int errcode;
390 void *ptr = {0};
391 size_t buflen_remaining = buflen - 1;
392
393 memset(&hints, 0, sizeof(hints));
394 hints.ai_family = address_family;
395 hints.ai_socktype = SOCK_STREAM;
396 hints.ai_flags |= AI_CANONNAME;
397
398 errcode = getaddrinfo(host, NULL, &hints, &result);
399 if (errcode != 0)
400 return errcode;
401
402 strcpy(buf, "");
403 res = result;
404
405 while (res) {
406 switch (res->ai_family) {
407 case AF_INET:
408 ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
409 break;
410 case AF_INET6:
411 ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
412 break;
413 }
414
415 inet_ntop(res->ai_family, ptr, addrstr, 100);
416 if (verbose >= 1) {
417 printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr);
418 }
419
420 // Append all IPs to buf as a comma-separated string
421 addrstr_len = strlen(addrstr);
422 if (buflen_remaining > addrstr_len + 1) {
423 if (buf[0] != '\0') {
424 strncat(buf, ",", buflen_remaining);
425 buflen_remaining -= 1;
426 }
427 strncat(buf, addrstr, buflen_remaining);
428 buflen_remaining -= addrstr_len;
429 }
430
431 res = res->ai_next;
432 }
433
434 freeaddrinfo(result);
435
436 return 0;
437}
438
439static void cleanup(void) {
440 if (status_line_initialized)
441 curlhelp_free_statusline(&status_line);
442 status_line_initialized = false;
443 if (curl_easy_initialized)
444 curl_easy_cleanup(curl);
445 curl_easy_initialized = false;
446 if (curl_global_initialized)
447 curl_global_cleanup();
448 curl_global_initialized = false;
449 if (body_buf_initialized)
450 curlhelp_freewritebuffer(&body_buf);
451 body_buf_initialized = false;
452 if (header_buf_initialized)
453 curlhelp_freewritebuffer(&header_buf);
454 header_buf_initialized = false;
455 if (put_buf_initialized)
456 curlhelp_freereadbuffer(&put_buf);
457 put_buf_initialized = false;
458}
459 228
460int check_http(void) { 229 if (verbose >= 2 && workingState.http_post_data) {
461 int result = STATE_OK; 230 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data);
462 int result_ssl = STATE_OK;
463 int page_len = 0;
464 int i;
465 char *force_host_header = NULL;
466 struct curl_slist *host = NULL;
467 char addrstr[DEFAULT_BUFFER_SIZE / 2];
468 char dnscache[DEFAULT_BUFFER_SIZE];
469
470 /* initialize curl */
471 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
472 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
473 curl_global_initialized = true;
474
475 if ((curl = curl_easy_init()) == NULL) {
476 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
477 } 231 }
478 curl_easy_initialized = true;
479 232
480 /* register cleanup function to shut down libcurl properly */ 233 mp_subcheck sc_curl = mp_subcheck_init();
481 atexit(cleanup);
482 234
483 if (verbose >= 1) 235 /* Curl errors, result in critical Nagios state */
484 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); 236 if (res != CURLE_OK) {
485 237 xasprintf(&sc_curl.output, _("Error while performing connection: cURL returned %d - %s"),
486 /* print everything on stdout like check_http would do */ 238 res, errbuf[0] ? errbuf : curl_easy_strerror(res));
487 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); 239 sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL);
488 240 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
489 if (automatic_decompression) 241 return sc_result;
490#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
491 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING");
492#else
493 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
494#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
495
496 /* initialize buffer for body of the answer */
497 if (curlhelp_initwritebuffer(&body_buf) < 0)
498 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
499 body_buf_initialized = true;
500 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback),
501 "CURLOPT_WRITEFUNCTION");
502 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA");
503
504 /* initialize buffer for header of the answer */
505 if (curlhelp_initwritebuffer(&header_buf) < 0)
506 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n");
507 header_buf_initialized = true;
508 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback),
509 "CURLOPT_HEADERFUNCTION");
510 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER");
511
512 /* set the error buffer */
513 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER");
514
515 /* set timeouts */
516 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT");
517 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT");
518
519 /* enable haproxy protocol */
520 if (haproxy_protocol) {
521 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL");
522 } 242 }
523 243
524 // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we 244 /* get status line of answer, check sanity of HTTP code */
525 // use the host_name later on to make SNI happy 245 if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) {
526 if (use_ssl && host_name != NULL) { 246 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
527 if ((res = lookup_host(server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { 247 /* we cannot know the major/minor version here for sure as we cannot parse the first
528 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), server_address, res, 248 * line */
529 gai_strerror(res)); 249 xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line");
530 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); 250 return sc_result;
531 }
532 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr);
533 host = curl_slist_append(NULL, dnscache);
534 curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
535 if (verbose >= 1)
536 printf("* curl CURLOPT_RESOLVE: %s\n", dnscache);
537 } 251 }
538 252
539 // If server_address is an IPv6 address it must be surround by square brackets 253 curl_state.status_line_initialized = true;
540 struct in6_addr tmp_in_addr;
541 if (inet_pton(AF_INET6, server_address, &tmp_in_addr) == 1) {
542 char *new_server_address = malloc(strlen(server_address) + 3);
543 if (new_server_address == NULL) {
544 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
545 }
546 snprintf(new_server_address, strlen(server_address) + 3, "[%s]", server_address);
547 free(server_address);
548 server_address = new_server_address;
549 }
550 254
551 /* compose URL: use the address we want to connect to, set Host: header later */ 255 size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf);
552 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", use_ssl ? "https" : "http",
553 (use_ssl & (host_name != NULL)) ? host_name : server_address, server_port, server_url);
554
555 if (verbose >= 1)
556 printf("* curl CURLOPT_URL: %s\n", url);
557 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL");
558
559 /* extract proxy information for legacy proxy https requests */
560 if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) {
561 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY");
562 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT");
563 if (verbose >= 2)
564 printf("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port);
565 http_method = "GET";
566 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, server_url), "CURLOPT_URL");
567 }
568 256
569 /* disable body for HEAD request */ 257 double total_time;
570 if (http_method && !strcmp(http_method, "HEAD")) { 258 handle_curl_option_return_code(
571 no_body = true; 259 curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time),
572 } 260 "CURLINFO_TOTAL_TIME");
573 261
574 /* set HTTP protocol version */ 262 xasprintf(
575 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION"); 263 &sc_curl.output, "%s %d %s - %ld bytes in %.3f second response time",
576 264 string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor),
577 /* set HTTP method */ 265 curl_state.status_line->http_code, curl_state.status_line->msg, page_len, total_time);
578 if (http_method) { 266 sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK);
579 if (!strcmp(http_method, "POST")) 267 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
580 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST");
581 else if (!strcmp(http_method, "PUT"))
582 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
583 else
584 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST");
585 }
586 268
587 /* check if Host header is explicitly set in options */ 269 // ==========
588 if (http_opt_headers_count) { 270 // Evaluation
589 for (i = 0; i < http_opt_headers_count; i++) { 271 // ==========
590 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
591 force_host_header = http_opt_headers[i];
592 }
593 }
594 }
595
596 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */
597 if (host_name != NULL && force_host_header == NULL) {
598 if ((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) {
599 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port);
600 } else {
601 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name);
602 }
603 header_list = curl_slist_append(header_list, http_header);
604 }
605
606 /* always close connection, be nice to servers */
607 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
608 header_list = curl_slist_append(header_list, http_header);
609
610 /* attach additional headers supplied by the user */
611 /* optionally send any other header tag */
612 if (http_opt_headers_count) {
613 for (i = 0; i < http_opt_headers_count; i++) {
614 header_list = curl_slist_append(header_list, http_opt_headers[i]);
615 }
616 /* This cannot be free'd here because a redirection will then try to access this and segfault */
617 /* Covered in a testcase in tests/check_http.t */
618 /* free(http_opt_headers); */
619 }
620
621 /* set HTTP headers */
622 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER");
623 272
624#ifdef LIBCURL_FEATURE_SSL 273#ifdef LIBCURL_FEATURE_SSL
274 if (workingState.use_ssl && config.check_cert) {
275 mp_subcheck sc_certificate = check_curl_certificate_checks(
276 curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit);
625 277
626 /* set SSL version, warn about insecure or unsupported versions */ 278 mp_add_subcheck_to_subcheck(&sc_result, sc_certificate);
627 if (use_ssl) { 279 if (!config.continue_after_check_cert) {
628 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION"); 280 return sc_result;
629 }
630
631 /* client certificate and key to present to server (SSL) */
632 if (client_cert)
633 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT");
634 if (client_privkey)
635 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
636 if (ca_cert) {
637 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
638 }
639 if (ca_cert || verify_peer_and_host) {
640 /* per default if we have a CA verify both the peer and the
641 * hostname in the certificate, can be switched off later */
642 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
643 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
644 } else {
645 /* backward-compatible behaviour, be tolerant in checks
646 * TODO: depending on more options have aspects we want
647 * to be less tolerant about ssl verfications
648 */
649 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER");
650 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST");
651 }
652
653 /* detect SSL library used by libcurl */
654 ssl_library = curlhelp_get_ssl_library();
655
656 /* try hard to get a stack of certificates to verify against */
657 if (check_cert) {
658# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
659 /* inform curl to report back certificates */
660 switch (ssl_library) {
661 case CURLHELP_SSL_LIBRARY_OPENSSL:
662 case CURLHELP_SSL_LIBRARY_LIBRESSL:
663 /* set callback to extract certificate with OpenSSL context function (works with
664 * OpenSSL-style libraries only!) */
665# ifdef USE_OPENSSL
666 /* libcurl and monitoring plugins built with OpenSSL, good */
667 add_sslctx_verify_fun = true;
668 is_openssl_callback = true;
669# endif /* USE_OPENSSL */
670 /* libcurl is built with OpenSSL, monitoring plugins, so falling
671 * back to manually extracting certificate information */
672 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
673 break;
674
675 case CURLHELP_SSL_LIBRARY_NSS:
676# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
677 /* NSS: support for CERTINFO is implemented since 7.34.0 */
678 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
679# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
680 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n",
681 curlhelp_get_ssl_library_string(ssl_library));
682# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
683 break;
684
685 case CURLHELP_SSL_LIBRARY_GNUTLS:
686# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
687 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
688 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
689# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
690 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n",
691 curlhelp_get_ssl_library_string(ssl_library));
692# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
693 break;
694
695 case CURLHELP_SSL_LIBRARY_UNKNOWN:
696 default:
697 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n",
698 curlhelp_get_ssl_library_string(ssl_library));
699 break;
700 } 281 }
701# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
702 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
703 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL)
704 add_sslctx_verify_fun = true;
705 else
706 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl "
707 "too old and has no CURLOPT_CERTINFO)\n");
708# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
709 } 282 }
710
711# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */
712 // ssl ctx function is not available with all ssl backends
713 if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION)
714 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
715# endif
716
717#endif /* LIBCURL_FEATURE_SSL */
718
719 /* set default or user-given user agent identification */
720 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT");
721
722 /* proxy-authentication */
723 if (strcmp(proxy_auth, ""))
724 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD");
725
726 /* authentication */
727 if (strcmp(user_auth, ""))
728 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD");
729
730 /* TODO: parameter auth method, bitfield of following methods:
731 * CURLAUTH_BASIC (default)
732 * CURLAUTH_DIGEST
733 * CURLAUTH_DIGEST_IE
734 * CURLAUTH_NEGOTIATE
735 * CURLAUTH_NTLM
736 * CURLAUTH_NTLM_WB
737 *
738 * convenience tokens for typical sets of methods:
739 * CURLAUTH_ANYSAFE: most secure, without BASIC
740 * or CURLAUTH_ANY: most secure, even BASIC if necessary
741 *
742 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
743 */
744
745 /* handle redirections */
746 if (onredirect == STATE_DEPENDENT) {
747 if (followmethod == FOLLOW_LIBCURL) {
748 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
749
750 /* default -1 is infinite, not good, could lead to zombie plugins!
751 Setting it to one bigger than maximal limit to handle errors nicely below
752 */
753 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, max_depth + 1), "CURLOPT_MAXREDIRS");
754
755 /* for now allow only http and https (we are a http(s) check plugin in the end) */
756#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
757 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"),
758 "CURLOPT_REDIR_PROTOCOLS_STR");
759#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
760 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS),
761 "CURLOPT_REDIRECT_PROTOCOLS");
762#endif 283#endif
763 284
764 /* TODO: handle the following aspects of redirection, make them 285 /* we got the data and we executed the request in a given time, so we can append
765 * command line options too later: 286 * performance data to the answer always
766 CURLOPT_POSTREDIR: method switch 287 */
767 CURLINFO_REDIRECT_URL: custom redirect option
768 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
769 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size?
770 */
771 } else {
772 /* old style redirection is handled below */
773 }
774 }
775
776 /* no-body */
777 if (no_body)
778 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY");
779
780 /* IPv4 or IPv6 forced DNS resolution */
781 if (address_family == AF_UNSPEC)
782 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER),
783 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
784 else if (address_family == AF_INET)
785 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
786 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
787#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
788 else if (address_family == AF_INET6)
789 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
790 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
791#endif
792 288
793 /* either send http POST data (any data, not only POST)*/ 289 // total time the query took
794 if (!strcmp(http_method, "POST") || !strcmp(http_method, "PUT")) { 290 mp_perfdata pd_total_time = perfdata_init();
795 /* set content of payload for POST and PUT */ 291 mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time);
796 if (http_content_type) { 292 pd_total_time.value = pd_val_total_time;
797 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type); 293 pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds);
798 header_list = curl_slist_append(header_list, http_header); 294 pd_total_time.label = "time";
799 } 295 pd_total_time.uom = "s";
800 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string 296
801 * in case of no POST/PUT data */ 297 mp_subcheck sc_total_time = mp_subcheck_init();
802 if (!http_post_data) 298 sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time));
803 http_post_data = ""; 299 xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time);
804 if (!strcmp(http_method, "POST")) { 300 mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time);
805 /* POST method, set payload with CURLOPT_POSTFIELDS */ 301
806 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS"); 302 mp_add_subcheck_to_subcheck(&sc_result, sc_total_time);
807 } else if (!strcmp(http_method, "PUT")) { 303
808 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), 304 if (config.show_extended_perfdata) {
809 "CURLOPT_READFUNCTION"); 305 // overall connection time
810 if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) 306 mp_perfdata pd_time_connect = perfdata_init();
811 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); 307 double time_connect;
812 put_buf_initialized = true; 308 handle_curl_option_return_code(
813 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); 309 curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect),
814 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(http_post_data)), 310 "CURLINFO_CONNECT_TIME");
815 "CURLOPT_INFILESIZE"); 311
312 mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect);
313 pd_time_connect.value = pd_val_time_connect;
314 pd_time_connect.label = "time_connect";
315 pd_time_connect.uom = "s";
316 pd_time_connect = mp_set_pd_max_value(
317 pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout));
318
319 pd_time_connect = mp_pd_set_thresholds(pd_time_connect, config.thlds);
320 mp_add_perfdata_to_subcheck(&sc_result, pd_time_connect);
321
322 // application connection time, used to compute other timings
323 double time_appconnect;
324 handle_curl_option_return_code(
325 curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect),
326 "CURLINFO_APPCONNECT_TIME");
327
328 if (workingState.use_ssl) {
329 mp_perfdata pd_time_tls = perfdata_init();
330 {
331 mp_perfdata_value pd_val_time_tls =
332 mp_create_pd_value(time_appconnect - time_connect);
333
334 pd_time_tls.value = pd_val_time_tls;
335 }
336 pd_time_tls.label = "time_tls";
337 pd_time_tls.uom = "s";
338 mp_add_perfdata_to_subcheck(&sc_result, pd_time_tls);
816 } 339 }
817 }
818
819 /* cookie handling */
820 if (cookie_jar_file != NULL) {
821 /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */
822 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE");
823 /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */
824 if (*cookie_jar_file)
825 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR");
826 }
827
828 /* do the request */
829 res = curl_easy_perform(curl);
830 340
831 if (verbose >= 2 && http_post_data) 341 mp_perfdata pd_time_headers = perfdata_init();
832 printf("**** REQUEST CONTENT ****\n%s\n", http_post_data); 342 {
343 double time_headers;
344 handle_curl_option_return_code(
345 curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers),
346 "CURLINFO_PRETRANSFER_TIME");
833 347
834 /* free header and server IP resolve lists, we don't need it anymore */ 348 mp_perfdata_value pd_val_time_headers =
835 curl_slist_free_all(header_list); 349 mp_create_pd_value(time_headers - time_appconnect);
836 header_list = NULL;
837 curl_slist_free_all(server_ips);
838 server_ips = NULL;
839 if (host) {
840 curl_slist_free_all(host);
841 host = NULL;
842 }
843 350
844 /* Curl errors, result in critical Nagios state */ 351 pd_time_headers.value = pd_val_time_headers;
845 if (res != CURLE_OK) { 352 }
846 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), server_port, 353 pd_time_headers.label = "time_headers";
847 res, errbuf[0] ? errbuf : curl_easy_strerror(res)); 354 pd_time_headers.uom = "s";
848 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); 355 mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers);
849 }
850 356
851 /* certificate checks */ 357 mp_perfdata pd_time_firstbyte = perfdata_init();
852#ifdef LIBCURL_FEATURE_SSL 358 double time_firstbyte;
853 if (use_ssl) { 359 handle_curl_option_return_code(
854 if (check_cert) { 360 curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
855 if (is_openssl_callback) { 361 "CURLINFO_STARTTRANSFER_TIME");
856# ifdef USE_OPENSSL
857 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
858 * and we actually have OpenSSL in the monitoring tools
859 */
860 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
861 if (!continue_after_check_cert) {
862 return result_ssl;
863 }
864# else /* USE_OPENSSL */
865 die(STATE_CRITICAL,
866 "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n");
867# endif /* USE_OPENSSL */
868 } else {
869 int i;
870 struct curl_slist *slist;
871 362
872 cert_ptr.to_info = NULL; 363 mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte);
873 res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); 364 pd_time_firstbyte.value = pd_val_time_firstbyte;
874 if (!res && cert_ptr.to_info) { 365 pd_time_firstbyte.label = "time_firstbyte";
875# ifdef USE_OPENSSL 366 pd_time_firstbyte.uom = "s";
876 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing 367 mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte);
877 * We only check the first certificate and assume it's the one of the server
878 */
879 const char *raw_cert = NULL;
880 for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
881 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
882 if (verbose >= 2)
883 printf("%d ** %s\n", i, slist->data);
884 if (strncmp(slist->data, "Cert:", 5) == 0) {
885 raw_cert = &slist->data[5];
886 goto GOT_FIRST_CERT;
887 }
888 }
889 }
890 GOT_FIRST_CERT:
891 if (!raw_cert) {
892 snprintf(msg, DEFAULT_BUFFER_SIZE,
893 _("Cannot retrieve certificates from CERTINFO information - certificate data was empty"));
894 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
895 }
896 BIO *cert_BIO = BIO_new(BIO_s_mem());
897 BIO_write(cert_BIO, raw_cert, strlen(raw_cert));
898 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
899 if (!cert) {
900 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error"));
901 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
902 }
903 BIO_free(cert_BIO);
904 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
905 if (!continue_after_check_cert) {
906 return result_ssl;
907 }
908# else /* USE_OPENSSL */
909 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal,
910 * so we use the libcurl CURLINFO data
911 */
912 result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit);
913 if (!continue_after_check_cert) {
914 return result_ssl;
915 }
916# endif /* USE_OPENSSL */
917 } else {
918 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"), res,
919 curl_easy_strerror(res));
920 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
921 }
922 }
923 }
924 }
925#endif /* LIBCURL_FEATURE_SSL */
926 368
927 /* we got the data and we executed the request in a given time, so we can append 369 mp_perfdata pd_time_transfer = perfdata_init();
928 * performance data to the answer always 370 pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte);
929 */ 371 pd_time_transfer.label = "time_transfer";
930 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); 372 pd_time_transfer.uom = "s";
931 page_len = get_content_length(&header_buf, &body_buf); 373 mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer);
932 if (show_extended_perfdata) {
933 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME");
934 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME");
935 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME");
936 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
937 "CURLINFO_STARTTRANSFER_TIME");
938 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time), perfd_size(page_len),
939 perfd_time_connect(time_connect), use_ssl ? perfd_time_ssl(time_appconnect - time_connect) : "",
940 perfd_time_headers(time_headers - time_appconnect), perfd_time_firstbyte(time_firstbyte - time_headers),
941 perfd_time_transfer(total_time - time_firstbyte));
942 } else {
943 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time), perfd_size(page_len));
944 } 374 }
945 375
946 /* return a CRITICAL status if we couldn't read any data */ 376 /* return a CRITICAL status if we couldn't read any data */
947 if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) 377 if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) {
948 die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); 378 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
949 379 xasprintf(&sc_result.output, "No header received from host");
950 /* get status line of answer, check sanity of HTTP code */ 380 return sc_result;
951 if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) {
952 snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring);
953 /* we cannot know the major/minor version here for sure as we cannot parse the first line */
954 die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg);
955 } 381 }
956 status_line_initialized = true;
957 382
958 /* get result code from cURL */ 383 /* get result code from cURL */
959 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE"); 384 long httpReturnCode;
960 if (verbose >= 2) 385 handle_curl_option_return_code(
961 printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); 386 curl_easy_getinfo(curl_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode),
387 "CURLINFO_RESPONSE_CODE");
388 if (verbose >= 2) {
389 printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode);
390 }
962 391
963 /* print status line, header, body if verbose */ 392 /* print status line, header, body if verbose */
964 if (verbose >= 2) { 393 if (verbose >= 2) {
965 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (no_body ? " [[ skipped ]]" : body_buf.buf)); 394 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf->buf,
395 (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf->buf));
966 } 396 }
967 397
968 /* make sure the status line matches the response we are looking for */ 398 /* make sure the status line matches the response we are looking for */
969 if (!expected_statuscode(status_line.first_line, server_expect)) { 399 mp_subcheck sc_expect = mp_subcheck_init();
970 if (server_port == HTTP_PORT) 400 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK);
971 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); 401 if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) {
972 else 402 if (workingState.serverPort == HTTP_PORT) {
973 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, 403 xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"),
974 status_line.first_line); 404 curl_state.status_line->first_line);
975 die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, show_body ? "\n" : "", show_body ? body_buf.buf : ""); 405 } else {
406 xasprintf(&sc_expect.output,
407 _("Invalid HTTP response received from host on port %d: %s\n"),
408 workingState.serverPort, curl_state.status_line->first_line);
409 }
410 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_CRITICAL);
411 } else {
412 xasprintf(&sc_expect.output, _("Status line output matched \"%s\""),
413 config.server_expect.string);
976 } 414 }
415 mp_add_subcheck_to_subcheck(&sc_result, sc_expect);
977 416
978 if (server_expect_yn) { 417 if (!config.server_expect.is_present) {
979 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect);
980 if (verbose)
981 printf("%s\n", msg);
982 result = STATE_OK;
983 } else {
984 /* illegal return codes result in a critical state */ 418 /* illegal return codes result in a critical state */
985 if (code >= 600 || code < 100) { 419 mp_subcheck sc_return_code = mp_subcheck_init();
986 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg); 420 sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK);
987 /* server errors result in a critical state */ 421 xasprintf(&sc_return_code.output, "HTTP return code: %d",
988 } else if (code >= 500) { 422 curl_state.status_line->http_code);
989 result = STATE_CRITICAL; 423
424 if (httpReturnCode >= 600 || httpReturnCode < 100) {
425 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
426 xasprintf(&sc_return_code.output, _("Invalid Status (%d, %.40s)"),
427 curl_state.status_line->http_code, curl_state.status_line->msg);
428 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
429 return sc_result;
430 }
431
432 // server errors result in a critical state
433 if (httpReturnCode >= 500) {
434 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
990 /* client errors result in a warning state */ 435 /* client errors result in a warning state */
991 } else if (code >= 400) { 436 } else if (httpReturnCode >= 400) {
992 result = STATE_WARNING; 437 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING);
993 /* check redirected page if specified */ 438 /* check redirected page if specified */
994 } else if (code >= 300) { 439 } else if (httpReturnCode >= 300) {
995 if (onredirect == STATE_DEPENDENT) { 440 if (config.on_redirect_dependent) {
996 if (followmethod == FOLLOW_LIBCURL) { 441 if (config.followmethod == FOLLOW_LIBCURL) {
997 code = status_line.http_code; 442 httpReturnCode = curl_state.status_line->http_code;
443 handle_curl_option_return_code(
444 curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth),
445 "CURLINFO_REDIRECT_COUNT");
446
447 if (verbose >= 2) {
448 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
449 }
450
451 mp_subcheck sc_redir_depth = mp_subcheck_init();
452 if (redir_depth > config.max_depth) {
453 xasprintf(&sc_redir_depth.output,
454 "maximum redirection depth %d exceeded in libcurl",
455 config.max_depth);
456 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL);
457 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
458 return sc_result;
459 }
460 xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)",
461 redir_depth, config.max_depth);
462 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
463
998 } else { 464 } else {
999 /* old check_http style redirection, if we come 465 /* old check_http style redirection, if we come
1000 * back here, we are in the same status as with 466 * back here, we are in the same status as with
1001 * the libcurl method 467 * the libcurl method
1002 */ 468 */
1003 redir(&header_buf); 469 redir_wrapper redir_result =
470 redir(curl_state.header_buf, config, redir_depth, workingState);
471 cleanup(curl_state);
472 mp_subcheck sc_redir =
473 check_http(config, redir_result.working_state, redir_result.redir_depth);
474 mp_add_subcheck_to_subcheck(&sc_result, sc_redir);
475
476 return sc_result;
1004 } 477 }
1005 } else { 478 } else {
1006 /* this is a specific code in the command line to 479 /* this is a specific code in the command line to
1007 * be returned when a redirection is encountered 480 * be returned when a redirection is encountered
1008 */ 481 */
482 sc_return_code =
483 mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state);
1009 } 484 }
1010 result = max_state_alt(onredirect, result);
1011 /* all other codes are considered ok */
1012 } else { 485 } else {
1013 result = STATE_OK; 486 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK);
1014 } 487 }
1015 }
1016 488
1017 /* libcurl redirection internally, handle error states here */ 489 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
1018 if (followmethod == FOLLOW_LIBCURL) {
1019 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT");
1020 if (verbose >= 2)
1021 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
1022 if (redir_depth > max_depth) {
1023 snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", max_depth);
1024 die(STATE_WARNING, "HTTP WARNING - %s", msg);
1025 }
1026 } 490 }
1027 491
1028 /* check status codes, set exit status accordingly */ 492 /* check status codes, set exit status accordingly */
1029 if (status_line.http_code != code) { 493 if (curl_state.status_line->http_code != httpReturnCode) {
1030 die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), 494 mp_subcheck sc_http_return_code_sanity = mp_subcheck_init();
1031 string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, code); 495 sc_http_return_code_sanity =
496 mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL);
497 xasprintf(&sc_http_return_code_sanity.output,
498 _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"),
499 string_statuscode(curl_state.status_line->http_major,
500 curl_state.status_line->http_minor),
501 curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode);
502
503 mp_add_subcheck_to_subcheck(&sc_result, sc_http_return_code_sanity);
504 return sc_result;
1032 } 505 }
1033 506
1034 if (maximum_age >= 0) { 507 if (config.maximum_age >= 0) {
1035 result = max_state_alt(check_document_dates(&header_buf, &msg), result); 508 mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age);
509 mp_add_subcheck_to_subcheck(&sc_result, sc_max_age);
1036 } 510 }
1037 511
1038 /* Page and Header content checks go here */ 512 /* Page and Header content checks go here */
513 if (strlen(config.header_expect)) {
514 mp_subcheck sc_header_expect = mp_subcheck_init();
515 sc_header_expect = mp_set_subcheck_default_state(sc_header_expect, STATE_OK);
516 xasprintf(&sc_header_expect.output, "Expect %s in header", config.header_expect);
1039 517
1040 if (strlen(header_expect)) { 518 if (!strstr(curl_state.header_buf->buf, config.header_expect)) {
1041 if (!strstr(header_buf.buf, header_expect)) { 519 char output_header_search[30] = "";
1042 520 strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search));
1043 strncpy(&output_header_search[0], header_expect, sizeof(output_header_search));
1044 521
1045 if (output_header_search[sizeof(output_header_search) - 1] != '\0') { 522 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1046 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); 523 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
1047 } 524 }
1048 525
1049 char tmp[DEFAULT_BUFFER_SIZE]; 526 xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "),
1050 527 output_header_search, workingState.use_ssl ? "https" : "http",
1051 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, 528 workingState.host_name ? workingState.host_name : workingState.server_address,
1052 use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); 529 workingState.serverPort, workingState.server_url);
1053 530
1054 strcpy(msg, tmp); 531 sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL);
1055
1056 result = STATE_CRITICAL;
1057 } 532 }
533
534 mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect);
1058 } 535 }
1059 536
1060 if (strlen(string_expect)) { 537 if (strlen(config.string_expect)) {
1061 if (!strstr(body_buf.buf, string_expect)) { 538 mp_subcheck sc_string_expect = mp_subcheck_init();
539 sc_string_expect = mp_set_subcheck_default_state(sc_string_expect, STATE_OK);
540 xasprintf(&sc_string_expect.output, "Expect string \"%s\" in body", config.string_expect);
1062 541
1063 strncpy(&output_string_search[0], string_expect, sizeof(output_string_search)); 542 if (!strstr(curl_state.body_buf->buf, config.string_expect)) {
543 char output_string_search[30] = "";
544 strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search));
1064 545
1065 if (output_string_search[sizeof(output_string_search) - 1] != '\0') { 546 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
1066 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); 547 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
1067 } 548 }
1068 549
1069 char tmp[DEFAULT_BUFFER_SIZE]; 550 xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "),
1070 551 output_string_search, workingState.use_ssl ? "https" : "http",
1071 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, 552 workingState.host_name ? workingState.host_name : workingState.server_address,
1072 use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); 553 workingState.serverPort, workingState.server_url);
1073
1074 strcpy(msg, tmp);
1075 554
1076 result = STATE_CRITICAL; 555 sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL);
1077 } 556 }
557
558 mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect);
1078 } 559 }
1079 560
1080 if (strlen(regexp)) { 561 if (strlen(config.regexp)) {
1081 errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); 562 mp_subcheck sc_body_regex = mp_subcheck_init();
1082 if ((errcode == 0 && !invert_regex) || (errcode == REG_NOMATCH && invert_regex)) { 563 xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp);
1083 /* OK - No-op to avoid changing the logic around it */ 564 regmatch_t pmatch[REGS];
1084 result = max_state_alt(STATE_OK, result);
1085 } else if ((errcode == REG_NOMATCH && !invert_regex) || (errcode == 0 && invert_regex)) {
1086 if (!invert_regex) {
1087 char tmp[DEFAULT_BUFFER_SIZE];
1088 565
1089 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg); 566 int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0);
1090 strcpy(msg, tmp);
1091 567
568 if (errcode == 0) {
569 // got a match
570 if (config.invert_regex) {
571 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
1092 } else { 572 } else {
1093 char tmp[DEFAULT_BUFFER_SIZE]; 573 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
574 }
575 } else if (errcode == REG_NOMATCH) {
576 // got no match
577 xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output);
1094 578
1095 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); 579 if (config.invert_regex) {
1096 strcpy(msg, tmp); 580 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
581 } else {
582 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
1097 } 583 }
1098 result = state_regex;
1099 } else { 584 } else {
1100 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 585 // error in regexec
1101 586 char error_buffer[DEFAULT_BUFFER_SIZE];
1102 char tmp[DEFAULT_BUFFER_SIZE]; 587 regerror(errcode, &config.compiled_regex, &error_buffer[0], DEFAULT_BUFFER_SIZE);
1103 588 xasprintf(&sc_body_regex.output, "regexec error: %s", error_buffer);
1104 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf); 589 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_UNKNOWN);
1105 strcpy(msg, tmp);
1106 result = STATE_UNKNOWN;
1107 } 590 }
591
592 mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex);
1108 } 593 }
1109 594
1110 /* make sure the page is of an appropriate size */ 595 // size a.k.a. page length
1111 if ((max_page_len > 0) && (page_len > max_page_len)) { 596 mp_perfdata pd_page_length = perfdata_init();
1112 char tmp[DEFAULT_BUFFER_SIZE]; 597 mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len);
598 pd_page_length.value = pd_val_page_length;
599 pd_page_length.label = "size";
600 pd_page_length.uom = "B";
601 pd_page_length.min = mp_create_pd_value(0);
602 pd_page_length.warn = config.page_length_limits;
603 pd_page_length.warn_present = true;
1113 604
1114 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); 605 /* make sure the page is of an appropriate size */
606 if (config.page_length_limits_is_set) {
607 mp_thresholds page_length_threshold = mp_thresholds_init();
608 page_length_threshold.warning = config.page_length_limits;
609 page_length_threshold.warning_is_set = true;
1115 610
1116 strcpy(msg, tmp); 611 pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold);
1117 612
1118 result = max_state_alt(STATE_WARNING, result); 613 mp_subcheck sc_page_length = mp_subcheck_init();
1119 614
1120 } else if ((min_page_len > 0) && (page_len < min_page_len)) { 615 mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length);
1121 char tmp[DEFAULT_BUFFER_SIZE];
1122 616
1123 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); 617 mp_state_enum tmp_state = mp_get_pd_status(pd_page_length);
1124 strcpy(msg, tmp); 618 sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state);
1125 result = max_state_alt(STATE_WARNING, result);
1126 }
1127 619
1128 /* -w, -c: check warning and critical level */ 620 switch (tmp_state) {
1129 result = max_state_alt(get_status(total_time, thlds), result); 621 case STATE_CRITICAL:
622 case STATE_WARNING:
623 xasprintf(&sc_page_length.output, _("page size %zu violates threshold"), page_len);
624 break;
625 case STATE_OK:
626 xasprintf(&sc_page_length.output, _("page size %zu is OK"), page_len);
627 break;
628 default:
629 assert(false);
630 }
1130 631
1131 /* Cut-off trailing characters */ 632 mp_add_subcheck_to_subcheck(&sc_result, sc_page_length);
1132 if (strlen(msg) >= 2) {
1133 if (msg[strlen(msg) - 2] == ',')
1134 msg[strlen(msg) - 2] = '\0';
1135 else
1136 msg[strlen(msg) - 3] = '\0';
1137 } 633 }
1138 634
1139 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ 635 return sc_result;
1140 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", state_text(result),
1141 string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg,
1142 strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (display_html ? "</A>" : ""), perfstring, (show_body ? body_buf.buf : ""),
1143 (show_body ? "\n" : ""));
1144
1145 return max_state_alt(result, result_ssl);
1146} 636}
1147 637
1148int uri_strcmp(const UriTextRangeA range, const char *s) { 638int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) {
1149 if (!range.first) 639 if (!range.first) {
1150 return -1; 640 return -1;
1151 if ((size_t)(range.afterLast - range.first) < strlen(s)) 641 }
642 if ((size_t)(range.afterLast - range.first) < strlen(stringToCompare)) {
1152 return -1; 643 return -1;
1153 return strncmp(s, range.first, min((size_t)(range.afterLast - range.first), strlen(s))); 644 }
645 return strncmp(stringToCompare, range.first,
646 min((size_t)(range.afterLast - range.first), strlen(stringToCompare)));
1154} 647}
1155 648
1156char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { 649char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) {
1157 if (!range.first) 650 if (!range.first) {
1158 return "(null)"; 651 return "(null)";
652 }
1159 strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first))); 653 strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first)));
1160 buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0'; 654 buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0';
1161 buf[range.afterLast - range.first] = '\0'; 655 buf[range.afterLast - range.first] = '\0';
1162 return buf; 656 return buf;
1163} 657}
1164 658
1165void redir(curlhelp_write_curlbuf *header_buf) { 659redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config,
1166 char *location = NULL; 660 int redir_depth, check_curl_working_state working_state) {
1167 curlhelp_statusline status_line; 661 curlhelp_statusline status_line;
1168 struct phr_header headers[255]; 662 struct phr_header headers[255];
1169 size_t nof_headers = 255;
1170 size_t msglen; 663 size_t msglen;
1171 char buf[DEFAULT_BUFFER_SIZE]; 664 size_t nof_headers = 255;
1172 char ipstr[INET_ADDR_MAX_SIZE]; 665 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
1173 int new_port; 666 &status_line.http_minor, &status_line.http_code, &status_line.msg,
1174 char *new_host; 667 &msglen, headers, &nof_headers, 0);
1175 char *new_url;
1176
1177 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
1178 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
1179 668
1180 if (res == -1) { 669 if (res == -1) {
1181 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); 670 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
1182 } 671 }
1183 672
1184 location = get_header_value(headers, nof_headers, "location"); 673 char *location = get_header_value(headers, nof_headers, "location");
1185 674
1186 if (verbose >= 2) 675 if (verbose >= 2) {
1187 printf(_("* Seen redirect location %s\n"), location); 676 printf(_("* Seen redirect location %s\n"), location);
677 }
1188 678
1189 if (++redir_depth > max_depth) 679 if (++redir_depth > config.max_depth) {
1190 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), max_depth, location, 680 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"),
1191 (display_html ? "</A>" : "")); 681 config.max_depth, location);
682 }
1192 683
1193 UriParserStateA state; 684 UriParserStateA state;
1194 UriUriA uri; 685 UriUriA uri;
1195 state.uri = &uri; 686 state.uri = &uri;
1196 if (uriParseUriA(&state, location) != URI_SUCCESS) { 687 if (uriParseUriA(&state, location) != URI_SUCCESS) {
1197 if (state.errorCode == URI_ERROR_SYNTAX) { 688 if (state.errorCode == URI_ERROR_SYNTAX) {
1198 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, (display_html ? "</A>" : "")); 689 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'\n"),
690 location);
1199 } else if (state.errorCode == URI_ERROR_MALLOC) { 691 } else if (state.errorCode == URI_ERROR_MALLOC) {
1200 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 692 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1201 } 693 }
1202 } 694 }
1203 695
696 char ipstr[INET_ADDR_MAX_SIZE];
697 char buf[DEFAULT_BUFFER_SIZE];
1204 if (verbose >= 2) { 698 if (verbose >= 2) {
1205 printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE)); 699 printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE));
1206 printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); 700 printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
@@ -1215,9 +709,9 @@ void redir(curlhelp_write_curlbuf *header_buf) {
1215 } 709 }
1216 if (uri.pathHead) { 710 if (uri.pathHead) {
1217 printf(_("** path: ")); 711 printf(_("** path: "));
1218 const UriPathSegmentA *p = uri.pathHead; 712 for (UriPathSegmentA *path_segment = uri.pathHead; path_segment;
1219 for (; p; p = p->next) { 713 path_segment = path_segment->next) {
1220 printf("/%s", uri_string(p->text, buf, DEFAULT_BUFFER_SIZE)); 714 printf("/%s", uri_string(path_segment->text, buf, DEFAULT_BUFFER_SIZE));
1221 } 715 }
1222 puts(""); 716 puts("");
1223 } 717 }
@@ -1230,99 +724,104 @@ void redir(curlhelp_write_curlbuf *header_buf) {
1230 } 724 }
1231 725
1232 if (uri.scheme.first) { 726 if (uri.scheme.first) {
1233 if (!uri_strcmp(uri.scheme, "https")) 727 working_state.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https"));
1234 use_ssl = true;
1235 else
1236 use_ssl = false;
1237 } 728 }
1238 729
1239 /* we do a sloppy test here only, because uriparser would have failed 730 /* we do a sloppy test here only, because uriparser would have failed
1240 * above, if the port would be invalid, we just check for MAX_PORT 731 * above, if the port would be invalid, we just check for MAX_PORT
1241 */ 732 */
733 int new_port;
1242 if (uri.portText.first) { 734 if (uri.portText.first) {
1243 new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); 735 new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE));
1244 } else { 736 } else {
1245 new_port = HTTP_PORT; 737 new_port = HTTP_PORT;
1246 if (use_ssl) 738 if (working_state.use_ssl) {
1247 new_port = HTTPS_PORT; 739 new_port = HTTPS_PORT;
740 }
741 }
742 if (new_port > MAX_PORT) {
743 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s\n"), MAX_PORT,
744 location);
1248 } 745 }
1249 if (new_port > MAX_PORT)
1250 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, display_html ? "</A>" : "");
1251 746
1252 /* by RFC 7231 relative URLs in Location should be taken relative to 747 /* by RFC 7231 relative URLs in Location should be taken relative to
1253 * the original URL, so we try to form a new absolute URL here 748 * the original URL, so we try to form a new absolute URL here
1254 */ 749 */
750 char *new_host;
1255 if (!uri.scheme.first && !uri.hostText.first) { 751 if (!uri.scheme.first && !uri.hostText.first) {
1256 new_host = strdup(host_name ? host_name : server_address); 752 new_host = strdup(working_state.host_name ? working_state.host_name
1257 new_port = server_port; 753 : working_state.server_address);
1258 if (use_ssl) 754 new_port = working_state.serverPort;
755 if (working_state.use_ssl) {
1259 uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); 756 uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE);
757 }
1260 } else { 758 } else {
1261 new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); 759 new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1262 } 760 }
1263 761
1264 /* compose new path */ 762 /* compose new path */
1265 /* TODO: handle fragments and query part of URL */ 763 /* TODO: handle fragments and query part of URL */
1266 new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); 764 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE);
1267 if (uri.pathHead) { 765 if (uri.pathHead) {
1268 const UriPathSegmentA *p = uri.pathHead; 766 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment;
1269 for (; p; p = p->next) { 767 pathSegment = pathSegment->next) {
1270 strncat(new_url, "/", DEFAULT_BUFFER_SIZE); 768 strncat(new_url, "/", DEFAULT_BUFFER_SIZE);
1271 strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE - 1); 769 strncat(new_url, uri_string(pathSegment->text, buf, DEFAULT_BUFFER_SIZE),
770 DEFAULT_BUFFER_SIZE - 1);
1272 } 771 }
1273 } 772 }
1274 773
1275 if (server_port == new_port && !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) && 774 if (working_state.serverPort == new_port &&
1276 (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) 775 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
1277 die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), use_ssl ? "https" : "http", 776 (working_state.host_name &&
1278 new_host, new_port, new_url, (display_html ? "</A>" : "")); 777 !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
778 !strcmp(working_state.server_url, new_url)) {
779 die(STATE_CRITICAL,
780 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s\n"),
781 working_state.use_ssl ? "https" : "http", new_host, new_port, new_url);
782 }
1279 783
1280 /* set new values for redirected request */ 784 /* set new values for redirected request */
1281 785
1282 if (!(followsticky & STICKY_HOST)) { 786 if (!(config.followsticky & STICKY_HOST)) {
1283 free(server_address); 787 free(working_state.server_address);
1284 server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); 788 working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH);
1285 } 789 }
1286 if (!(followsticky & STICKY_PORT)) { 790 if (!(config.followsticky & STICKY_PORT)) {
1287 server_port = (unsigned short)new_port; 791 working_state.serverPort = (unsigned short)new_port;
1288 } 792 }
1289 793
1290 free(host_name); 794 free(working_state.host_name);
1291 host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); 795 working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH);
1292 796
1293 /* reset virtual port */ 797 /* reset virtual port */
1294 virtual_port = server_port; 798 working_state.virtualPort = working_state.serverPort;
1295 799
1296 free(new_host); 800 free(new_host);
1297 free(server_url); 801 free(working_state.server_url);
1298 server_url = new_url; 802 working_state.server_url = new_url;
1299 803
1300 uriFreeUriMembersA(&uri); 804 uriFreeUriMembersA(&uri);
1301 805
1302 if (verbose) 806 if (verbose) {
1303 printf(_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, 807 printf(_("Redirection to %s://%s:%d%s\n"), working_state.use_ssl ? "https" : "http",
1304 server_url); 808 working_state.host_name ? working_state.host_name : working_state.server_address,
809 working_state.serverPort, working_state.server_url);
810 }
1305 811
1306 /* TODO: the hash component MUST be taken from the original URL and 812 /* TODO: the hash component MUST be taken from the original URL and
1307 * attached to the URL in Location 813 * attached to the URL in Location
1308 */ 814 */
1309 815
1310 cleanup(); 816 redir_wrapper result = {
1311 check_http(); 817 .redir_depth = redir_depth,
1312} 818 .working_state = working_state,
1313 819 .error_code = OK,
1314/* check whether a file exists */ 820 };
1315void test_file(char *path) { 821 return result;
1316 if (access(path, R_OK) == 0)
1317 return;
1318 usage2(_("file does not exist or is not readable"), path);
1319} 822}
1320 823
1321bool process_arguments(int argc, char **argv) { 824check_curl_config_wrapper process_arguments(int argc, char **argv) {
1322 char *p;
1323 int c = 1;
1324 char *temp;
1325
1326 enum { 825 enum {
1327 INVERT_REGEX = CHAR_MAX + 1, 826 INVERT_REGEX = CHAR_MAX + 1,
1328 SNI_OPTION, 827 SNI_OPTION,
@@ -1333,81 +832,101 @@ bool process_arguments(int argc, char **argv) {
1333 AUTOMATIC_DECOMPRESSION, 832 AUTOMATIC_DECOMPRESSION,
1334 COOKIE_JAR, 833 COOKIE_JAR,
1335 HAPROXY_PROTOCOL, 834 HAPROXY_PROTOCOL,
1336 STATE_REGEX 835 STATE_REGEX,
836 OUTPUT_FORMAT
1337 }; 837 };
1338 838
1339 int option = 0; 839 static struct option longopts[] = {
1340 int got_plus = 0; 840 STD_LONG_OPTS,
1341 static struct option longopts[] = {STD_LONG_OPTS, 841 {"link", no_argument, 0, 'L'},
1342 {"link", no_argument, 0, 'L'}, 842 {"nohtml", no_argument, 0, 'n'},
1343 {"nohtml", no_argument, 0, 'n'}, 843 {"ssl", optional_argument, 0, 'S'},
1344 {"ssl", optional_argument, 0, 'S'}, 844 {"sni", no_argument, 0, SNI_OPTION},
1345 {"sni", no_argument, 0, SNI_OPTION}, 845 {"post", required_argument, 0, 'P'},
1346 {"post", required_argument, 0, 'P'}, 846 {"method", required_argument, 0, 'j'},
1347 {"method", required_argument, 0, 'j'}, 847 {"IP-address", required_argument, 0, 'I'},
1348 {"IP-address", required_argument, 0, 'I'}, 848 {"url", required_argument, 0, 'u'},
1349 {"url", required_argument, 0, 'u'}, 849 {"port", required_argument, 0, 'p'},
1350 {"port", required_argument, 0, 'p'}, 850 {"authorization", required_argument, 0, 'a'},
1351 {"authorization", required_argument, 0, 'a'}, 851 {"proxy-authorization", required_argument, 0, 'b'},
1352 {"proxy-authorization", required_argument, 0, 'b'}, 852 {"header-string", required_argument, 0, 'd'},
1353 {"header-string", required_argument, 0, 'd'}, 853 {"string", required_argument, 0, 's'},
1354 {"string", required_argument, 0, 's'}, 854 {"expect", required_argument, 0, 'e'},
1355 {"expect", required_argument, 0, 'e'}, 855 {"regex", required_argument, 0, 'r'},
1356 {"regex", required_argument, 0, 'r'}, 856 {"ereg", required_argument, 0, 'r'},
1357 {"ereg", required_argument, 0, 'r'}, 857 {"eregi", required_argument, 0, 'R'},
1358 {"eregi", required_argument, 0, 'R'}, 858 {"linespan", no_argument, 0, 'l'},
1359 {"linespan", no_argument, 0, 'l'}, 859 {"onredirect", required_argument, 0, 'f'},
1360 {"onredirect", required_argument, 0, 'f'}, 860 {"certificate", required_argument, 0, 'C'},
1361 {"certificate", required_argument, 0, 'C'}, 861 {"client-cert", required_argument, 0, 'J'},
1362 {"client-cert", required_argument, 0, 'J'}, 862 {"private-key", required_argument, 0, 'K'},
1363 {"private-key", required_argument, 0, 'K'}, 863 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
1364 {"ca-cert", required_argument, 0, CA_CERT_OPTION}, 864 {"verify-cert", no_argument, 0, 'D'},
1365 {"verify-cert", no_argument, 0, 'D'}, 865 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
1366 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, 866 {"useragent", required_argument, 0, 'A'},
1367 {"useragent", required_argument, 0, 'A'}, 867 {"header", required_argument, 0, 'k'},
1368 {"header", required_argument, 0, 'k'}, 868 {"no-body", no_argument, 0, 'N'},
1369 {"no-body", no_argument, 0, 'N'}, 869 {"max-age", required_argument, 0, 'M'},
1370 {"max-age", required_argument, 0, 'M'}, 870 {"content-type", required_argument, 0, 'T'},
1371 {"content-type", required_argument, 0, 'T'}, 871 {"pagesize", required_argument, 0, 'm'},
1372 {"pagesize", required_argument, 0, 'm'}, 872 {"invert-regex", no_argument, NULL, INVERT_REGEX},
1373 {"invert-regex", no_argument, NULL, INVERT_REGEX}, 873 {"state-regex", required_argument, 0, STATE_REGEX},
1374 {"state-regex", required_argument, 0, STATE_REGEX}, 874 {"use-ipv4", no_argument, 0, '4'},
1375 {"use-ipv4", no_argument, 0, '4'}, 875 {"use-ipv6", no_argument, 0, '6'},
1376 {"use-ipv6", no_argument, 0, '6'}, 876 {"extended-perfdata", no_argument, 0, 'E'},
1377 {"extended-perfdata", no_argument, 0, 'E'}, 877 {"show-body", no_argument, 0, 'B'},
1378 {"show-body", no_argument, 0, 'B'}, 878 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
1379 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, 879 {"http-version", required_argument, 0, HTTP_VERSION_OPTION},
1380 {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, 880 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION},
1381 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, 881 {"cookie-jar", required_argument, 0, COOKIE_JAR},
1382 {"cookie-jar", required_argument, 0, COOKIE_JAR}, 882 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL},
1383 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, 883 {"output-format", required_argument, 0, OUTPUT_FORMAT},
1384 {0, 0, 0, 0}}; 884 {0, 0, 0, 0}};
1385 885
1386 if (argc < 2) 886 check_curl_config_wrapper result = {
1387 return false; 887 .errorcode = OK,
888 .config = check_curl_config_init(),
889 };
1388 890
1389 /* support check_http compatible arguments */ 891 if (argc < 2) {
1390 for (c = 1; c < argc; c++) { 892 result.errorcode = ERROR;
1391 if (strcmp("-to", argv[c]) == 0) 893 return result;
1392 strcpy(argv[c], "-t");
1393 if (strcmp("-hn", argv[c]) == 0)
1394 strcpy(argv[c], "-H");
1395 if (strcmp("-wt", argv[c]) == 0)
1396 strcpy(argv[c], "-w");
1397 if (strcmp("-ct", argv[c]) == 0)
1398 strcpy(argv[c], "-c");
1399 if (strcmp("-nohtml", argv[c]) == 0)
1400 strcpy(argv[c], "-n");
1401 } 894 }
1402 895
1403 server_url = strdup(DEFAULT_SERVER_URL); 896 /* support check_http compatible arguments */
897 for (int index = 1; index < argc; index++) {
898 if (strcmp("-to", argv[index]) == 0) {
899 strcpy(argv[index], "-t");
900 }
901 if (strcmp("-hn", argv[index]) == 0) {
902 strcpy(argv[index], "-H");
903 }
904 if (strcmp("-wt", argv[index]) == 0) {
905 strcpy(argv[index], "-w");
906 }
907 if (strcmp("-ct", argv[index]) == 0) {
908 strcpy(argv[index], "-c");
909 }
910 if (strcmp("-nohtml", argv[index]) == 0) {
911 strcpy(argv[index], "-n");
912 }
913 }
1404 914
1405 while (1) { 915 int option = 0;
1406 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); 916 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
1407 if (c == -1 || c == EOF || c == 1) 917 bool specify_port = false;
918 bool enable_tls = false;
919 char *tls_option_optarg = NULL;
920
921 while (true) {
922 int option_index = getopt_long(
923 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",
924 longopts, &option);
925 if (option_index == -1 || option_index == EOF || option_index == 1) {
1408 break; 926 break;
927 }
1409 928
1410 switch (c) { 929 switch (option_index) {
1411 case 'h': 930 case 'h':
1412 print_help(); 931 print_help();
1413 exit(STATE_UNKNOWN); 932 exit(STATE_UNKNOWN);
@@ -1421,270 +940,253 @@ bool process_arguments(int argc, char **argv) {
1421 verbose++; 940 verbose++;
1422 break; 941 break;
1423 case 't': /* timeout period */ 942 case 't': /* timeout period */
1424 if (!is_intnonneg(optarg)) 943 if (!is_intnonneg(optarg)) {
1425 usage2(_("Timeout interval must be a positive integer"), optarg); 944 usage2(_("Timeout interval must be a positive integer"), optarg);
1426 else 945 } else {
1427 socket_timeout = (int)strtol(optarg, NULL, 10); 946 result.config.curl_config.socket_timeout = (int)strtol(optarg, NULL, 10);
947 }
1428 break; 948 break;
1429 case 'c': /* critical time threshold */ 949 case 'c': /* critical time threshold */
1430 critical_thresholds = optarg; 950 {
1431 break; 951 mp_range_parsed critical_range = mp_parse_range_string(optarg);
952 if (critical_range.error != MP_PARSING_SUCCES) {
953 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg);
954 }
955 result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range);
956 } break;
1432 case 'w': /* warning time threshold */ 957 case 'w': /* warning time threshold */
1433 warning_thresholds = optarg; 958 {
1434 break; 959 mp_range_parsed warning_range = mp_parse_range_string(optarg);
960
961 if (warning_range.error != MP_PARSING_SUCCES) {
962 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg);
963 }
964 result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range);
965 } break;
1435 case 'H': /* virtual host */ 966 case 'H': /* virtual host */
1436 host_name = strdup(optarg); 967 result.config.initial_config.host_name = strdup(optarg);
1437 if (host_name[0] == '[') { 968 char *tmp_string;
1438 if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */ 969 size_t host_name_length;
1439 virtual_port = atoi(p + 2); 970 if (result.config.initial_config.host_name[0] == '[') {
971 if ((tmp_string = strstr(result.config.initial_config.host_name, "]:")) !=
972 NULL) { /* [IPv6]:port */
973 result.config.initial_config.virtualPort = atoi(tmp_string + 2);
1440 /* cut off the port */ 974 /* cut off the port */
1441 host_name_length = strlen(host_name) - strlen(p) - 1; 975 host_name_length =
1442 free(host_name); 976 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
1443 host_name = strndup(optarg, host_name_length); 977 free(result.config.initial_config.host_name);
978 result.config.initial_config.host_name = strndup(optarg, host_name_length);
1444 } 979 }
1445 } else if ((p = strchr(host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ 980 } else if ((tmp_string = strchr(result.config.initial_config.host_name, ':')) != NULL &&
1446 virtual_port = atoi(p); 981 strchr(++tmp_string, ':') == NULL) { /* IPv4:port or host:port */
982 result.config.initial_config.virtualPort = atoi(tmp_string);
1447 /* cut off the port */ 983 /* cut off the port */
1448 host_name_length = strlen(host_name) - strlen(p) - 1; 984 host_name_length =
1449 free(host_name); 985 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
1450 host_name = strndup(optarg, host_name_length); 986 free(result.config.initial_config.host_name);
987 result.config.initial_config.host_name = strndup(optarg, host_name_length);
1451 } 988 }
1452 break; 989 break;
1453 case 'I': /* internet address */ 990 case 'I': /* internet address */
1454 server_address = strdup(optarg); 991 result.config.initial_config.server_address = strdup(optarg);
1455 break; 992 break;
1456 case 'u': /* URL path */ 993 case 'u': /* URL path */
1457 server_url = strdup(optarg); 994 result.config.initial_config.server_url = strdup(optarg);
1458 break; 995 break;
1459 case 'p': /* Server port */ 996 case 'p': /* Server port */
1460 if (!is_intnonneg(optarg)) 997 if (!is_intnonneg(optarg)) {
1461 usage2(_("Invalid port number, expecting a non-negative number"), optarg); 998 usage2(_("Invalid port number, expecting a non-negative number"), optarg);
1462 else { 999 } else {
1463 if (strtol(optarg, NULL, 10) > MAX_PORT) 1000 if (strtol(optarg, NULL, 10) > MAX_PORT) {
1464 usage2(_("Invalid port number, supplied port number is too big"), optarg); 1001 usage2(_("Invalid port number, supplied port number is too big"), optarg);
1465 server_port = (unsigned short)strtol(optarg, NULL, 10); 1002 }
1003 result.config.initial_config.serverPort = (unsigned short)strtol(optarg, NULL, 10);
1466 specify_port = true; 1004 specify_port = true;
1467 } 1005 }
1468 break; 1006 break;
1469 case 'a': /* authorization info */ 1007 case 'a': /* authorization info */
1470 strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1); 1008 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1);
1471 user_auth[MAX_INPUT_BUFFER - 1] = 0; 1009 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0;
1472 break; 1010 break;
1473 case 'b': /* proxy-authorization info */ 1011 case 'b': /* proxy-authorization info */
1474 strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1); 1012 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1475 proxy_auth[MAX_INPUT_BUFFER - 1] = 0; 1013 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1476 break; 1014 break;
1477 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ 1015 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1478 if (!http_post_data) 1016 if (!result.config.initial_config.http_post_data) {
1479 http_post_data = strdup(optarg); 1017 result.config.initial_config.http_post_data = strdup(optarg);
1480 if (!http_method) 1018 }
1481 http_method = strdup("POST"); 1019 if (!result.config.initial_config.http_method) {
1020 result.config.initial_config.http_method = strdup("POST");
1021 }
1482 break; 1022 break;
1483 case 'j': /* Set HTTP method */ 1023 case 'j': /* Set HTTP method */
1484 if (http_method) 1024 if (result.config.initial_config.http_method) {
1485 free(http_method); 1025 free(result.config.initial_config.http_method);
1486 http_method = strdup(optarg); 1026 }
1027 result.config.initial_config.http_method = strdup(optarg);
1487 break; 1028 break;
1488 case 'A': /* useragent */ 1029 case 'A': /* useragent */
1489 strncpy(user_agent, optarg, DEFAULT_BUFFER_SIZE); 1030 strncpy(result.config.curl_config.user_agent, optarg, DEFAULT_BUFFER_SIZE);
1490 user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; 1031 result.config.curl_config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0';
1491 break; 1032 break;
1492 case 'k': /* Additional headers */ 1033 case 'k': /* Additional headers */
1493 if (http_opt_headers_count == 0) 1034 if (result.config.curl_config.http_opt_headers_count == 0) {
1494 http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); 1035 result.config.curl_config.http_opt_headers =
1495 else 1036 malloc(sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1496 http_opt_headers = realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); 1037 } else {
1497 http_opt_headers[http_opt_headers_count - 1] = optarg; 1038 result.config.curl_config.http_opt_headers =
1039 realloc(result.config.curl_config.http_opt_headers,
1040 sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1041 }
1042 result.config.curl_config
1043 .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg;
1498 break; 1044 break;
1499 case 'L': /* show html link */ 1045 case 'L': /* show html link */
1500 display_html = true;
1501 break;
1502 case 'n': /* do not show html link */ 1046 case 'n': /* do not show html link */
1503 display_html = false; 1047 // HTML link related options are deprecated
1504 break; 1048 break;
1505 case 'C': /* Check SSL cert validity */ 1049 case 'C': /* Check SSL cert validity */
1506#ifdef LIBCURL_FEATURE_SSL 1050#ifndef LIBCURL_FEATURE_SSL
1507 if ((temp = strchr(optarg, ',')) != NULL) { 1051 usage4(_("Invalid option - SSL is not available"));
1508 *temp = '\0';
1509 if (!is_intnonneg(optarg))
1510 usage2(_("Invalid certificate expiration period"), optarg);
1511 days_till_exp_warn = atoi(optarg);
1512 *temp = ',';
1513 temp++;
1514 if (!is_intnonneg(temp))
1515 usage2(_("Invalid certificate expiration period"), temp);
1516 days_till_exp_crit = atoi(temp);
1517 } else {
1518 days_till_exp_crit = 0;
1519 if (!is_intnonneg(optarg))
1520 usage2(_("Invalid certificate expiration period"), optarg);
1521 days_till_exp_warn = atoi(optarg);
1522 }
1523 check_cert = true;
1524 goto enable_ssl;
1525#endif 1052#endif
1053 {
1054 char *temp;
1055 if ((temp = strchr(optarg, ',')) != NULL) {
1056 *temp = '\0';
1057 if (!is_intnonneg(optarg)) {
1058 usage2(_("Invalid certificate expiration period"), optarg);
1059 }
1060 result.config.days_till_exp_warn = atoi(optarg);
1061 *temp = ',';
1062 temp++;
1063 if (!is_intnonneg(temp)) {
1064 usage2(_("Invalid certificate expiration period"), temp);
1065 }
1066 result.config.days_till_exp_crit = atoi(temp);
1067 } else {
1068 result.config.days_till_exp_crit = 0;
1069 if (!is_intnonneg(optarg)) {
1070 usage2(_("Invalid certificate expiration period"), optarg);
1071 }
1072 result.config.days_till_exp_warn = atoi(optarg);
1073 }
1074 result.config.check_cert = true;
1075 enable_tls = true;
1076 }
1077 break;
1526 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ 1078 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
1527#ifdef HAVE_SSL 1079#ifdef HAVE_SSL
1528 continue_after_check_cert = true; 1080 result.config.continue_after_check_cert = true;
1529 break; 1081 break;
1530#endif 1082#endif
1531 case 'J': /* use client certificate */ 1083 case 'J': /* use client certificate */
1532#ifdef LIBCURL_FEATURE_SSL 1084#ifndef LIBCURL_FEATURE_SSL
1533 test_file(optarg); 1085 usage4(_("Invalid option - SSL is not available"));
1534 client_cert = optarg;
1535 goto enable_ssl;
1536#endif 1086#endif
1537 case 'K': /* use client private key */
1538#ifdef LIBCURL_FEATURE_SSL
1539 test_file(optarg); 1087 test_file(optarg);
1540 client_privkey = optarg; 1088 result.config.curl_config.client_cert = optarg;
1541 goto enable_ssl; 1089 enable_tls = true;
1090 break;
1091 case 'K': /* use client private key */
1092#ifndef LIBCURL_FEATURE_SSL
1093 usage4(_("Invalid option - SSL is not available"));
1542#endif 1094#endif
1543#ifdef LIBCURL_FEATURE_SSL
1544 case CA_CERT_OPTION: /* use CA chain file */
1545 test_file(optarg); 1095 test_file(optarg);
1546 ca_cert = optarg; 1096 result.config.curl_config.client_privkey = optarg;
1547 goto enable_ssl; 1097 enable_tls = true;
1098 break;
1099 case CA_CERT_OPTION: /* use CA chain file */
1100#ifndef LIBCURL_FEATURE_SSL
1101 usage4(_("Invalid option - SSL is not available"));
1548#endif 1102#endif
1549#ifdef LIBCURL_FEATURE_SSL 1103 test_file(optarg);
1550 case 'D': /* verify peer certificate & host */ 1104 result.config.curl_config.ca_cert = optarg;
1551 verify_peer_and_host = true; 1105 enable_tls = true;
1552 break; 1106 break;
1107 case 'D': /* verify peer certificate & host */
1108#ifndef LIBCURL_FEATURE_SSL
1109 usage4(_("Invalid option - SSL is not available"));
1553#endif 1110#endif
1554 case 'S': /* use SSL */ 1111 result.config.curl_config.verify_peer_and_host = true;
1555#ifdef LIBCURL_FEATURE_SSL 1112 enable_tls = true;
1556 enable_ssl:
1557 use_ssl = true;
1558 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1559 * Only set if it's non-zero. This helps when we include multiple
1560 * parameters, like -S and -C combinations */
1561 ssl_version = CURL_SSLVERSION_DEFAULT;
1562 if (c == 'S' && optarg != NULL) {
1563 char *plus_ptr = strchr(optarg, '+');
1564 if (plus_ptr) {
1565 got_plus = 1;
1566 *plus_ptr = '\0';
1567 }
1568
1569 if (optarg[0] == '2')
1570 ssl_version = CURL_SSLVERSION_SSLv2;
1571 else if (optarg[0] == '3')
1572 ssl_version = CURL_SSLVERSION_SSLv3;
1573 else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0"))
1574# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1575 ssl_version = CURL_SSLVERSION_TLSv1_0;
1576# else
1577 ssl_version = CURL_SSLVERSION_DEFAULT;
1578# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1579 else if (!strcmp(optarg, "1.1"))
1580# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1581 ssl_version = CURL_SSLVERSION_TLSv1_1;
1582# else
1583 ssl_version = CURL_SSLVERSION_DEFAULT;
1584# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1585 else if (!strcmp(optarg, "1.2"))
1586# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1587 ssl_version = CURL_SSLVERSION_TLSv1_2;
1588# else
1589 ssl_version = CURL_SSLVERSION_DEFAULT;
1590# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1591 else if (!strcmp(optarg, "1.3"))
1592# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1593 ssl_version = CURL_SSLVERSION_TLSv1_3;
1594# else
1595 ssl_version = CURL_SSLVERSION_DEFAULT;
1596# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1597 else
1598 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)"));
1599 }
1600# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1601 if (got_plus) {
1602 switch (ssl_version) {
1603 case CURL_SSLVERSION_TLSv1_3:
1604 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1605 break;
1606 case CURL_SSLVERSION_TLSv1_2:
1607 case CURL_SSLVERSION_TLSv1_1:
1608 case CURL_SSLVERSION_TLSv1_0:
1609 ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1610 break;
1611 }
1612 } else {
1613 switch (ssl_version) {
1614 case CURL_SSLVERSION_TLSv1_3:
1615 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1616 break;
1617 case CURL_SSLVERSION_TLSv1_2:
1618 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1619 break;
1620 case CURL_SSLVERSION_TLSv1_1:
1621 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1622 break;
1623 case CURL_SSLVERSION_TLSv1_0:
1624 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1625 break;
1626 }
1627 }
1628# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1629 if (verbose >= 2)
1630 printf(_("* Set SSL/TLS version to %d\n"), ssl_version);
1631 if (!specify_port)
1632 server_port = HTTPS_PORT;
1633 break; 1113 break;
1634#else /* LIBCURL_FEATURE_SSL */ 1114 case 'S': /* use SSL */
1635 /* -C -J and -K fall through to here without SSL */ 1115 tls_option_optarg = optarg;
1116 enable_tls = true;
1117#ifndef LIBCURL_FEATURE_SSL
1636 usage4(_("Invalid option - SSL is not available")); 1118 usage4(_("Invalid option - SSL is not available"));
1119#endif
1637 break; 1120 break;
1638 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */ 1121 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */
1639 use_sni = true; 1122#ifndef LIBCURL_FEATURE_SSL
1640 break; 1123 usage4(_("Invalid option - SSL is not available"));
1641#endif /* LIBCURL_FEATURE_SSL */ 1124#endif /* LIBCURL_FEATURE_SSL */
1125 break;
1642 case MAX_REDIRS_OPTION: 1126 case MAX_REDIRS_OPTION:
1643 if (!is_intnonneg(optarg)) 1127 if (!is_intnonneg(optarg)) {
1644 usage2(_("Invalid max_redirs count"), optarg); 1128 usage2(_("Invalid max_redirs count"), optarg);
1645 else { 1129 } else {
1646 max_depth = atoi(optarg); 1130 result.config.max_depth = atoi(optarg);
1647 } 1131 }
1648 break; 1132 break;
1649 case 'f': /* onredirect */ 1133 case 'f': /* onredirect */
1650 if (!strcmp(optarg, "ok")) 1134 if (!strcmp(optarg, "ok")) {
1651 onredirect = STATE_OK; 1135 result.config.on_redirect_result_state = STATE_OK;
1652 else if (!strcmp(optarg, "warning")) 1136 result.config.on_redirect_dependent = false;
1653 onredirect = STATE_WARNING; 1137 } else if (!strcmp(optarg, "warning")) {
1654 else if (!strcmp(optarg, "critical")) 1138 result.config.on_redirect_result_state = STATE_WARNING;
1655 onredirect = STATE_CRITICAL; 1139 result.config.on_redirect_dependent = false;
1656 else if (!strcmp(optarg, "unknown")) 1140 } else if (!strcmp(optarg, "critical")) {
1657 onredirect = STATE_UNKNOWN; 1141 result.config.on_redirect_result_state = STATE_CRITICAL;
1658 else if (!strcmp(optarg, "follow")) 1142 result.config.on_redirect_dependent = false;
1659 onredirect = STATE_DEPENDENT; 1143 } else if (!strcmp(optarg, "unknown")) {
1660 else if (!strcmp(optarg, "stickyport")) 1144 result.config.on_redirect_result_state = STATE_UNKNOWN;
1661 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST | STICKY_PORT; 1145 result.config.on_redirect_dependent = false;
1662 else if (!strcmp(optarg, "sticky")) 1146 } else if (!strcmp(optarg, "follow")) {
1663 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST; 1147 result.config.on_redirect_dependent = true;
1664 else if (!strcmp(optarg, "follow")) 1148 } else if (!strcmp(optarg, "stickyport")) {
1665 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE; 1149 result.config.on_redirect_dependent = true;
1666 else if (!strcmp(optarg, "curl")) 1150 result.config.followmethod = FOLLOW_HTTP_CURL,
1667 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL; 1151 result.config.followsticky = STICKY_HOST | STICKY_PORT;
1668 else 1152 } else if (!strcmp(optarg, "sticky")) {
1153 result.config.on_redirect_dependent = true;
1154 result.config.followmethod = FOLLOW_HTTP_CURL,
1155 result.config.followsticky = STICKY_HOST;
1156 } else if (!strcmp(optarg, "follow")) {
1157 result.config.on_redirect_dependent = true;
1158 result.config.followmethod = FOLLOW_HTTP_CURL,
1159 result.config.followsticky = STICKY_NONE;
1160 } else if (!strcmp(optarg, "curl")) {
1161 result.config.on_redirect_dependent = true;
1162 result.config.followmethod = FOLLOW_LIBCURL;
1163 } else {
1669 usage2(_("Invalid onredirect option"), optarg); 1164 usage2(_("Invalid onredirect option"), optarg);
1670 if (verbose >= 2) 1165 }
1671 printf(_("* Following redirects set to %s\n"), state_text(onredirect)); 1166 if (verbose >= 2) {
1167 if (result.config.on_redirect_dependent) {
1168 printf(_("* Following redirects\n"));
1169 } else {
1170 printf(_("* Following redirects set to state %s\n"),
1171 state_text(result.config.on_redirect_result_state));
1172 }
1173 }
1672 break; 1174 break;
1673 case 'd': /* string or substring */ 1175 case 'd': /* string or substring */
1674 strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); 1176 strncpy(result.config.header_expect, optarg, MAX_INPUT_BUFFER - 1);
1675 header_expect[MAX_INPUT_BUFFER - 1] = 0; 1177 result.config.header_expect[MAX_INPUT_BUFFER - 1] = 0;
1676 break; 1178 break;
1677 case 's': /* string or substring */ 1179 case 's': /* string or substring */
1678 strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1); 1180 strncpy(result.config.string_expect, optarg, MAX_INPUT_BUFFER - 1);
1679 string_expect[MAX_INPUT_BUFFER - 1] = 0; 1181 result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0;
1680 break; 1182 break;
1681 case 'e': /* string or substring */ 1183 case 'e': /* string or substring */
1682 strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1); 1184 strncpy(result.config.server_expect.string, optarg, MAX_INPUT_BUFFER - 1);
1683 server_expect[MAX_INPUT_BUFFER - 1] = 0; 1185 result.config.server_expect.string[MAX_INPUT_BUFFER - 1] = 0;
1684 server_expect_yn = 1; 1186 result.config.server_expect.is_present = true;
1685 break; 1187 break;
1686 case 'T': /* Content-type */ 1188 case 'T': /* Content-type */
1687 http_content_type = strdup(optarg); 1189 result.config.curl_config.http_content_type = strdup(optarg);
1688 break; 1190 break;
1689 case 'l': /* linespan */ 1191 case 'l': /* linespan */
1690 cflags &= ~REG_NEWLINE; 1192 cflags &= ~REG_NEWLINE;
@@ -1693,185 +1195,258 @@ bool process_arguments(int argc, char **argv) {
1693 cflags |= REG_ICASE; 1195 cflags |= REG_ICASE;
1694 // fall through 1196 // fall through
1695 case 'r': /* regex */ 1197 case 'r': /* regex */
1696 strncpy(regexp, optarg, MAX_RE_SIZE - 1); 1198 strncpy(result.config.regexp, optarg, MAX_RE_SIZE - 1);
1697 regexp[MAX_RE_SIZE - 1] = 0; 1199 result.config.regexp[MAX_RE_SIZE - 1] = 0;
1698 errcode = regcomp(&preg, regexp, cflags); 1200 regex_t preg;
1201 int errcode = regcomp(&preg, result.config.regexp, cflags);
1699 if (errcode != 0) { 1202 if (errcode != 0) {
1700 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 1203 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1701 printf(_("Could Not Compile Regular Expression: %s"), errbuf); 1204 printf(_("Could Not Compile Regular Expression: %s"), errbuf);
1702 return false; 1205 result.errorcode = ERROR;
1206 return result;
1703 } 1207 }
1208
1209 result.config.compiled_regex = preg;
1704 break; 1210 break;
1705 case INVERT_REGEX: 1211 case INVERT_REGEX:
1706 invert_regex = true; 1212 result.config.invert_regex = true;
1707 break; 1213 break;
1708 case STATE_REGEX: 1214 case STATE_REGEX:
1709 if (!strcasecmp(optarg, "critical")) 1215 if (!strcasecmp(optarg, "critical")) {
1710 state_regex = STATE_CRITICAL; 1216 result.config.state_regex = STATE_CRITICAL;
1711 else if (!strcasecmp(optarg, "warning")) 1217 } else if (!strcasecmp(optarg, "warning")) {
1712 state_regex = STATE_WARNING; 1218 result.config.state_regex = STATE_WARNING;
1713 else 1219 } else {
1714 usage2(_("Invalid state-regex option"), optarg); 1220 usage2(_("Invalid state-regex option"), optarg);
1221 }
1715 break; 1222 break;
1716 case '4': 1223 case '4':
1717 address_family = AF_INET; 1224 result.config.curl_config.sin_family = AF_INET;
1718 break; 1225 break;
1719 case '6': 1226 case '6':
1720#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) 1227#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
1721 address_family = AF_INET6; 1228 result.config.curl_config.sin_family = AF_INET6;
1722#else 1229#else
1723 usage4(_("IPv6 support not available")); 1230 usage4(_("IPv6 support not available"));
1724#endif 1231#endif
1725 break; 1232 break;
1726 case 'm': /* min_page_length */ 1233 case 'm': /* min_page_length */
1727 { 1234 {
1728 char *tmp; 1235 mp_range_parsed foo = mp_parse_range_string(optarg);
1729 if (strchr(optarg, ':') != (char *)NULL) { 1236
1730 /* range, so get two values, min:max */ 1237 if (foo.error != MP_PARSING_SUCCES) {
1731 tmp = strtok(optarg, ":"); 1238 die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg);
1732 if (tmp == NULL) { 1239 }
1733 printf("Bad format: try \"-m min:max\"\n"); 1240
1734 exit(STATE_WARNING); 1241 result.config.page_length_limits = foo.range;
1735 } else 1242 result.config.page_length_limits_is_set = true;
1736 min_page_len = atoi(tmp);
1737
1738 tmp = strtok(NULL, ":");
1739 if (tmp == NULL) {
1740 printf("Bad format: try \"-m min:max\"\n");
1741 exit(STATE_WARNING);
1742 } else
1743 max_page_len = atoi(tmp);
1744 } else
1745 min_page_len = atoi(optarg);
1746 break; 1243 break;
1747 } 1244 }
1748 case 'N': /* no-body */ 1245 case 'N': /* no-body */
1749 no_body = true; 1246 result.config.initial_config.no_body = true;
1750 break; 1247 break;
1751 case 'M': /* max-age */ 1248 case 'M': /* max-age */
1752 { 1249 {
1753 int L = strlen(optarg); 1250 size_t option_length = strlen(optarg);
1754 if (L && optarg[L - 1] == 'm') 1251 if (option_length && optarg[option_length - 1] == 'm') {
1755 maximum_age = atoi(optarg) * 60; 1252 result.config.maximum_age = atoi(optarg) * 60;
1756 else if (L && optarg[L - 1] == 'h') 1253 } else if (option_length && optarg[option_length - 1] == 'h') {
1757 maximum_age = atoi(optarg) * 60 * 60; 1254 result.config.maximum_age = atoi(optarg) * 60 * 60;
1758 else if (L && optarg[L - 1] == 'd') 1255 } else if (option_length && optarg[option_length - 1] == 'd') {
1759 maximum_age = atoi(optarg) * 60 * 60 * 24; 1256 result.config.maximum_age = atoi(optarg) * 60 * 60 * 24;
1760 else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) 1257 } else if (option_length &&
1761 maximum_age = atoi(optarg); 1258 (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) {
1762 else { 1259 result.config.maximum_age = atoi(optarg);
1260 } else {
1763 fprintf(stderr, "unparsable max-age: %s\n", optarg); 1261 fprintf(stderr, "unparsable max-age: %s\n", optarg);
1764 exit(STATE_WARNING); 1262 exit(STATE_WARNING);
1765 } 1263 }
1766 if (verbose >= 2) 1264 if (verbose >= 2) {
1767 printf("* Maximal age of document set to %d seconds\n", maximum_age); 1265 printf("* Maximal age of document set to %d seconds\n", result.config.maximum_age);
1266 }
1768 } break; 1267 } break;
1769 case 'E': /* show extended perfdata */ 1268 case 'E': /* show extended perfdata */
1770 show_extended_perfdata = true; 1269 result.config.show_extended_perfdata = true;
1771 break; 1270 break;
1772 case 'B': /* print body content after status line */ 1271 case 'B': /* print body content after status line */
1773 show_body = true; 1272 result.config.show_body = true;
1774 break; 1273 break;
1775 case HTTP_VERSION_OPTION: 1274 case HTTP_VERSION_OPTION:
1776 curl_http_version = CURL_HTTP_VERSION_NONE; 1275 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1777 if (strcmp(optarg, "1.0") == 0) { 1276 if (strcmp(optarg, "1.0") == 0) {
1778 curl_http_version = CURL_HTTP_VERSION_1_0; 1277 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_0;
1779 } else if (strcmp(optarg, "1.1") == 0) { 1278 } else if (strcmp(optarg, "1.1") == 0) {
1780 curl_http_version = CURL_HTTP_VERSION_1_1; 1279 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_1;
1781 } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { 1280 } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) {
1782#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) 1281#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0)
1783 curl_http_version = CURL_HTTP_VERSION_2_0; 1282 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_2_0;
1784#else 1283#else
1785 curl_http_version = CURL_HTTP_VERSION_NONE; 1284 result.config.curl_http_version = CURL_HTTP_VERSION_NONE;
1786#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ 1285#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */
1286 } else if ((strcmp(optarg, "3") == 0)) {
1287#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0)
1288 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3;
1289#else
1290 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1291#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */
1787 } else { 1292 } else {
1788 fprintf(stderr, "unknown http-version parameter: %s\n", optarg); 1293 fprintf(stderr, "unknown http-version parameter: %s\n", optarg);
1789 exit(STATE_WARNING); 1294 exit(STATE_WARNING);
1790 } 1295 }
1791 break; 1296 break;
1792 case AUTOMATIC_DECOMPRESSION: 1297 case AUTOMATIC_DECOMPRESSION:
1793 automatic_decompression = true; 1298 result.config.curl_config.automatic_decompression = true;
1794 break; 1299 break;
1795 case COOKIE_JAR: 1300 case COOKIE_JAR:
1796 cookie_jar_file = optarg; 1301 result.config.curl_config.cookie_jar_file = optarg;
1797 break; 1302 break;
1798 case HAPROXY_PROTOCOL: 1303 case HAPROXY_PROTOCOL:
1799 haproxy_protocol = true; 1304 result.config.curl_config.haproxy_protocol = true;
1800 break; 1305 break;
1801 case '?': 1306 case '?':
1802 /* print short usage statement if args not parsable */ 1307 /* print short usage statement if args not parsable */
1803 usage5(); 1308 usage5();
1804 break; 1309 break;
1310 case OUTPUT_FORMAT: {
1311 parsed_output_format parser = mp_parse_output_format(optarg);
1312 if (!parser.parsing_success) {
1313 // TODO List all available formats here, maybe add anothoer usage function
1314 printf("Invalid output format: %s\n", optarg);
1315 exit(STATE_UNKNOWN);
1316 }
1317
1318 result.config.output_format_is_set = true;
1319 result.config.output_format = parser.output_format;
1320 break;
1321 }
1805 } 1322 }
1806 } 1323 }
1807 1324
1808 c = optind; 1325 if (enable_tls) {
1326 bool got_plus = false;
1327 result.config.initial_config.use_ssl = true;
1328 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1329 * Only set if it's non-zero. This helps when we include multiple
1330 * parameters, like -S and -C combinations */
1331 result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT;
1332 if (tls_option_optarg != NULL) {
1333 char *plus_ptr = strchr(optarg, '+');
1334 if (plus_ptr) {
1335 got_plus = true;
1336 *plus_ptr = '\0';
1337 }
1809 1338
1810 if (server_address == NULL && c < argc) 1339 if (optarg[0] == '2') {
1811 server_address = strdup(argv[c++]); 1340 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2;
1341 } else if (optarg[0] == '3') {
1342 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3;
1343 } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) {
1344#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1345 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0;
1346#else
1347 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1348#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1349 } else if (!strcmp(optarg, "1.1")) {
1350#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1351 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1;
1352#else
1353 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1354#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1355 } else if (!strcmp(optarg, "1.2")) {
1356#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1357 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2;
1358#else
1359 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1360#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1361 } else if (!strcmp(optarg, "1.3")) {
1362#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1363 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3;
1364#else
1365 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1366#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1367 } else {
1368 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 "
1369 "(with optional '+' suffix)"));
1370 }
1371 }
1372#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1373 if (got_plus) {
1374 switch (result.config.curl_config.ssl_version) {
1375 case CURL_SSLVERSION_TLSv1_3:
1376 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1377 break;
1378 case CURL_SSLVERSION_TLSv1_2:
1379 case CURL_SSLVERSION_TLSv1_1:
1380 case CURL_SSLVERSION_TLSv1_0:
1381 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1382 break;
1383 }
1384 } else {
1385 switch (result.config.curl_config.ssl_version) {
1386 case CURL_SSLVERSION_TLSv1_3:
1387 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1388 break;
1389 case CURL_SSLVERSION_TLSv1_2:
1390 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1391 break;
1392 case CURL_SSLVERSION_TLSv1_1:
1393 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1394 break;
1395 case CURL_SSLVERSION_TLSv1_0:
1396 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1397 break;
1398 }
1399 }
1400#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1401 if (verbose >= 2) {
1402 printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version);
1403 }
1404 if (!specify_port) {
1405 result.config.initial_config.serverPort = HTTPS_PORT;
1406 }
1407 }
1812 1408
1813 if (host_name == NULL && c < argc) 1409 int option_counter = optind;
1814 host_name = strdup(argv[c++]);
1815 1410
1816 if (server_address == NULL) { 1411 if (result.config.initial_config.server_address == NULL && option_counter < argc) {
1817 if (host_name == NULL) 1412 result.config.initial_config.server_address = strdup(argv[option_counter++]);
1818 usage4(_("You must specify a server address or host name"));
1819 else
1820 server_address = strdup(host_name);
1821 } 1413 }
1822 1414
1823 set_thresholds(&thlds, warning_thresholds, critical_thresholds); 1415 if (result.config.initial_config.host_name == NULL && option_counter < argc) {
1416 result.config.initial_config.host_name = strdup(argv[option_counter++]);
1417 }
1824 1418
1825 if (critical_thresholds && thlds->critical->end > (double)socket_timeout) 1419 if (result.config.initial_config.server_address == NULL) {
1826 socket_timeout = (int)thlds->critical->end + 1; 1420 if (result.config.initial_config.host_name == NULL) {
1827 if (verbose >= 2) 1421 usage4(_("You must specify a server address or host name"));
1828 printf("* Socket timeout set to %ld seconds\n", socket_timeout); 1422 } else {
1423 result.config.initial_config.server_address =
1424 strdup(result.config.initial_config.host_name);
1425 }
1426 }
1829 1427
1830 if (http_method == NULL) 1428 if (result.config.initial_config.http_method == NULL) {
1831 http_method = strdup("GET"); 1429 result.config.initial_config.http_method = strdup("GET");
1430 }
1832 1431
1833 if (client_cert && !client_privkey) 1432 if (result.config.curl_config.client_cert && !result.config.curl_config.client_privkey) {
1834 usage4(_("If you use a client certificate you must also specify a private key file")); 1433 usage4(_("If you use a client certificate you must also specify a private key file"));
1835
1836 if (virtual_port == 0)
1837 virtual_port = server_port;
1838 else {
1839 if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT))
1840 if (!specify_port)
1841 server_port = virtual_port;
1842 } 1434 }
1843 1435
1844 return true; 1436 if (result.config.initial_config.virtualPort == 0) {
1845} 1437 result.config.initial_config.virtualPort = result.config.initial_config.serverPort;
1846 1438 } else {
1847char *perfd_time(double elapsed_time) { 1439 if ((result.config.initial_config.use_ssl &&
1848 return fperfdata("time", elapsed_time, "s", thlds->warning ? true : false, thlds->warning ? thlds->warning->end : 0, 1440 result.config.initial_config.serverPort == HTTPS_PORT) ||
1849 thlds->critical ? true : false, thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); 1441 (!result.config.initial_config.use_ssl &&
1850} 1442 result.config.initial_config.serverPort == HTTP_PORT)) {
1851 1443 if (!specify_port) {
1852char *perfd_time_connect(double elapsed_time_connect) { 1444 result.config.initial_config.serverPort = result.config.initial_config.virtualPort;
1853 return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1445 }
1854} 1446 }
1855 1447 }
1856char *perfd_time_ssl(double elapsed_time_ssl) {
1857 return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1858}
1859
1860char *perfd_time_headers(double elapsed_time_headers) {
1861 return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1862}
1863
1864char *perfd_time_firstbyte(double elapsed_time_firstbyte) {
1865 return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1866}
1867
1868char *perfd_time_transfer(double elapsed_time_transfer) {
1869 return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1870}
1871 1448
1872char *perfd_size(int page_len) { 1449 return result;
1873 return perfdata("size", page_len, "B", (min_page_len > 0 ? true : false), min_page_len, (min_page_len > 0 ? true : false), 0, true, 0,
1874 false, 0);
1875} 1450}
1876 1451
1877void print_help(void) { 1452void print_help(void) {
@@ -1885,7 +1460,8 @@ void print_help(void) {
1885 printf("%s\n", _("strings and regular expressions, check connection times, and report on")); 1460 printf("%s\n", _("strings and regular expressions, check connection times, and report on"));
1886 printf("%s\n", _("certificate expiration times.")); 1461 printf("%s\n", _("certificate expiration times."));
1887 printf("\n"); 1462 printf("\n");
1888 printf("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); 1463 printf("%s\n",
1464 _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1889 printf("%s\n", _("as possible.")); 1465 printf("%s\n", _("as possible."));
1890 1466
1891 printf("\n\n"); 1467 printf("\n\n");
@@ -1903,7 +1479,8 @@ void print_help(void) {
1903 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)")); 1479 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1904 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); 1480 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1905 printf(" %s\n", "-I, --IP-address=ADDRESS"); 1481 printf(" %s\n", "-I, --IP-address=ADDRESS");
1906 printf(" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); 1482 printf(" %s\n",
1483 _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1907 printf(" %s\n", "-p, --port=INTEGER"); 1484 printf(" %s\n", "-p, --port=INTEGER");
1908 printf(" %s", _("Port number (default: ")); 1485 printf(" %s", _("Port number (default: "));
1909 printf("%d)\n", HTTP_PORT); 1486 printf("%d)\n", HTTP_PORT);
@@ -1912,27 +1489,36 @@ void print_help(void) {
1912 1489
1913#ifdef LIBCURL_FEATURE_SSL 1490#ifdef LIBCURL_FEATURE_SSL
1914 printf(" %s\n", "-S, --ssl=VERSION[+]"); 1491 printf(" %s\n", "-S, --ssl=VERSION[+]");
1915 printf(" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); 1492 printf(" %s\n",
1493 _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1916 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); 1494 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1917 printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); 1495 printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are "
1918 printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually disabled in libcurl")); 1496 "also accepted."));
1497 printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually "
1498 "disabled in libcurl"));
1919 printf(" %s\n", "--sni"); 1499 printf(" %s\n", "--sni");
1920 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 1500 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1921# if LIBCURL_VERSION_NUM >= 0x071801 1501# if LIBCURL_VERSION_NUM >= 0x071801
1922 printf(" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); 1502 printf(" %s\n",
1503 _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
1923 printf(" %s\n", _(" SNI only really works since TLSv1.0")); 1504 printf(" %s\n", _(" SNI only really works since TLSv1.0"));
1924# else 1505# else
1925 printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); 1506 printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
1926# endif 1507# endif
1927 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); 1508 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1928 printf(" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); 1509 printf(" %s\n",
1929 printf(" %s\n", _("A STATE_WARNING is returned if the certificate has a validity less than the")); 1510 _("Minimum number of days a certificate has to be valid. Port defaults to 443."));
1930 printf(" %s\n", _("first agument's value. If there is a second argument and the certificate's")); 1511 printf(" %s\n",
1512 _("A STATE_WARNING is returned if the certificate has a validity less than the"));
1513 printf(" %s\n",
1514 _("first agument's value. If there is a second argument and the certificate's"));
1931 printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned.")); 1515 printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned."));
1932 printf(" %s\n", _("(When this option is used the URL is not checked by default. You can use")); 1516 printf(" %s\n",
1517 _("(When this option is used the URL is not checked by default. You can use"));
1933 printf(" %s\n", _(" --continue-after-certificate to override this behavior)")); 1518 printf(" %s\n", _(" --continue-after-certificate to override this behavior)"));
1934 printf(" %s\n", "--continue-after-certificate"); 1519 printf(" %s\n", "--continue-after-certificate");
1935 printf(" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); 1520 printf(" %s\n",
1521 _("Allows the HTTP check to continue after performing the certificate check."));
1936 printf(" %s\n", _("Does nothing unless -C is used.")); 1522 printf(" %s\n", _("Does nothing unless -C is used."));
1937 printf(" %s\n", "-J, --client-cert=FILE"); 1523 printf(" %s\n", "-J, --client-cert=FILE");
1938 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)")); 1524 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)"));
@@ -1950,7 +1536,8 @@ void print_help(void) {
1950 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); 1536 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1951 printf(" %s", _("the first (status) line of the server response (default: ")); 1537 printf(" %s", _("the first (status) line of the server response (default: "));
1952 printf("%s)\n", HTTP_EXPECT); 1538 printf("%s)\n", HTTP_EXPECT);
1953 printf(" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); 1539 printf(" %s\n",
1540 _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1954 printf(" %s\n", "-d, --header-string=STRING"); 1541 printf(" %s\n", "-d, --header-string=STRING");
1955 printf(" %s\n", _("String to expect in the response headers")); 1542 printf(" %s\n", _("String to expect in the response headers"));
1956 printf(" %s\n", "-s, --string=STRING"); 1543 printf(" %s\n", "-s, --string=STRING");
@@ -1959,7 +1546,8 @@ void print_help(void) {
1959 printf(" %s\n", _("URL to GET or POST (default: /)")); 1546 printf(" %s\n", _("URL to GET or POST (default: /)"));
1960 printf(" %s\n", "-P, --post=STRING"); 1547 printf(" %s\n", "-P, --post=STRING");
1961 printf(" %s\n", _("URL decoded http POST data")); 1548 printf(" %s\n", _("URL decoded http POST data"));
1962 printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); 1549 printf(" %s\n",
1550 "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1963 printf(" %s\n", _("Set HTTP method.")); 1551 printf(" %s\n", _("Set HTTP method."));
1964 printf(" %s\n", "-N, --no-body"); 1552 printf(" %s\n", "-N, --no-body");
1965 printf(" %s\n", _("Don't wait for document body: stop reading after headers.")); 1553 printf(" %s\n", _("Don't wait for document body: stop reading after headers."));
@@ -1979,7 +1567,8 @@ void print_help(void) {
1979 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); 1567 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
1980 printf(" %s\n", _("can be changed with --state--regex)")); 1568 printf(" %s\n", _("can be changed with --state--regex)"));
1981 printf(" %s\n", "--state-regex=STATE"); 1569 printf(" %s\n", "--state-regex=STATE");
1982 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of \"critical\",\"warning\"")); 1570 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of "
1571 "\"critical\",\"warning\""));
1983 printf(" %s\n", "-a, --authorization=AUTH_PAIR"); 1572 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1984 printf(" %s\n", _("Username:password on sites with basic authentication")); 1573 printf(" %s\n", _("Username:password on sites with basic authentication"));
1985 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); 1574 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
@@ -1987,13 +1576,14 @@ void print_help(void) {
1987 printf(" %s\n", "-A, --useragent=STRING"); 1576 printf(" %s\n", "-A, --useragent=STRING");
1988 printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); 1577 printf(" %s\n", _("String to be sent in http header as \"User Agent\""));
1989 printf(" %s\n", "-k, --header=STRING"); 1578 printf(" %s\n", "-k, --header=STRING");
1990 printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); 1579 printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for "
1580 "additional headers"));
1991 printf(" %s\n", "-E, --extended-perfdata"); 1581 printf(" %s\n", "-E, --extended-perfdata");
1992 printf(" %s\n", _("Print additional performance data")); 1582 printf(" %s\n", _("Print additional performance data"));
1993 printf(" %s\n", "-B, --show-body"); 1583 printf(" %s\n", "-B, --show-body");
1994 printf(" %s\n", _("Print body content below status line")); 1584 printf(" %s\n", _("Print body content below status line"));
1995 printf(" %s\n", "-L, --link"); 1585 // printf(" %s\n", "-L, --link");
1996 printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); 1586 // printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1997 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>"); 1587 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
1998 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); 1588 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1999 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); 1589 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
@@ -2003,20 +1593,25 @@ void print_help(void) {
2003 printf(" %s", _("Maximal number of redirects (default: ")); 1593 printf(" %s", _("Maximal number of redirects (default: "));
2004 printf("%d)\n", DEFAULT_MAX_REDIRS); 1594 printf("%d)\n", DEFAULT_MAX_REDIRS);
2005 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); 1595 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
2006 printf(" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); 1596 printf(" %s\n",
1597 _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
2007 printf("\n"); 1598 printf("\n");
2008 printf(" %s\n", "--http-version=VERSION"); 1599 printf(" %s\n", "--http-version=VERSION");
2009 printf(" %s\n", _("Connect via specific HTTP protocol.")); 1600 printf(" %s\n", _("Connect via specific HTTP protocol."));
2010 printf(" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); 1601 printf(" %s\n",
1602 _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)"));
2011 printf(" %s\n", "--enable-automatic-decompression"); 1603 printf(" %s\n", "--enable-automatic-decompression");
2012 printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING).")); 1604 printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING)."));
2013 printf(" %s\n", "--haproxy-protocol"); 1605 printf(" %s\n", "--haproxy-protocol");
2014 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL).")); 1606 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL)."));
2015 printf(" %s\n", "--cookie-jar=FILE"); 1607 printf(" %s\n", "--cookie-jar=FILE");
2016 printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested.")); 1608 printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested."));
2017 printf(" %s\n", _("Specify an empty string as FILE to enable curl's cookie engine without saving")); 1609 printf(" %s\n",
2018 printf(" %s\n", _("the cookies to disk. Only enabling the engine without saving to disk requires")); 1610 _("Specify an empty string as FILE to enable curl's cookie engine without saving"));
2019 printf(" %s\n", _("handling multiple requests internally to curl, so use it with --onredirect=curl")); 1611 printf(" %s\n",
1612 _("the cookies to disk. Only enabling the engine without saving to disk requires"));
1613 printf(" %s\n",
1614 _("handling multiple requests internally to curl, so use it with --onredirect=curl"));
2020 printf("\n"); 1615 printf("\n");
2021 1616
2022 printf(UT_WARN_CRIT); 1617 printf(UT_WARN_CRIT);
@@ -2025,13 +1620,18 @@ void print_help(void) {
2025 1620
2026 printf(UT_VERBOSE); 1621 printf(UT_VERBOSE);
2027 1622
1623 printf(UT_OUTPUT_FORMAT);
1624
2028 printf("\n"); 1625 printf("\n");
2029 printf("%s\n", _("Notes:")); 1626 printf("%s\n", _("Notes:"));
2030 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); 1627 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
2031 printf(" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); 1628 printf(" %s\n",
2032 printf(" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); 1629 _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1630 printf(" %s\n",
1631 _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
2033 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); 1632 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
2034 printf(" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); 1633 printf(" %s\n",
1634 _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
2035 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); 1635 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
2036 1636
2037#ifdef LIBCURL_FEATURE_SSL 1637#ifdef LIBCURL_FEATURE_SSL
@@ -2047,38 +1647,53 @@ void print_help(void) {
2047 printf("%s\n", _("Examples:")); 1647 printf("%s\n", _("Examples:"));
2048 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); 1648 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
2049 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); 1649 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
2050 printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1650 printf(" %s\n",
2051 printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1651 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1652 printf(" %s\n",
1653 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2052 printf(" %s\n", _("a STATE_CRITICAL will be returned.")); 1654 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
2053 printf("\n"); 1655 printf("\n");
2054 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); 1656 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
2055 printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); 1657 printf(" %s\n",
2056 printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1658 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
2057 printf(" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); 1659 printf(" %s\n",
1660 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1661 printf(" %s\n",
1662 _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
2058 printf(" %s\n\n", _("the certificate is expired.")); 1663 printf(" %s\n\n", _("the certificate is expired."));
2059 printf("\n"); 1664 printf("\n");
2060 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); 1665 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
2061 printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); 1666 printf(" %s\n",
2062 printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1667 _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1668 printf(" %s\n",
1669 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
2063 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); 1670 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
2064 printf(" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); 1671 printf(" %s\n",
1672 _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
2065#endif 1673#endif
2066 1674
2067 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); 1675 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
2068 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1676 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
2069 printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); 1677 printf(" %s\n",
1678 _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
2070 printf(" %s\n", _("legacy proxy requests in check_http style still work:")); 1679 printf(" %s\n", _("legacy proxy requests in check_http style still work:"));
2071 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org")); 1680 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ "
1681 "-H www.monitoring-plugins.org"));
2072 1682
2073#ifdef LIBCURL_FEATURE_SSL 1683#ifdef LIBCURL_FEATURE_SSL
2074 printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); 1684 printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
2075 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1685 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
2076 printf(" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); 1686 printf(" %s\n",
1687 _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
2077 printf(" %s\n", _("legacy proxy requests in check_http style still work:")); 1688 printf(" %s\n", _("legacy proxy requests in check_http style still work:"));
2078 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); 1689 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j "
2079 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>")); 1690 "CONNECT -H www.verisign.com "));
2080 printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1691 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
2081 printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1692 "-S(sl) -j CONNECT -H <webserver>"));
1693 printf(" %s\n",
1694 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1695 printf(" %s\n",
1696 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2082 printf(" %s\n", _("a STATE_CRITICAL will be returned.")); 1697 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
2083 1698
2084#endif 1699#endif
@@ -2089,10 +1704,12 @@ void print_help(void) {
2089void print_usage(void) { 1704void print_usage(void) {
2090 printf("%s\n", _("Usage:")); 1705 printf("%s\n", _("Usage:"));
2091 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname); 1706 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
2092 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>] [-D]\n"); 1707 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate "
1708 "file>] [-D]\n");
2093 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1709 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
2094 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n"); 1710 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n");
2095 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); 1711 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
1712 "regex>]\n");
2096 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1713 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
2097 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n"); 1714 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
2098 printf(" [-T <content-type>] [-j method]\n"); 1715 printf(" [-T <content-type>] [-j method]\n");
@@ -2109,435 +1726,49 @@ void print_usage(void) {
2109 1726
2110void print_curl_version(void) { printf("%s\n", curl_version()); } 1727void print_curl_version(void) { printf("%s\n", curl_version()); }
2111 1728
2112int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) {
2113 buf->bufsize = DEFAULT_BUFFER_SIZE;
2114 buf->buflen = 0;
2115 buf->buf = (char *)malloc((size_t)buf->bufsize);
2116 if (buf->buf == NULL)
2117 return -1;
2118 return 0;
2119}
2120
2121size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
2122 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
2123
2124 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
2125 buf->bufsize = buf->bufsize * 2;
2126 buf->buf = (char *)realloc(buf->buf, buf->bufsize);
2127 if (buf->buf == NULL) {
2128 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
2129 return -1;
2130 }
2131 }
2132
2133 memcpy(buf->buf + buf->buflen, buffer, size * nmemb);
2134 buf->buflen += size * nmemb;
2135 buf->buf[buf->buflen] = '\0';
2136
2137 return (int)(size * nmemb);
2138}
2139
2140size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
2141 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
2142
2143 size_t n = min(nmemb * size, buf->buflen - buf->pos);
2144
2145 memcpy(buffer, buf->buf + buf->pos, n);
2146 buf->pos += n;
2147
2148 return (int)n;
2149}
2150
2151void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) {
2152 free(buf->buf);
2153 buf->buf = NULL;
2154}
2155
2156int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) {
2157 buf->buflen = datalen;
2158 buf->buf = (char *)malloc((size_t)buf->buflen);
2159 if (buf->buf == NULL)
2160 return -1;
2161 memcpy(buf->buf, data, datalen);
2162 buf->pos = 0;
2163 return 0;
2164}
2165
2166void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) {
2167 free(buf->buf);
2168 buf->buf = NULL;
2169}
2170
2171/* TODO: where to put this, it's actually part of sstrings2 (logically)?
2172 */
2173const char *strrstr2(const char *haystack, const char *needle) {
2174 int counter;
2175 size_t len;
2176 const char *prev_pos;
2177 const char *pos;
2178
2179 if (haystack == NULL || needle == NULL)
2180 return NULL;
2181
2182 if (haystack[0] == '\0' || needle[0] == '\0')
2183 return NULL;
2184
2185 counter = 0;
2186 prev_pos = NULL;
2187 pos = haystack;
2188 len = strlen(needle);
2189 for (;;) {
2190 pos = strstr(pos, needle);
2191 if (pos == NULL) {
2192 if (counter == 0)
2193 return NULL;
2194 return prev_pos;
2195 }
2196 counter++;
2197 prev_pos = pos;
2198 pos += len;
2199 if (*pos == '\0')
2200 return prev_pos;
2201 }
2202}
2203
2204int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) {
2205 char *first_line_end;
2206 char *p;
2207 size_t first_line_len;
2208 char *pp;
2209 const char *start;
2210 char *first_line_buf;
2211
2212 /* find last start of a new header */
2213 start = strrstr2(buf, "\r\nHTTP/");
2214 if (start != NULL) {
2215 start += 2;
2216 buf = start;
2217 }
2218
2219 first_line_end = strstr(buf, "\r\n");
2220 if (first_line_end == NULL)
2221 return -1;
2222
2223 first_line_len = (size_t)(first_line_end - buf);
2224 status_line->first_line = (char *)malloc(first_line_len + 1);
2225 if (status_line->first_line == NULL)
2226 return -1;
2227 memcpy(status_line->first_line, buf, first_line_len);
2228 status_line->first_line[first_line_len] = '\0';
2229 first_line_buf = strdup(status_line->first_line);
2230
2231 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
2232
2233 p = strtok(first_line_buf, "/");
2234 if (p == NULL) {
2235 free(first_line_buf);
2236 return -1;
2237 }
2238 if (strcmp(p, "HTTP") != 0) {
2239 free(first_line_buf);
2240 return -1;
2241 }
2242
2243 p = strtok(NULL, " ");
2244 if (p == NULL) {
2245 free(first_line_buf);
2246 return -1;
2247 }
2248 if (strchr(p, '.') != NULL) {
2249
2250 /* HTTP 1.x case */
2251 strtok(p, ".");
2252 status_line->http_major = (int)strtol(p, &pp, 10);
2253 if (*pp != '\0') {
2254 free(first_line_buf);
2255 return -1;
2256 }
2257 strtok(NULL, " ");
2258 status_line->http_minor = (int)strtol(p, &pp, 10);
2259 if (*pp != '\0') {
2260 free(first_line_buf);
2261 return -1;
2262 }
2263 p += 4; /* 1.x SP */
2264 } else {
2265 /* HTTP 2 case */
2266 status_line->http_major = (int)strtol(p, &pp, 10);
2267 status_line->http_minor = 0;
2268 p += 2; /* 2 SP */
2269 }
2270
2271 /* status code: "404" or "404.1", then SP */
2272
2273 p = strtok(p, " ");
2274 if (p == NULL) {
2275 free(first_line_buf);
2276 return -1;
2277 }
2278 if (strchr(p, '.') != NULL) {
2279 char *ppp;
2280 ppp = strtok(p, ".");
2281 status_line->http_code = (int)strtol(ppp, &pp, 10);
2282 if (*pp != '\0') {
2283 free(first_line_buf);
2284 return -1;
2285 }
2286 ppp = strtok(NULL, "");
2287 status_line->http_subcode = (int)strtol(ppp, &pp, 10);
2288 if (*pp != '\0') {
2289 free(first_line_buf);
2290 return -1;
2291 }
2292 p += 6; /* 400.1 SP */
2293 } else {
2294 status_line->http_code = (int)strtol(p, &pp, 10);
2295 status_line->http_subcode = -1;
2296 if (*pp != '\0') {
2297 free(first_line_buf);
2298 return -1;
2299 }
2300 p += 4; /* 400 SP */
2301 }
2302
2303 /* Human readable message: "Not Found" CRLF */
2304
2305 p = strtok(p, "");
2306 if (p == NULL) {
2307 status_line->msg = "";
2308 return 0;
2309 }
2310 status_line->msg = status_line->first_line + (p - first_line_buf);
2311 free(first_line_buf);
2312
2313 return 0;
2314}
2315
2316void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); }
2317
2318char *get_header_value(const struct phr_header *headers, const size_t nof_headers, const char *header) {
2319 for (size_t i = 0; i < nof_headers; i++) {
2320 if (headers[i].name != NULL && strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) {
2321 return strndup(headers[i].value, headers[i].value_len);
2322 }
2323 }
2324 return NULL;
2325}
2326
2327int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE]) {
2328 char *server_date = NULL;
2329 char *document_date = NULL;
2330 int date_result = STATE_OK;
2331 curlhelp_statusline status_line;
2332 struct phr_header headers[255];
2333 size_t nof_headers = 255;
2334 size_t msglen;
2335
2336 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
2337 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
2338
2339 if (res == -1) {
2340 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2341 }
2342
2343 server_date = get_header_value(headers, nof_headers, "date");
2344 document_date = get_header_value(headers, nof_headers, "last-modified");
2345
2346 if (!server_date || !*server_date) {
2347 char tmp[DEFAULT_BUFFER_SIZE];
2348
2349 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg);
2350 strcpy(*msg, tmp);
2351
2352 date_result = max_state_alt(STATE_UNKNOWN, date_result);
2353
2354 } else if (!document_date || !*document_date) {
2355 char tmp[DEFAULT_BUFFER_SIZE];
2356
2357 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg);
2358 strcpy(*msg, tmp);
2359
2360 date_result = max_state_alt(STATE_CRITICAL, date_result);
2361
2362 } else {
2363 time_t srv_data = curl_getdate(server_date, NULL);
2364 time_t doc_data = curl_getdate(document_date, NULL);
2365 if (verbose >= 2)
2366 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data);
2367 if (srv_data <= 0) {
2368 char tmp[DEFAULT_BUFFER_SIZE];
2369
2370 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
2371 strcpy(*msg, tmp);
2372
2373 date_result = max_state_alt(STATE_CRITICAL, date_result);
2374 } else if (doc_data <= 0) {
2375 char tmp[DEFAULT_BUFFER_SIZE];
2376
2377 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
2378 strcpy(*msg, tmp);
2379
2380 date_result = max_state_alt(STATE_CRITICAL, date_result);
2381 } else if (doc_data > srv_data + 30) {
2382 char tmp[DEFAULT_BUFFER_SIZE];
2383
2384 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
2385 strcpy(*msg, tmp);
2386
2387 date_result = max_state_alt(STATE_CRITICAL, date_result);
2388 } else if (doc_data < srv_data - maximum_age) {
2389 int n = (srv_data - doc_data);
2390 if (n > (60 * 60 * 24 * 2)) {
2391 char tmp[DEFAULT_BUFFER_SIZE];
2392
2393 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float)n) / (60 * 60 * 24));
2394 strcpy(*msg, tmp);
2395
2396 date_result = max_state_alt(STATE_CRITICAL, date_result);
2397 } else {
2398 char tmp[DEFAULT_BUFFER_SIZE];
2399
2400 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
2401 strcpy(*msg, tmp);
2402
2403 date_result = max_state_alt(STATE_CRITICAL, date_result);
2404 }
2405 }
2406 }
2407
2408 if (server_date)
2409 free(server_date);
2410 if (document_date)
2411 free(document_date);
2412
2413 return date_result;
2414}
2415
2416int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf) {
2417 size_t content_length = 0;
2418 struct phr_header headers[255];
2419 size_t nof_headers = 255;
2420 size_t msglen;
2421 char *content_length_s = NULL;
2422 curlhelp_statusline status_line;
2423
2424 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
2425 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
2426
2427 if (res == -1) {
2428 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2429 }
2430
2431 content_length_s = get_header_value(headers, nof_headers, "content-length");
2432 if (!content_length_s) {
2433 return header_buf->buflen + body_buf->buflen;
2434 }
2435 content_length_s += strspn(content_length_s, " \t");
2436 content_length = atoi(content_length_s);
2437 if (content_length != body_buf->buflen) {
2438 /* TODO: should we warn if the actual and the reported body length don't match? */
2439 }
2440
2441 if (content_length_s)
2442 free(content_length_s);
2443
2444 return header_buf->buflen + body_buf->buflen;
2445}
2446
2447/* TODO: is there a better way in libcurl to check for the SSL library? */
2448curlhelp_ssl_library curlhelp_get_ssl_library(void) {
2449 curl_version_info_data *version_data;
2450 char *ssl_version;
2451 char *library;
2452 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
2453
2454 version_data = curl_version_info(CURLVERSION_NOW);
2455 if (version_data == NULL)
2456 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2457
2458 ssl_version = strdup(version_data->ssl_version);
2459 if (ssl_version == NULL)
2460 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2461
2462 library = strtok(ssl_version, "/");
2463 if (library == NULL)
2464 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2465
2466 if (strcmp(library, "OpenSSL") == 0)
2467 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
2468 else if (strcmp(library, "LibreSSL") == 0)
2469 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
2470 else if (strcmp(library, "GnuTLS") == 0)
2471 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
2472 else if (strcmp(library, "NSS") == 0)
2473 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
2474
2475 if (verbose >= 2)
2476 printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library);
2477
2478 free(ssl_version);
2479
2480 return ssl_library;
2481}
2482
2483const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library ssl_library) {
2484 switch (ssl_library) {
2485 case CURLHELP_SSL_LIBRARY_OPENSSL:
2486 return "OpenSSL";
2487 case CURLHELP_SSL_LIBRARY_LIBRESSL:
2488 return "LibreSSL";
2489 case CURLHELP_SSL_LIBRARY_GNUTLS:
2490 return "GnuTLS";
2491 case CURLHELP_SSL_LIBRARY_NSS:
2492 return "NSS";
2493 case CURLHELP_SSL_LIBRARY_UNKNOWN:
2494 default:
2495 return "unknown";
2496 }
2497}
2498
2499#ifdef LIBCURL_FEATURE_SSL 1729#ifdef LIBCURL_FEATURE_SSL
2500# ifndef USE_OPENSSL 1730# ifndef USE_OPENSSL
2501time_t parse_cert_date(const char *s) { 1731time_t parse_cert_date(const char *s) {
2502 struct tm tm; 1732 if (!s) {
2503 time_t date;
2504 char *res;
2505
2506 if (!s)
2507 return -1; 1733 return -1;
1734 }
2508 1735
2509 /* Jan 17 14:25:12 2020 GMT */ 1736 /* Jan 17 14:25:12 2020 GMT */
2510 res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); 1737 struct tm tm;
1738 char *res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm);
2511 /* Sep 11 12:00:00 2020 GMT */ 1739 /* Sep 11 12:00:00 2020 GMT */
2512 if (res == NULL) 1740 if (res == NULL) {
2513 strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); 1741 strptime(s, "%Y %m %d %H:%M:%S GMT", &tm);
2514 date = mktime(&tm); 1742 }
1743 time_t date = mktime(&tm);
2515 1744
2516 return date; 1745 return date;
2517} 1746}
1747# endif /* USE_OPENSSL */
1748#endif /* LIBCURL_FEATURE_SSL */
2518 1749
1750#ifdef LIBCURL_FEATURE_SSL
1751# ifndef USE_OPENSSL
2519/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to 1752/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to
2520 * OpenSSL could be this function 1753 * OpenSSL could be this function
2521 */ 1754 */
2522int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { 1755int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn,
2523 int i; 1756 int days_till_exp_crit) {
2524 struct curl_slist *slist;
2525 int cname_found = 0;
2526 char *start_date_str = NULL;
2527 char *end_date_str = NULL;
2528 time_t start_date;
2529 time_t end_date;
2530 char *tz;
2531 float time_left;
2532 int days_left;
2533 int time_remaining;
2534 char timestamp[50] = "";
2535 int status = STATE_UNKNOWN;
2536 1757
2537 if (verbose >= 2) 1758 if (verbose >= 2) {
2538 printf("**** REQUEST CERTIFICATES ****\n"); 1759 printf("**** REQUEST CERTIFICATES ****\n");
1760 }
1761
1762 char *start_date_str = NULL;
1763 char *end_date_str = NULL;
1764 bool have_first_cert = false;
1765 bool cname_found = false;
1766 for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) {
1767 if (have_first_cert) {
1768 break;
1769 }
2539 1770
2540 for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { 1771 struct curl_slist *slist;
2541 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { 1772 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) {
2542 /* find first common name in subject, 1773 /* find first common name in subject,
2543 * TODO: check alternative subjects for 1774 * TODO: check alternative subjects for
@@ -2553,7 +1784,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_
2553 } 1784 }
2554 if (p != NULL) { 1785 if (p != NULL) {
2555 if (strncmp(host_name, p + d, strlen(host_name)) == 0) { 1786 if (strncmp(host_name, p + d, strlen(host_name)) == 0) {
2556 cname_found = 1; 1787 cname_found = true;
2557 } 1788 }
2558 } 1789 }
2559 } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) { 1790 } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) {
@@ -2561,78 +1792,93 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_
2561 } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) { 1792 } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) {
2562 end_date_str = &slist->data[12]; 1793 end_date_str = &slist->data[12];
2563 } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { 1794 } else if (strncasecmp(slist->data, "Cert:", 5) == 0) {
2564 goto HAVE_FIRST_CERT; 1795 have_first_cert = true;
1796 break;
2565 } 1797 }
2566 if (verbose >= 2) 1798 if (verbose >= 2) {
2567 printf("%d ** %s\n", i, slist->data); 1799 printf("%d ** %s\n", i, slist->data);
1800 }
2568 } 1801 }
2569 } 1802 }
2570HAVE_FIRST_CERT:
2571 1803
2572 if (verbose >= 2) 1804 if (verbose >= 2) {
2573 printf("**** REQUEST CERTIFICATES ****\n"); 1805 printf("**** REQUEST CERTIFICATES ****\n");
1806 }
2574 1807
2575 if (!cname_found) { 1808 if (!cname_found) {
2576 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); 1809 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
2577 return STATE_CRITICAL; 1810 return STATE_CRITICAL;
2578 } 1811 }
2579 1812
2580 start_date = parse_cert_date(start_date_str); 1813 time_t start_date = parse_cert_date(start_date_str);
2581 if (start_date <= 0) { 1814 if (start_date <= 0) {
2582 snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); 1815 snprintf(msg, DEFAULT_BUFFER_SIZE,
1816 _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str);
2583 puts(msg); 1817 puts(msg);
2584 return STATE_WARNING; 1818 return STATE_WARNING;
2585 } 1819 }
2586 1820
2587 end_date = parse_cert_date(end_date_str); 1821 time_t end_date = parse_cert_date(end_date_str);
2588 if (end_date <= 0) { 1822 if (end_date <= 0) {
2589 snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); 1823 snprintf(msg, DEFAULT_BUFFER_SIZE,
1824 _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str);
2590 puts(msg); 1825 puts(msg);
2591 return STATE_WARNING; 1826 return STATE_WARNING;
2592 } 1827 }
2593 1828
2594 time_left = difftime(end_date, time(NULL)); 1829 float time_left = difftime(end_date, time(NULL));
2595 days_left = time_left / 86400; 1830 int days_left = time_left / 86400;
2596 tz = getenv("TZ"); 1831 char *tz = getenv("TZ");
2597 setenv("TZ", "GMT", 1); 1832 setenv("TZ", "GMT", 1);
2598 tzset(); 1833 tzset();
1834
1835 char timestamp[50] = "";
2599 strftime(timestamp, 50, "%c %z", localtime(&end_date)); 1836 strftime(timestamp, 50, "%c %z", localtime(&end_date));
2600 if (tz) 1837 if (tz) {
2601 setenv("TZ", tz, 1); 1838 setenv("TZ", tz, 1);
2602 else 1839 } else {
2603 unsetenv("TZ"); 1840 unsetenv("TZ");
1841 }
2604 tzset(); 1842 tzset();
2605 1843
1844 mp_state_enum status = STATE_UNKNOWN;
1845 int time_remaining;
2606 if (days_left > 0 && days_left <= days_till_exp_warn) { 1846 if (days_left > 0 && days_left <= days_till_exp_warn) {
2607 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", 1847 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
2608 host_name, days_left, timestamp); 1848 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left,
2609 if (days_left > days_till_exp_crit) 1849 timestamp);
1850 if (days_left > days_till_exp_crit) {
2610 status = STATE_WARNING; 1851 status = STATE_WARNING;
2611 else 1852 } else {
2612 status = STATE_CRITICAL; 1853 status = STATE_CRITICAL;
1854 }
2613 } else if (days_left == 0 && time_left > 0) { 1855 } else if (days_left == 0 && time_left > 0) {
2614 if (time_left >= 3600) 1856 if (time_left >= 3600) {
2615 time_remaining = (int)time_left / 3600; 1857 time_remaining = (int)time_left / 3600;
2616 else 1858 } else {
2617 time_remaining = (int)time_left / 60; 1859 time_remaining = (int)time_left / 60;
1860 }
2618 1861
2619 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, 1862 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
2620 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); 1863 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining,
1864 time_left >= 3600 ? "hours" : "minutes", timestamp);
2621 1865
2622 if (days_left > days_till_exp_crit) 1866 if (days_left > days_till_exp_crit) {
2623 status = STATE_WARNING; 1867 status = STATE_WARNING;
2624 else 1868 } else {
2625 status = STATE_CRITICAL; 1869 status = STATE_CRITICAL;
1870 }
2626 } else if (time_left < 0) { 1871 } else if (time_left < 0) {
2627 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); 1872 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp);
2628 status = STATE_CRITICAL; 1873 status = STATE_CRITICAL;
2629 } else if (days_left == 0) { 1874 } else if (days_left == 0) {
2630 printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, 1875 printf(_("%s - Certificate '%s' just expired (%s).\n"),
2631 timestamp); 1876 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp);
2632 if (days_left > days_till_exp_crit) 1877 if (days_left > days_till_exp_crit) {
2633 status = STATE_WARNING; 1878 status = STATE_WARNING;
2634 else 1879 } else {
2635 status = STATE_CRITICAL; 1880 status = STATE_CRITICAL;
1881 }
2636 } else { 1882 } else {
2637 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp); 1883 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp);
2638 status = STATE_OK; 1884 status = STATE_OK;
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..c3c2ba55
--- /dev/null
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -0,0 +1,1267 @@
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 <stdlib.h>
7#include "../utils.h"
8#include "check_curl.d/config.h"
9#include "output.h"
10#include "perfdata.h"
11#include "states.h"
12
13extern int verbose;
14char errbuf[MAX_INPUT_BUFFER];
15bool is_openssl_callback = false;
16bool add_sslctx_verify_fun = false;
17
18check_curl_configure_curl_wrapper
19check_curl_configure_curl(const check_curl_static_curl_config config,
20 check_curl_working_state working_state, bool check_cert,
21 bool on_redirect_dependent, int follow_method, int max_depth) {
22 check_curl_configure_curl_wrapper result = {
23 .errorcode = OK,
24 .curl_state =
25 {
26 .curl_global_initialized = false,
27 .curl_easy_initialized = false,
28 .curl = NULL,
29
30 .body_buf_initialized = false,
31 .body_buf = NULL,
32 .header_buf_initialized = false,
33 .header_buf = NULL,
34 .status_line_initialized = false,
35 .status_line = NULL,
36 .put_buf_initialized = false,
37 .put_buf = NULL,
38
39 .header_list = NULL,
40 .host = NULL,
41 },
42 };
43
44 if ((result.curl_state.status_line = calloc(1, sizeof(curlhelp_statusline))) == NULL) {
45 die(STATE_UNKNOWN, "HTTP UNKNOWN - allocation of statusline failed\n");
46 }
47
48 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
49 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
50 }
51 result.curl_state.curl_global_initialized = true;
52
53 if ((result.curl_state.curl = curl_easy_init()) == NULL) {
54 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
55 }
56 result.curl_state.curl_easy_initialized = true;
57
58 if (verbose >= 1) {
59 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1),
60 "CURLOPT_VERBOSE");
61 }
62
63 /* print everything on stdout like check_http would do */
64 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout),
65 "CURLOPT_STDERR");
66
67 if (config.automatic_decompression) {
68#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
69 handle_curl_option_return_code(
70 curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""),
71 "CURLOPT_ACCEPT_ENCODING");
72#else
73 handle_curl_option_return_code(
74 curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
75#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
76 }
77
78 /* initialize buffer for body of the answer */
79 if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) {
80 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
81 }
82 result.curl_state.body_buf_initialized = true;
83
84 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION,
85 curlhelp_buffer_write_callback),
86 "CURLOPT_WRITEFUNCTION");
87 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA,
88 (void *)result.curl_state.body_buf),
89 "CURLOPT_WRITEDATA");
90
91 /* initialize buffer for header of the answer */
92 if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) {
93 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n");
94 }
95 result.curl_state.header_buf_initialized = true;
96
97 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION,
98 curlhelp_buffer_write_callback),
99 "CURLOPT_HEADERFUNCTION");
100 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER,
101 (void *)result.curl_state.header_buf),
102 "CURLOPT_WRITEHEADER");
103
104 /* set the error buffer */
105 handle_curl_option_return_code(
106 curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf),
107 "CURLOPT_ERRORBUFFER");
108
109 /* set timeouts */
110 handle_curl_option_return_code(
111 curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout),
112 "CURLOPT_CONNECTTIMEOUT");
113
114 handle_curl_option_return_code(
115 curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout),
116 "CURLOPT_TIMEOUT");
117
118 /* enable haproxy protocol */
119 if (config.haproxy_protocol) {
120 handle_curl_option_return_code(
121 curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L),
122 "CURLOPT_HAPROXYPROTOCOL");
123 }
124
125 // fill dns resolve cache to make curl connect to the given server_address instead of the
126 // host_name, only required for ssl, because we use the host_name later on to make SNI happy
127 char dnscache[DEFAULT_BUFFER_SIZE];
128 char addrstr[DEFAULT_BUFFER_SIZE / 2];
129 if (working_state.use_ssl && working_state.host_name != NULL) {
130 int res;
131 if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2,
132 config.sin_family)) != 0) {
133 die(STATE_CRITICAL,
134 _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"),
135 working_state.server_address, res, gai_strerror(res));
136 }
137
138 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name,
139 working_state.serverPort, addrstr);
140 result.curl_state.host = curl_slist_append(NULL, dnscache);
141 curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host);
142
143 if (verbose >= 1) {
144 printf("* curl CURLOPT_RESOLVE: %s\n", dnscache);
145 }
146 }
147
148 // If server_address is an IPv6 address it must be surround by square brackets
149 struct in6_addr tmp_in_addr;
150 if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) {
151 char *new_server_address = calloc(strlen(working_state.server_address) + 3, sizeof(char));
152 if (new_server_address == NULL) {
153 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
154 }
155 snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]",
156 working_state.server_address);
157 working_state.server_address = new_server_address;
158 }
159
160 /* compose URL: use the address we want to connect to, set Host: header later */
161 char *url = fmt_url(working_state);
162
163 if (verbose >= 1) {
164 printf("* curl CURLOPT_URL: %s\n", url);
165 }
166 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url),
167 "CURLOPT_URL");
168
169 free(url);
170
171 /* extract proxy information for legacy proxy https requests */
172 if (!strcmp(working_state.http_method, "CONNECT") ||
173 strstr(working_state.server_url, "http") == working_state.server_url) {
174 handle_curl_option_return_code(
175 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address),
176 "CURLOPT_PROXY");
177 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT,
178 (long)working_state.serverPort),
179 "CURLOPT_PROXYPORT");
180 if (verbose >= 2) {
181 printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address,
182 working_state.serverPort);
183 }
184 working_state.http_method = "GET";
185 handle_curl_option_return_code(
186 curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url),
187 "CURLOPT_URL");
188 }
189
190 /* disable body for HEAD request */
191 if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) {
192 working_state.no_body = true;
193 }
194
195 /* set HTTP protocol version */
196 handle_curl_option_return_code(
197 curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version),
198 "CURLOPT_HTTP_VERSION");
199
200 /* set HTTP method */
201 if (working_state.http_method) {
202 if (!strcmp(working_state.http_method, "POST")) {
203 handle_curl_option_return_code(
204 curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST");
205 } else if (!strcmp(working_state.http_method, "PUT")) {
206 handle_curl_option_return_code(
207 curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
208 } else {
209 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
210 CURLOPT_CUSTOMREQUEST,
211 working_state.http_method),
212 "CURLOPT_CUSTOMREQUEST");
213 }
214 }
215
216 char *force_host_header = NULL;
217 /* check if Host header is explicitly set in options */
218 if (config.http_opt_headers_count) {
219 for (size_t i = 0; i < config.http_opt_headers_count; i++) {
220 if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) {
221 force_host_header = config.http_opt_headers[i];
222 }
223 }
224 }
225
226 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in
227 * anyway */
228 char http_header[DEFAULT_BUFFER_SIZE];
229 if (working_state.host_name != NULL && force_host_header == NULL) {
230 if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) ||
231 (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) {
232 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name,
233 working_state.virtualPort);
234 } else {
235 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name);
236 }
237 result.curl_state.header_list =
238 curl_slist_append(result.curl_state.header_list, http_header);
239 }
240
241 /* always close connection, be nice to servers */
242 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
243 result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header);
244
245 /* attach additional headers supplied by the user */
246 /* optionally send any other header tag */
247 if (config.http_opt_headers_count) {
248 for (size_t i = 0; i < config.http_opt_headers_count; i++) {
249 result.curl_state.header_list =
250 curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]);
251 }
252 }
253
254 /* set HTTP headers */
255 handle_curl_option_return_code(
256 curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list),
257 "CURLOPT_HTTPHEADER");
258
259#ifdef LIBCURL_FEATURE_SSL
260 /* set SSL version, warn about insecure or unsupported versions */
261 if (working_state.use_ssl) {
262 handle_curl_option_return_code(
263 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version),
264 "CURLOPT_SSLVERSION");
265 }
266
267 /* client certificate and key to present to server (SSL) */
268 if (config.client_cert) {
269 handle_curl_option_return_code(
270 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert),
271 "CURLOPT_SSLCERT");
272 }
273
274 if (config.client_privkey) {
275 handle_curl_option_return_code(
276 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey),
277 "CURLOPT_SSLKEY");
278 }
279
280 if (config.ca_cert) {
281 handle_curl_option_return_code(
282 curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert),
283 "CURLOPT_CAINFO");
284 }
285
286 if (config.ca_cert || config.verify_peer_and_host) {
287 /* per default if we have a CA verify both the peer and the
288 * hostname in the certificate, can be switched off later */
289 handle_curl_option_return_code(
290 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1),
291 "CURLOPT_SSL_VERIFYPEER");
292 handle_curl_option_return_code(
293 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2),
294 "CURLOPT_SSL_VERIFYHOST");
295 } else {
296 /* backward-compatible behaviour, be tolerant in checks
297 * TODO: depending on more options have aspects we want
298 * to be less tolerant about ssl verfications
299 */
300 handle_curl_option_return_code(
301 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0),
302 "CURLOPT_SSL_VERIFYPEER");
303 handle_curl_option_return_code(
304 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0),
305 "CURLOPT_SSL_VERIFYHOST");
306 }
307
308 /* detect SSL library used by libcurl */
309 curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library();
310
311 /* try hard to get a stack of certificates to verify against */
312 if (check_cert) {
313# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
314 /* inform curl to report back certificates */
315 switch (ssl_library) {
316 case CURLHELP_SSL_LIBRARY_OPENSSL:
317 case CURLHELP_SSL_LIBRARY_LIBRESSL:
318 /* set callback to extract certificate with OpenSSL context function (works with
319 * OpenSSL-style libraries only!) */
320# ifdef USE_OPENSSL
321 /* libcurl and monitoring plugins built with OpenSSL, good */
322 add_sslctx_verify_fun = true;
323 is_openssl_callback = true;
324# endif /* USE_OPENSSL */
325 /* libcurl is built with OpenSSL, monitoring plugins, so falling
326 * back to manually extracting certificate information */
327 handle_curl_option_return_code(
328 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
329 break;
330
331 case CURLHELP_SSL_LIBRARY_NSS:
332# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
333 /* NSS: support for CERTINFO is implemented since 7.34.0 */
334 handle_curl_option_return_code(
335 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
336# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
337 die(STATE_CRITICAL,
338 "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library "
339 "'%s' is too old)\n",
340 curlhelp_get_ssl_library_string(ssl_library));
341# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
342 break;
343
344 case CURLHELP_SSL_LIBRARY_GNUTLS:
345# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
346 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
347 handle_curl_option_return_code(
348 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
349# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
350 die(STATE_CRITICAL,
351 "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library "
352 "'%s' is too old)\n",
353 curlhelp_get_ssl_library_string(ssl_library));
354# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
355 break;
356
357 case CURLHELP_SSL_LIBRARY_UNKNOWN:
358 default:
359 die(STATE_CRITICAL,
360 "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must "
361 "implement first)\n",
362 curlhelp_get_ssl_library_string(ssl_library));
363 break;
364 }
365# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
366 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
367 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL ||
368 ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) {
369 add_sslctx_verify_fun = true;
370 } else {
371 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no "
372 "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl "
373 "too old and has no CURLOPT_CERTINFO)\n");
374 }
375# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
376 }
377
378# if LIBCURL_VERSION_NUM >= \
379 MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */
380 // ssl ctx function is not available with all ssl backends
381 if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) !=
382 CURLE_UNKNOWN_OPTION) {
383 handle_curl_option_return_code(
384 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun),
385 "CURLOPT_SSL_CTX_FUNCTION");
386 }
387# endif
388#endif /* LIBCURL_FEATURE_SSL */
389
390 /* set default or user-given user agent identification */
391 handle_curl_option_return_code(
392 curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent),
393 "CURLOPT_USERAGENT");
394
395 /* proxy-authentication */
396 if (strcmp(config.proxy_auth, "")) {
397 handle_curl_option_return_code(
398 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth),
399 "CURLOPT_PROXYUSERPWD");
400 }
401
402 /* authentication */
403 if (strcmp(config.user_auth, "")) {
404 handle_curl_option_return_code(
405 curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth),
406 "CURLOPT_USERPWD");
407 }
408 /* TODO: parameter auth method, bitfield of following methods:
409 * CURLAUTH_BASIC (default)
410 * CURLAUTH_DIGEST
411 * CURLAUTH_DIGEST_IE
412 * CURLAUTH_NEGOTIATE
413 * CURLAUTH_NTLM
414 * CURLAUTH_NTLM_WB
415 *
416 * convenience tokens for typical sets of methods:
417 * CURLAUTH_ANYSAFE: most secure, without BASIC
418 * or CURLAUTH_ANY: most secure, even BASIC if necessary
419 *
420 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH,
421 * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
422 */
423
424 /* handle redirections */
425 if (on_redirect_dependent) {
426 if (follow_method == FOLLOW_LIBCURL) {
427 handle_curl_option_return_code(
428 curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1),
429 "CURLOPT_FOLLOWLOCATION");
430
431 /* default -1 is infinite, not good, could lead to zombie plugins!
432 Setting it to one bigger than maximal limit to handle errors nicely below
433 */
434 handle_curl_option_return_code(
435 curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1),
436 "CURLOPT_MAXREDIRS");
437
438 /* for now allow only http and https (we are a http(s) check plugin in the end) */
439#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
440 handle_curl_option_return_code(
441 curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"),
442 "CURLOPT_REDIR_PROTOCOLS_STR");
443#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
444 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
445 CURLOPT_REDIR_PROTOCOLS,
446 CURLPROTO_HTTP | CURLPROTO_HTTPS),
447 "CURLOPT_REDIRECT_PROTOCOLS");
448#endif
449
450 /* TODO: handle the following aspects of redirection, make them
451 * command line options too later:
452 CURLOPT_POSTREDIR: method switch
453 CURLINFO_REDIRECT_URL: custom redirect option
454 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
455 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range
456 option here is nice like for expected page size?
457 */
458 } else {
459 /* old style redirection*/
460 }
461 }
462 /* no-body */
463 if (working_state.no_body) {
464 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1),
465 "CURLOPT_NOBODY");
466 }
467
468 /* IPv4 or IPv6 forced DNS resolution */
469 if (config.sin_family == AF_UNSPEC) {
470 handle_curl_option_return_code(
471 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER),
472 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
473 } else if (config.sin_family == AF_INET) {
474 handle_curl_option_return_code(
475 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
476 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
477 }
478#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
479 else if (config.sin_family == AF_INET6) {
480 handle_curl_option_return_code(
481 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
482 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
483 }
484#endif
485
486 /* either send http POST data (any data, not only POST)*/
487 if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) {
488 /* set content of payload for POST and PUT */
489 if (config.http_content_type) {
490 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s",
491 config.http_content_type);
492 result.curl_state.header_list =
493 curl_slist_append(result.curl_state.header_list, http_header);
494 }
495 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
496 * in case of no POST/PUT data */
497 if (!working_state.http_post_data) {
498 working_state.http_post_data = "";
499 }
500
501 if (!strcmp(working_state.http_method, "POST")) {
502 /* POST method, set payload with CURLOPT_POSTFIELDS */
503 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
504 CURLOPT_POSTFIELDS,
505 working_state.http_post_data),
506 "CURLOPT_POSTFIELDS");
507 } else if (!strcmp(working_state.http_method, "PUT")) {
508 handle_curl_option_return_code(
509 curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION,
510 (curl_read_callback)curlhelp_buffer_read_callback),
511 "CURLOPT_READFUNCTION");
512 if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data,
513 strlen(working_state.http_post_data)) < 0) {
514 die(STATE_UNKNOWN,
515 "HTTP CRITICAL - out of memory allocating read buffer for PUT\n");
516 }
517 result.curl_state.put_buf_initialized = true;
518 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
519 CURLOPT_READDATA,
520 (void *)result.curl_state.put_buf),
521 "CURLOPT_READDATA");
522 handle_curl_option_return_code(
523 curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE,
524 (curl_off_t)strlen(working_state.http_post_data)),
525 "CURLOPT_INFILESIZE");
526 }
527 }
528
529 /* cookie handling */
530 if (config.cookie_jar_file != NULL) {
531 /* enable reading cookies from a file, and if the filename is an empty string, only
532 * enable the curl cookie engine */
533 handle_curl_option_return_code(
534 curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file),
535 "CURLOPT_COOKIEFILE");
536 /* now enable saving cookies to a file, but only if the filename is not an empty string,
537 * since writing it would fail */
538 if (*config.cookie_jar_file) {
539 handle_curl_option_return_code(
540 curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file),
541 "CURLOPT_COOKIEJAR");
542 }
543 }
544
545 result.working_state = working_state;
546
547 return result;
548}
549
550void handle_curl_option_return_code(CURLcode res, const char *option) {
551 if (res != CURLE_OK) {
552 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"),
553 option, res, curl_easy_strerror(res));
554 }
555}
556
557char *get_header_value(const struct phr_header *headers, const size_t nof_headers,
558 const char *header) {
559 for (size_t i = 0; i < nof_headers; i++) {
560 if (headers[i].name != NULL &&
561 strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) {
562 return strndup(headers[i].value, headers[i].value_len);
563 }
564 }
565 return NULL;
566}
567
568check_curl_working_state check_curl_working_state_init() {
569 check_curl_working_state result = {
570 .server_address = NULL,
571 .server_url = DEFAULT_SERVER_URL,
572 .host_name = NULL,
573 .http_method = NULL,
574 .http_post_data = NULL,
575 .virtualPort = 0,
576 .serverPort = HTTP_PORT,
577 .use_ssl = false,
578 .no_body = false,
579 };
580 return result;
581}
582
583check_curl_config check_curl_config_init() {
584 check_curl_config tmp = {
585 .initial_config = check_curl_working_state_init(),
586
587 .curl_config =
588 {
589 .automatic_decompression = false,
590 .socket_timeout = DEFAULT_SOCKET_TIMEOUT,
591 .haproxy_protocol = false,
592 .sin_family = AF_UNSPEC,
593 .curl_http_version = CURL_HTTP_VERSION_NONE,
594 .http_opt_headers = NULL,
595 .http_opt_headers_count = 0,
596 .ssl_version = CURL_SSLVERSION_DEFAULT,
597 .client_cert = NULL,
598 .client_privkey = NULL,
599 .ca_cert = NULL,
600 .verify_peer_and_host = false,
601 .user_agent = {'\0'},
602 .proxy_auth = "",
603 .user_auth = "",
604 .http_content_type = NULL,
605 .cookie_jar_file = NULL,
606 },
607 .max_depth = DEFAULT_MAX_REDIRS,
608 .followmethod = FOLLOW_HTTP_CURL,
609 .followsticky = STICKY_NONE,
610
611 .maximum_age = -1,
612 .regexp = {},
613 .compiled_regex = {},
614 .state_regex = STATE_CRITICAL,
615 .invert_regex = false,
616 .check_cert = false,
617 .continue_after_check_cert = false,
618 .days_till_exp_warn = 0,
619 .days_till_exp_crit = 0,
620 .thlds = mp_thresholds_init(),
621 .page_length_limits = mp_range_init(),
622 .page_length_limits_is_set = false,
623 .server_expect =
624 {
625 .string = HTTP_EXPECT,
626 .is_present = false,
627 },
628 .string_expect = "",
629 .header_expect = "",
630 .on_redirect_result_state = STATE_OK,
631 .on_redirect_dependent = false,
632
633 .show_extended_perfdata = false,
634 .show_body = false,
635
636 .output_format_is_set = false,
637 };
638
639 snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)",
640 "check_curl", NP_VERSION, VERSION, curl_version());
641
642 return tmp;
643}
644
645/* TODO: is there a better way in libcurl to check for the SSL library? */
646curlhelp_ssl_library curlhelp_get_ssl_library(void) {
647 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
648
649 curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW);
650 if (version_data == NULL) {
651 return CURLHELP_SSL_LIBRARY_UNKNOWN;
652 }
653
654 char *ssl_version = strdup(version_data->ssl_version);
655 if (ssl_version == NULL) {
656 return CURLHELP_SSL_LIBRARY_UNKNOWN;
657 }
658
659 char *library = strtok(ssl_version, "/");
660 if (library == NULL) {
661 return CURLHELP_SSL_LIBRARY_UNKNOWN;
662 }
663
664 if (strcmp(library, "OpenSSL") == 0) {
665 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
666 } else if (strcmp(library, "LibreSSL") == 0) {
667 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
668 } else if (strcmp(library, "GnuTLS") == 0) {
669 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
670 } else if (strcmp(library, "NSS") == 0) {
671 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
672 }
673
674 if (verbose >= 2) {
675 printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library,
676 ssl_library);
677 }
678
679 free(ssl_version);
680
681 return ssl_library;
682}
683
684const char *curlhelp_get_ssl_library_string(const curlhelp_ssl_library ssl_library) {
685 switch (ssl_library) {
686 case CURLHELP_SSL_LIBRARY_OPENSSL:
687 return "OpenSSL";
688 case CURLHELP_SSL_LIBRARY_LIBRESSL:
689 return "LibreSSL";
690 case CURLHELP_SSL_LIBRARY_GNUTLS:
691 return "GnuTLS";
692 case CURLHELP_SSL_LIBRARY_NSS:
693 return "NSS";
694 case CURLHELP_SSL_LIBRARY_UNKNOWN:
695 default:
696 return "unknown";
697 }
698}
699
700size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
701 const curlhelp_write_curlbuf *body_buf) {
702 struct phr_header headers[255];
703 size_t nof_headers = 255;
704 size_t msglen;
705 curlhelp_statusline status_line;
706 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
707 &status_line.http_minor, &status_line.http_code, &status_line.msg,
708 &msglen, headers, &nof_headers, 0);
709
710 if (res == -1) {
711 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
712 }
713
714 char *content_length_s = get_header_value(headers, nof_headers, "content-length");
715 if (!content_length_s) {
716 return header_buf->buflen + body_buf->buflen;
717 }
718
719 content_length_s += strspn(content_length_s, " \t");
720 size_t content_length = atoi(content_length_s);
721 if (content_length != body_buf->buflen) {
722 /* TODO: should we warn if the actual and the reported body length don't match? */
723 }
724
725 if (content_length_s) {
726 free(content_length_s);
727 }
728
729 return header_buf->buflen + body_buf->buflen;
730}
731
732mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const int maximum_age) {
733 struct phr_header headers[255];
734 size_t nof_headers = 255;
735 curlhelp_statusline status_line;
736 size_t msglen;
737 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
738 &status_line.http_minor, &status_line.http_code, &status_line.msg,
739 &msglen, headers, &nof_headers, 0);
740
741 if (res == -1) {
742 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
743 }
744
745 char *server_date = get_header_value(headers, nof_headers, "date");
746 char *document_date = get_header_value(headers, nof_headers, "last-modified");
747
748 mp_subcheck sc_document_dates = mp_subcheck_init();
749 if (!server_date || !*server_date) {
750 xasprintf(&sc_document_dates.output, _("Server date unknown"));
751 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_UNKNOWN);
752 } else if (!document_date || !*document_date) {
753 xasprintf(&sc_document_dates.output, _("Document modification date unknown, "));
754 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
755 } else {
756 time_t srv_data = curl_getdate(server_date, NULL);
757 time_t doc_data = curl_getdate(document_date, NULL);
758
759 if (verbose >= 2) {
760 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data,
761 document_date, (int)doc_data);
762 }
763
764 if (srv_data <= 0) {
765 xasprintf(&sc_document_dates.output, _("Server date \"%100s\" unparsable"),
766 server_date);
767 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
768 } else if (doc_data <= 0) {
769
770 xasprintf(&sc_document_dates.output, _("Document date \"%100s\" unparsable"),
771 document_date);
772 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
773 } else if (doc_data > srv_data + 30) {
774
775 xasprintf(&sc_document_dates.output, _("Document is %d seconds in the future"),
776 (int)doc_data - (int)srv_data);
777
778 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
779 } else if (doc_data < srv_data - maximum_age) {
780 time_t last_modified = (srv_data - doc_data);
781 if (last_modified > (60 * 60 * 24 * 2)) { // two days hardcoded?
782 xasprintf(&sc_document_dates.output, _("Last modified %.1f days ago"),
783 ((float)last_modified) / (60 * 60 * 24));
784 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
785 } else {
786 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"),
787 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
788 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
789 }
790 } else {
791 // TODO is this the OK case?
792 time_t last_modified = (srv_data - doc_data);
793 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"),
794 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
795 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK);
796 }
797 }
798
799 if (server_date) {
800 free(server_date);
801 }
802 if (document_date) {
803 free(document_date);
804 }
805
806 return sc_document_dates;
807}
808
809void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); }
810
811int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) {
812 /* find last start of a new header */
813 const char *start = strrstr2(buf, "\r\nHTTP/");
814 if (start != NULL) {
815 start += 2;
816 buf = start;
817 }
818
819 char *first_line_end = strstr(buf, "\r\n");
820 if (first_line_end == NULL) {
821 return -1;
822 }
823
824 size_t first_line_len = (size_t)(first_line_end - buf);
825 status_line->first_line = (char *)calloc(first_line_len + 1, sizeof(char));
826 if (status_line->first_line == NULL) {
827 return -1;
828 }
829 memcpy(status_line->first_line, buf, first_line_len);
830 status_line->first_line[first_line_len] = '\0';
831 char *first_line_buf = strdup(status_line->first_line);
832
833 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
834 char *temp_string = strtok(first_line_buf, "/");
835 if (temp_string == NULL) {
836 free(first_line_buf);
837 return -1;
838 }
839 if (strcmp(temp_string, "HTTP") != 0) {
840 free(first_line_buf);
841 return -1;
842 }
843
844 temp_string = strtok(NULL, " ");
845 if (temp_string == NULL) {
846 free(first_line_buf);
847 return -1;
848 }
849
850 char *temp_string_2;
851 if (strchr(temp_string, '.') != NULL) {
852
853 /* HTTP 1.x case */
854 strtok(temp_string, ".");
855 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
856 if (*temp_string_2 != '\0') {
857 free(first_line_buf);
858 return -1;
859 }
860 strtok(NULL, " ");
861 status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10);
862 if (*temp_string_2 != '\0') {
863 free(first_line_buf);
864 return -1;
865 }
866 temp_string += 4; /* 1.x SP */
867 } else {
868 /* HTTP 2 case */
869 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
870 status_line->http_minor = 0;
871 temp_string += 2; /* 2 SP */
872 }
873
874 /* status code: "404" or "404.1", then SP */
875 temp_string = strtok(temp_string, " ");
876 if (temp_string == NULL) {
877 free(first_line_buf);
878 return -1;
879 }
880 if (strchr(temp_string, '.') != NULL) {
881 char *ppp;
882 ppp = strtok(temp_string, ".");
883 status_line->http_code = (int)strtol(ppp, &temp_string_2, 10);
884 if (*temp_string_2 != '\0') {
885 free(first_line_buf);
886 return -1;
887 }
888 ppp = strtok(NULL, "");
889 status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10);
890 if (*temp_string_2 != '\0') {
891 free(first_line_buf);
892 return -1;
893 }
894 temp_string += 6; /* 400.1 SP */
895 } else {
896 status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10);
897 status_line->http_subcode = -1;
898 if (*temp_string_2 != '\0') {
899 free(first_line_buf);
900 return -1;
901 }
902 temp_string += 4; /* 400 SP */
903 }
904
905 /* Human readable message: "Not Found" CRLF */
906
907 temp_string = strtok(temp_string, "");
908 if (temp_string == NULL) {
909 status_line->msg = "";
910 return 0;
911 }
912 status_line->msg = status_line->first_line + (temp_string - first_line_buf);
913 free(first_line_buf);
914
915 return 0;
916}
917
918/* TODO: where to put this, it's actually part of sstrings2 (logically)?
919 */
920const char *strrstr2(const char *haystack, const char *needle) {
921 if (haystack == NULL || needle == NULL) {
922 return NULL;
923 }
924
925 if (haystack[0] == '\0' || needle[0] == '\0') {
926 return NULL;
927 }
928
929 int counter = 0;
930 const char *prev_pos = NULL;
931 const char *pos = haystack;
932 size_t len = strlen(needle);
933 for (;;) {
934 pos = strstr(pos, needle);
935 if (pos == NULL) {
936 if (counter == 0) {
937 return NULL;
938 }
939 return prev_pos;
940 }
941 counter++;
942 prev_pos = pos;
943 pos += len;
944 if (*pos == '\0') {
945 return prev_pos;
946 }
947 }
948}
949
950void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) {
951 free(buf->buf);
952 buf->buf = NULL;
953}
954
955void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) {
956 free(buf->buf);
957 buf->buf = NULL;
958}
959
960int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char *data, size_t datalen) {
961 if ((*buf = calloc(1, sizeof(curlhelp_read_curlbuf))) == NULL) {
962 return 1;
963 }
964
965 (*buf)->buflen = datalen;
966 (*buf)->buf = (char *)calloc((*buf)->buflen, sizeof(char));
967 if ((*buf)->buf == NULL) {
968 return -1;
969 }
970 memcpy((*buf)->buf, data, datalen);
971 (*buf)->pos = 0;
972 return 0;
973}
974
975size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
976 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
977
978 size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos);
979
980 memcpy(buffer, buf->buf + buf->pos, minimalSize);
981 buf->pos += minimalSize;
982
983 return minimalSize;
984}
985
986int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf) {
987 if ((*buf = calloc(1, sizeof(curlhelp_write_curlbuf))) == NULL) {
988 return 1;
989 }
990 (*buf)->bufsize = DEFAULT_BUFFER_SIZE * sizeof(char);
991 (*buf)->buflen = 0;
992 (*buf)->buf = (char *)calloc((*buf)->bufsize, sizeof(char));
993 if ((*buf)->buf == NULL) {
994 return -1;
995 }
996 return 0;
997}
998
999size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
1000 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
1001
1002 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
1003 buf->bufsize = buf->bufsize * 2;
1004 buf->buf = (char *)realloc(buf->buf, buf->bufsize);
1005 if (buf->buf == NULL) {
1006 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
1007 return 0;
1008 }
1009 }
1010
1011 memcpy(buf->buf + buf->buflen, buffer, size * nmemb);
1012 buf->buflen += size * nmemb;
1013 buf->buf[buf->buflen] = '\0';
1014
1015 return size * nmemb;
1016}
1017
1018void cleanup(check_curl_global_state global_state) {
1019 if (global_state.status_line_initialized) {
1020 curlhelp_free_statusline(global_state.status_line);
1021 }
1022 global_state.status_line_initialized = false;
1023
1024 if (global_state.curl_easy_initialized) {
1025 curl_easy_cleanup(global_state.curl);
1026 }
1027 global_state.curl_easy_initialized = false;
1028
1029 if (global_state.curl_global_initialized) {
1030 curl_global_cleanup();
1031 }
1032 global_state.curl_global_initialized = false;
1033
1034 if (global_state.body_buf_initialized) {
1035 curlhelp_freewritebuffer(global_state.body_buf);
1036 }
1037 global_state.body_buf_initialized = false;
1038
1039 if (global_state.header_buf_initialized) {
1040 curlhelp_freewritebuffer(global_state.header_buf);
1041 }
1042 global_state.header_buf_initialized = false;
1043
1044 if (global_state.put_buf_initialized) {
1045 curlhelp_freereadbuffer(global_state.put_buf);
1046 }
1047 global_state.put_buf_initialized = false;
1048
1049 if (global_state.header_list) {
1050 curl_slist_free_all(global_state.header_list);
1051 }
1052
1053 if (global_state.host) {
1054 curl_slist_free_all(global_state.host);
1055 }
1056}
1057
1058int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) {
1059 struct addrinfo hints = {
1060 .ai_family = addr_family,
1061 .ai_socktype = SOCK_STREAM,
1062 .ai_flags = AI_CANONNAME,
1063 };
1064
1065 struct addrinfo *result;
1066 int errcode = getaddrinfo(host, NULL, &hints, &result);
1067 if (errcode != 0) {
1068 return errcode;
1069 }
1070
1071 strcpy(buf, "");
1072 struct addrinfo *res = result;
1073
1074 size_t buflen_remaining = buflen - 1;
1075 size_t addrstr_len;
1076 char addrstr[100];
1077 void *ptr = {0};
1078 while (res) {
1079 switch (res->ai_family) {
1080 case AF_INET:
1081 ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1082 break;
1083 case AF_INET6:
1084 ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1085 break;
1086 }
1087
1088 inet_ntop(res->ai_family, ptr, addrstr, 100);
1089 if (verbose >= 1) {
1090 printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4,
1091 addrstr);
1092 }
1093
1094 // Append all IPs to buf as a comma-separated string
1095 addrstr_len = strlen(addrstr);
1096 if (buflen_remaining > addrstr_len + 1) {
1097 if (buf[0] != '\0') {
1098 strncat(buf, ",", buflen_remaining);
1099 buflen_remaining -= 1;
1100 }
1101 strncat(buf, addrstr, buflen_remaining);
1102 buflen_remaining -= addrstr_len;
1103 }
1104
1105 res = res->ai_next;
1106 }
1107
1108 freeaddrinfo(result);
1109
1110 return 0;
1111}
1112
1113/* Checks if the server 'reply' is one of the expected 'statuscodes' */
1114bool expected_statuscode(const char *reply, const char *statuscodes) {
1115 char *expected;
1116
1117 if ((expected = strdup(statuscodes)) == NULL) {
1118 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
1119 }
1120
1121 bool result = false;
1122 for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) {
1123 if (strstr(reply, code) != NULL) {
1124 result = true;
1125 break;
1126 }
1127 }
1128
1129 free(expected);
1130 return result;
1131}
1132
1133/* returns a string "HTTP/1.x" or "HTTP/2" */
1134char *string_statuscode(int major, int minor) {
1135 static char buf[10];
1136
1137 switch (major) {
1138 case 1:
1139 snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor);
1140 break;
1141 case 2:
1142 case 3:
1143 snprintf(buf, sizeof(buf), "HTTP/%d", major);
1144 break;
1145 default:
1146 /* assuming here HTTP/N with N>=4 */
1147 snprintf(buf, sizeof(buf), "HTTP/%d", major);
1148 break;
1149 }
1150
1151 return buf;
1152}
1153
1154/* check whether a file exists */
1155void test_file(char *path) {
1156 if (access(path, R_OK) == 0) {
1157 return;
1158 }
1159 usage2(_("file does not exist or is not readable"), path);
1160}
1161
1162mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
1163 int days_till_exp_crit);
1164
1165mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
1166 int crit_days_till_exp) {
1167 mp_subcheck sc_cert_result = mp_subcheck_init();
1168 sc_cert_result = mp_set_subcheck_default_state(sc_cert_result, STATE_OK);
1169
1170#ifdef LIBCURL_FEATURE_SSL
1171 if (is_openssl_callback) {
1172# ifdef USE_OPENSSL
1173 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
1174 * and we actually have OpenSSL in the monitoring tools
1175 */
1176 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1177# else /* USE_OPENSSL */
1178 xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL "
1179 "callback used and not linked against OpenSSL\n");
1180 mp_set_subcheck_state(result, STATE_CRITICAL);
1181# endif /* USE_OPENSSL */
1182 } else {
1183 struct curl_slist *slist;
1184
1185 cert_ptr_union cert_ptr = {0};
1186 cert_ptr.to_info = NULL;
1187 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
1188 if (!res && cert_ptr.to_info) {
1189# ifdef USE_OPENSSL
1190 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert
1191 * parsing We only check the first certificate and assume it's the one of
1192 * the server
1193 */
1194 char *raw_cert = NULL;
1195 bool got_first_cert = false;
1196 for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
1197 if (got_first_cert) {
1198 break;
1199 }
1200
1201 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
1202 if (verbose >= 2) {
1203 printf("%d ** %s\n", i, slist->data);
1204 }
1205 if (strncmp(slist->data, "Cert:", 5) == 0) {
1206 raw_cert = &slist->data[5];
1207 got_first_cert = true;
1208 break;
1209 }
1210 }
1211 }
1212
1213 if (!raw_cert) {
1214
1215 xasprintf(&sc_cert_result.output,
1216 _("Cannot retrieve certificates from CERTINFO information - "
1217 "certificate data was empty"));
1218 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1219 return sc_cert_result;
1220 }
1221
1222 BIO *cert_BIO = BIO_new(BIO_s_mem());
1223 BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert));
1224
1225 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
1226 if (!cert) {
1227 xasprintf(&sc_cert_result.output,
1228 _("Cannot read certificate from CERTINFO information - BIO error"));
1229 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1230 return sc_cert_result;
1231 }
1232
1233 BIO_free(cert_BIO);
1234 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1235# else /* USE_OPENSSL */
1236 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our
1237 * disposal, so we use the libcurl CURLINFO data
1238 */
1239 return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn,
1240 days_till_exp_crit);
1241# endif /* USE_OPENSSL */
1242 } else {
1243 xasprintf(&sc_cert_result.output,
1244 _("Cannot retrieve certificates - cURL returned %d - %s"), res,
1245 curl_easy_strerror(res));
1246 mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1247 }
1248 }
1249#endif /* LIBCURL_FEATURE_SSL */
1250
1251 return sc_cert_result;
1252}
1253
1254char *fmt_url(check_curl_working_state workingState) {
1255 char *url = calloc(DEFAULT_BUFFER_SIZE, sizeof(char));
1256 if (url == NULL) {
1257 die(STATE_UNKNOWN, "memory allocation failed");
1258 }
1259
1260 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http",
1261 (workingState.use_ssl & (workingState.host_name != NULL))
1262 ? workingState.host_name
1263 : workingState.server_address,
1264 workingState.serverPort, workingState.server_url);
1265
1266 return url;
1267}
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..87e45a9d
--- /dev/null
+++ b/plugins/check_curl.d/check_curl_helpers.h
@@ -0,0 +1,124 @@
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
10/* for buffers for header and body */
11typedef struct {
12 size_t buflen;
13 size_t bufsize;
14 char *buf;
15} curlhelp_write_curlbuf;
16
17/* for buffering the data sent in PUT */
18typedef struct {
19 size_t buflen;
20 off_t pos;
21 char *buf;
22} curlhelp_read_curlbuf;
23
24/* for parsing the HTTP status line */
25typedef struct {
26 int http_major; /* major version of the protocol, always 1 (HTTP/0.9
27 * never reached the big internet most likely) */
28 int http_minor; /* minor version of the protocol, usually 0 or 1 */
29 int http_code; /* HTTP return code as in RFC 2145 */
30 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
31 * http://support.microsoft.com/kb/318380/en-us */
32 const char *msg; /* the human readable message */
33 char *first_line; /* a copy of the first line */
34} curlhelp_statusline;
35
36typedef struct {
37 bool curl_global_initialized;
38 bool curl_easy_initialized;
39
40 bool body_buf_initialized;
41 curlhelp_write_curlbuf *body_buf;
42
43 bool header_buf_initialized;
44 curlhelp_write_curlbuf *header_buf;
45
46 bool status_line_initialized;
47 curlhelp_statusline *status_line;
48
49 bool put_buf_initialized;
50 curlhelp_read_curlbuf *put_buf;
51
52 CURL *curl;
53
54 struct curl_slist *header_list;
55 struct curl_slist *host;
56} check_curl_global_state;
57
58/* to know the underlying SSL library used by libcurl */
59typedef enum curlhelp_ssl_library {
60 CURLHELP_SSL_LIBRARY_UNKNOWN,
61 CURLHELP_SSL_LIBRARY_OPENSSL,
62 CURLHELP_SSL_LIBRARY_LIBRESSL,
63 CURLHELP_SSL_LIBRARY_GNUTLS,
64 CURLHELP_SSL_LIBRARY_NSS
65} curlhelp_ssl_library;
66
67#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch))
68
69typedef struct {
70 int errorcode;
71 check_curl_global_state curl_state;
72 check_curl_working_state working_state;
73} check_curl_configure_curl_wrapper;
74
75check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_curl_config config,
76 check_curl_working_state working_state,
77 bool check_cert,
78 bool on_redirect_dependent,
79 int follow_method, int max_depth);
80
81void handle_curl_option_return_code(CURLcode res, const char *option);
82
83int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf);
84size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/,
85 void * /*stream*/);
86void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/);
87
88int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char * /*data*/, size_t /*datalen*/);
89size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/,
90 void * /*stream*/);
91void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/);
92
93curlhelp_ssl_library curlhelp_get_ssl_library(void);
94const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/);
95
96typedef union {
97 struct curl_slist *to_info;
98 struct curl_certinfo *to_certinfo;
99} cert_ptr_union;
100int net_noopenssl_check_certificate(cert_ptr_union *, int, int);
101
102int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/);
103void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/);
104
105char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header);
106mp_subcheck check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/,
107 int /*maximum_age*/);
108size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
109 const curlhelp_write_curlbuf *body_buf);
110int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family);
111CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm);
112
113#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
114const char *strrstr2(const char *haystack, const char *needle);
115
116void cleanup(check_curl_global_state global_state);
117
118bool expected_statuscode(const char *reply, const char *statuscodes);
119char *string_statuscode(int major, int minor);
120
121void test_file(char *path);
122mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
123 int crit_days_till_exp);
124char *fmt_url(check_curl_working_state workingState);
diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h
new file mode 100644
index 00000000..f51b2ee9
--- /dev/null
+++ b/plugins/check_curl.d/config.h
@@ -0,0 +1,116 @@
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} check_curl_working_state;
52
53check_curl_working_state check_curl_working_state_init();
54
55typedef struct {
56 bool automatic_decompression;
57 bool haproxy_protocol;
58 long socket_timeout;
59 sa_family_t sin_family;
60 int curl_http_version;
61 char **http_opt_headers;
62 size_t http_opt_headers_count;
63 int ssl_version;
64 char *client_cert;
65 char *client_privkey;
66 char *ca_cert;
67 bool verify_peer_and_host;
68 char user_agent[DEFAULT_BUFFER_SIZE];
69 char proxy_auth[MAX_INPUT_BUFFER];
70 char user_auth[MAX_INPUT_BUFFER];
71 char *http_content_type;
72 char *cookie_jar_file;
73} check_curl_static_curl_config;
74
75typedef struct {
76 check_curl_working_state initial_config;
77
78 check_curl_static_curl_config curl_config;
79 int max_depth;
80 int followmethod;
81 int followsticky;
82
83 int maximum_age;
84
85 // the original regex string from the command line
86 char regexp[MAX_RE_SIZE];
87
88 // the compiled regex for usage later
89 regex_t compiled_regex;
90
91 mp_state_enum state_regex;
92 bool invert_regex;
93 bool check_cert;
94 bool continue_after_check_cert;
95 int days_till_exp_warn;
96 int days_till_exp_crit;
97 mp_thresholds thlds;
98 mp_range page_length_limits;
99 bool page_length_limits_is_set;
100 struct {
101 char string[MAX_INPUT_BUFFER];
102 bool is_present;
103 } server_expect;
104 char string_expect[MAX_INPUT_BUFFER];
105 char header_expect[MAX_INPUT_BUFFER];
106 mp_state_enum on_redirect_result_state;
107 bool on_redirect_dependent;
108
109 bool show_extended_perfdata;
110 bool show_body;
111
112 bool output_format_is_set;
113 mp_output_format output_format;
114} check_curl_config;
115
116check_curl_config check_curl_config_init();
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c
index 9efcd1cb..468ded31 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -71,8 +71,9 @@ static double timediff(struct timeval /*start*/, struct timeval /*end*/);
71 71
72static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...); 72static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...);
73 73
74static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/, double * /*res_val*/, double * /*res_time*/, mp_dbi_metric /*metric*/, 74static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/,
75 mp_dbi_type /*type*/, char * /*np_dbi_query*/); 75 double * /*res_val*/, double * /*res_time*/, mp_dbi_metric /*metric*/,
76 mp_dbi_type /*type*/, char * /*np_dbi_query*/);
76 77
77int main(int argc, char **argv) { 78int main(int argc, char **argv) {
78 int status = STATE_UNKNOWN; 79 int status = STATE_UNKNOWN;
@@ -118,7 +119,8 @@ int main(int argc, char **argv) {
118 dbi_inst *instance_p = {0}; 119 dbi_inst *instance_p = {0};
119 120
120 if (dbi_initialize_r(NULL, instance_p) < 0) { 121 if (dbi_initialize_r(NULL, instance_p) < 0) {
121 printf("UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); 122 printf(
123 "UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n");
122 return STATE_UNKNOWN; 124 return STATE_UNKNOWN;
123 } 125 }
124 126
@@ -133,10 +135,12 @@ int main(int argc, char **argv) {
133 135
134 driver = dbi_driver_open_r(config.dbi_driver, instance_p); 136 driver = dbi_driver_open_r(config.dbi_driver, instance_p);
135 if (!driver) { 137 if (!driver) {
136 printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver); 138 printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
139 config.dbi_driver);
137 140
138 printf("Known drivers:\n"); 141 printf("Known drivers:\n");
139 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;
143 driver = dbi_driver_list_r(driver, instance_p)) {
140 printf(" - %s\n", dbi_driver_get_name(driver)); 144 printf(" - %s\n", dbi_driver_get_name(driver));
141 } 145 }
142 return STATE_UNKNOWN; 146 return STATE_UNKNOWN;
@@ -156,7 +160,8 @@ int main(int argc, char **argv) {
156 const char *opt; 160 const char *opt;
157 161
158 if (verbose > 1) { 162 if (verbose > 1) {
159 printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key, config.dbi_options[i].value); 163 printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key,
164 config.dbi_options[i].value);
160 } 165 }
161 166
162 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) { 167 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) {
@@ -164,10 +169,12 @@ int main(int argc, char **argv) {
164 } 169 }
165 /* else: status != 0 */ 170 /* else: status != 0 */
166 171
167 np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", config.dbi_options[i].key, config.dbi_options[i].value); 172 np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'",
173 config.dbi_options[i].key, config.dbi_options[i].value);
168 printf("Known driver options:\n"); 174 printf("Known driver options:\n");
169 175
170 for (opt = dbi_conn_get_option_list(conn, NULL); opt; opt = dbi_conn_get_option_list(conn, opt)) { 176 for (opt = dbi_conn_get_option_list(conn, NULL); opt;
177 opt = dbi_conn_get_option_list(conn, opt)) {
171 printf(" - %s\n", opt); 178 printf(" - %s\n", opt);
172 } 179 }
173 dbi_conn_close(conn); 180 dbi_conn_close(conn);
@@ -230,14 +237,16 @@ int main(int argc, char **argv) {
230 } 237 }
231 238
232 if (dbi_conn_select_db(conn, config.dbi_database)) { 239 if (dbi_conn_select_db(conn, config.dbi_database)) {
233 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.dbi_database); 240 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'",
241 config.dbi_database);
234 return STATE_UNKNOWN; 242 return STATE_UNKNOWN;
235 } 243 }
236 } 244 }
237 245
238 if (config.dbi_query) { 246 if (config.dbi_query) {
239 /* execute query */ 247 /* execute query */
240 status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type, config.dbi_query); 248 status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type,
249 config.dbi_query);
241 if (status != STATE_OK) { 250 if (status != STATE_OK) {
242 /* do_query prints an error message in this case */ 251 /* do_query prints an error message in this case */
243 return status; 252 return status;
@@ -281,7 +290,8 @@ int main(int argc, char **argv) {
281 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error 290 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error
282 * which should have been reported and handled (abort) before 291 * which should have been reported and handled (abort) before
283 * ... unless we expected a string to be returned */ 292 * ... unless we expected a string to be returned */
284 assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) || (config.type == TYPE_STRING)); 293 assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) ||
294 (config.type == TYPE_STRING));
285 295
286 assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str)); 296 assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str));
287 297
@@ -289,12 +299,14 @@ int main(int argc, char **argv) {
289 if (config.dbi_query) { 299 if (config.dbi_query) {
290 if (config.type == TYPE_STRING) { 300 if (config.type == TYPE_STRING) {
291 assert(config.expect || config.expect_re_str); 301 assert(config.expect || config.expect_re_str);
292 printf(", '%s' returned '%s' in %fs", config.dbi_query, query_val_str ? query_val_str : "<nothing>", query_time); 302 printf(", '%s' returned '%s' in %fs", config.dbi_query,
303 query_val_str ? query_val_str : "<nothing>", query_time);
293 if (status != STATE_OK) { 304 if (status != STATE_OK) {
294 if (config.expect) { 305 if (config.expect) {
295 printf(" (expected '%s')", config.expect); 306 printf(" (expected '%s')", config.expect);
296 } else if (config.expect_re_str) { 307 } else if (config.expect_re_str) {
297 printf(" (expected regex /%s/%s)", config.expect_re_str, ((config.expect_re_cflags & REG_ICASE) ? "i" : "")); 308 printf(" (expected regex /%s/%s)", config.expect_re_str,
309 ((config.expect_re_cflags & REG_ICASE) ? "i" : ""));
298 } 310 }
299 } 311 }
300 } else if (isnan(query_val)) { 312 } else if (isnan(query_val)) {
@@ -304,18 +316,31 @@ int main(int argc, char **argv) {
304 } 316 }
305 } 317 }
306 318
307 printf(" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, 319 printf(
308 ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "", 320 " | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time,
309 ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "", server_version, 321 ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "",
310 ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range : "", 322 ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "",
311 ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range : ""); 323 server_version,
324 ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range
325 : "",
326 ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range
327 : "");
312 if (config.dbi_query) { 328 if (config.dbi_query) {
313 if (!isnan(query_val)) { /* this is also true when -e is used */ 329 if (!isnan(query_val)) { /* this is also true when -e is used */
314 printf(" query=%f;%s;%s;;", query_val, ((config.metric == METRIC_QUERY_RESULT) && config.warning_range) ? config.warning_range : "", 330 printf(" query=%f;%s;%s;;", query_val,
315 ((config.metric == METRIC_QUERY_RESULT) && config.critical_range) ? config.critical_range : ""); 331 ((config.metric == METRIC_QUERY_RESULT) && config.warning_range)
332 ? config.warning_range
333 : "",
334 ((config.metric == METRIC_QUERY_RESULT) && config.critical_range)
335 ? config.critical_range
336 : "");
316 } 337 }
317 printf(" querytime=%fs;%s;%s;0;", query_time, ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range : "", 338 printf(" querytime=%fs;%s;%s;0;", query_time,
318 ((config.metric == METRIC_QUERY_TIME) && config.critical_range) ? config.critical_range : ""); 339 ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range
340 : "",
341 ((config.metric == METRIC_QUERY_TIME) && config.critical_range)
342 ? config.critical_range
343 : "");
319 } 344 }
320 printf("\n"); 345 printf("\n");
321 return status; 346 return status;
@@ -442,7 +467,8 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
442 *value = '\0'; 467 *value = '\0';
443 ++value; 468 ++value;
444 469
445 new = realloc(result.config.dbi_options, (result.config.dbi_options_num + 1) * sizeof(*new)); 470 new = realloc(result.config.dbi_options,
471 (result.config.dbi_options_num + 1) * sizeof(*new));
446 if (!new) { 472 if (!new) {
447 printf("UNKNOWN - failed to reallocate memory\n"); 473 printf("UNKNOWN - failed to reallocate memory\n");
448 exit(STATE_UNKNOWN); 474 exit(STATE_UNKNOWN);
@@ -464,7 +490,8 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
464 } 490 }
465 } 491 }
466 492
467 set_thresholds(&result.config.dbi_thresholds, result.config.warning_range, result.config.critical_range); 493 set_thresholds(&result.config.dbi_thresholds, result.config.warning_range,
494 result.config.critical_range);
468 495
469 return validate_arguments(result); 496 return validate_arguments(result);
470} 497}
@@ -474,21 +501,28 @@ check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrap
474 usage("Must specify a DBI driver"); 501 usage("Must specify a DBI driver");
475 } 502 }
476 503
477 if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) || (config_wrapper.config.metric == METRIC_QUERY_TIME)) && 504 if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) ||
505 (config_wrapper.config.metric == METRIC_QUERY_TIME)) &&
478 (!config_wrapper.config.dbi_query)) { 506 (!config_wrapper.config.dbi_query)) {
479 usage("Must specify a query to execute (metric == QUERY_RESULT)"); 507 usage("Must specify a query to execute (metric == QUERY_RESULT)");
480 } 508 }
481 509
482 if ((config_wrapper.config.metric != METRIC_CONN_TIME) && (config_wrapper.config.metric != METRIC_SERVER_VERSION) && 510 if ((config_wrapper.config.metric != METRIC_CONN_TIME) &&
483 (config_wrapper.config.metric != METRIC_QUERY_RESULT) && (config_wrapper.config.metric != METRIC_QUERY_TIME)) { 511 (config_wrapper.config.metric != METRIC_SERVER_VERSION) &&
512 (config_wrapper.config.metric != METRIC_QUERY_RESULT) &&
513 (config_wrapper.config.metric != METRIC_QUERY_TIME)) {
484 usage("Invalid metric specified"); 514 usage("Invalid metric specified");
485 } 515 }
486 516
487 if (config_wrapper.config.expect && (config_wrapper.config.warning_range || config_wrapper.config.critical_range || config_wrapper.config.expect_re_str)) { 517 if (config_wrapper.config.expect &&
518 (config_wrapper.config.warning_range || config_wrapper.config.critical_range ||
519 config_wrapper.config.expect_re_str)) {
488 usage("Do not mix -e and -w/-c/-r/-R"); 520 usage("Do not mix -e and -w/-c/-r/-R");
489 } 521 }
490 522
491 if (config_wrapper.config.expect_re_str && (config_wrapper.config.warning_range || config_wrapper.config.critical_range || config_wrapper.config.expect)) { 523 if (config_wrapper.config.expect_re_str &&
524 (config_wrapper.config.warning_range || config_wrapper.config.critical_range ||
525 config_wrapper.config.expect)) {
492 usage("Do not mix -r/-R and -w/-c/-e"); 526 usage("Do not mix -r/-R and -w/-c/-e");
493 } 527 }
494 528
@@ -496,7 +530,8 @@ check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrap
496 usage("Option -e requires metric QUERY_RESULT"); 530 usage("Option -e requires metric QUERY_RESULT");
497 } 531 }
498 532
499 if (config_wrapper.config.expect_re_str && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { 533 if (config_wrapper.config.expect_re_str &&
534 (config_wrapper.config.metric != METRIC_QUERY_RESULT)) {
500 usage("Options -r/-R require metric QUERY_RESULT"); 535 usage("Options -r/-R require metric QUERY_RESULT");
501 } 536 }
502 537
@@ -607,7 +642,8 @@ void print_usage(void) {
607 printf(" [-e <string>] [-r|-R <regex>]\n"); 642 printf(" [-e <string>] [-r|-R <regex>]\n");
608} 643}
609 644
610const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type, mp_dbi_metric metric, mp_dbi_type type) { 645const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type,
646 mp_dbi_metric metric, mp_dbi_type type) {
611 const char *str; 647 const char *str;
612 648
613 if (field_type != DBI_TYPE_STRING) { 649 if (field_type != DBI_TYPE_STRING) {
@@ -630,7 +666,8 @@ const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_ty
630 return str; 666 return str;
631} 667}
632 668
633double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric, mp_dbi_type type) { 669double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric,
670 mp_dbi_type type) {
634 double val = NAN; 671 double val = NAN;
635 672
636 if (*field_type == DBI_TYPE_INTEGER) { 673 if (*field_type == DBI_TYPE_INTEGER) {
@@ -679,7 +716,8 @@ double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_d
679 return val; 716 return val;
680} 717}
681 718
682mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val, mp_dbi_metric metric, mp_dbi_type type) { 719mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str,
720 double *res_val, mp_dbi_metric metric, mp_dbi_type type) {
683 unsigned short field_type; 721 unsigned short field_type;
684 double val = NAN; 722 double val = NAN;
685 723
@@ -747,8 +785,8 @@ mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_v
747 return STATE_OK; 785 return STATE_OK;
748} 786}
749 787
750mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time, mp_dbi_metric metric, mp_dbi_type type, 788mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time,
751 char *np_dbi_query) { 789 mp_dbi_metric metric, mp_dbi_type type, char *np_dbi_query) {
752 dbi_result res; 790 dbi_result res;
753 791
754 struct timeval timeval_start; 792 struct timeval timeval_start;
diff --git a/plugins/check_dig.c b/plugins/check_dig.c
index 2bbd1e05..c27e5f13 100644
--- a/plugins/check_dig.c
+++ b/plugins/check_dig.c
@@ -41,97 +41,95 @@ const char *email = "devel@monitoring-plugins.org";
41#include "utils.h" 41#include "utils.h"
42#include "runcmd.h" 42#include "runcmd.h"
43 43
44static int process_arguments(int /*argc*/, char ** /*argv*/); 44#include "check_dig.d/config.h"
45static int validate_arguments(void); 45#include "states.h"
46
47typedef struct {
48 int errorcode;
49 check_dig_config config;
50} check_dig_config_wrapper;
51static check_dig_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
52static check_dig_config_wrapper validate_arguments(check_dig_config_wrapper /*config_wrapper*/);
53
46static void print_help(void); 54static void print_help(void);
47void print_usage(void); 55void print_usage(void);
48 56
49#define UNDEFINED 0 57static int verbose = 0;
50#define DEFAULT_PORT 53
51#define DEFAULT_TRIES 2
52
53static char *query_address = NULL;
54static char *record_type = "A";
55static char *expected_address = NULL;
56static char *dns_server = NULL;
57static char *dig_args = "";
58static char *query_transport = "";
59static bool verbose = false;
60static int server_port = DEFAULT_PORT;
61static int number_tries = DEFAULT_TRIES;
62static double warning_interval = UNDEFINED;
63static double critical_interval = UNDEFINED;
64static struct timeval tv;
65 58
66int main(int argc, char **argv) { 59int main(int argc, char **argv) {
67 char *command_line;
68 output chld_out;
69 output chld_err;
70 char *msg = NULL;
71 size_t i;
72 char *t;
73 long microsec;
74 double elapsed_time;
75 int result = STATE_UNKNOWN;
76 int timeout_interval_dig;
77
78 setlocale(LC_ALL, ""); 60 setlocale(LC_ALL, "");
79 bindtextdomain(PACKAGE, LOCALEDIR); 61 bindtextdomain(PACKAGE, LOCALEDIR);
80 textdomain(PACKAGE); 62 textdomain(PACKAGE);
81 63
82 /* Set signal handling and alarm */ 64 /* Set signal handling and alarm */
83 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) 65 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
84 usage_va(_("Cannot catch SIGALRM")); 66 usage_va(_("Cannot catch SIGALRM"));
67 }
85 68
86 /* Parse extra opts if any */ 69 /* Parse extra opts if any */
87 argv = np_extra_opts(&argc, argv, progname); 70 argv = np_extra_opts(&argc, argv, progname);
88 71
89 if (process_arguments(argc, argv) == ERROR) 72 check_dig_config_wrapper tmp_config = process_arguments(argc, argv);
73 if (tmp_config.errorcode == ERROR) {
90 usage_va(_("Could not parse arguments")); 74 usage_va(_("Could not parse arguments"));
75 }
76
77 const check_dig_config config = tmp_config.config;
91 78
92 /* dig applies the timeout to each try, so we need to work around this */ 79 /* dig applies the timeout to each try, so we need to work around this */
93 timeout_interval_dig = timeout_interval / number_tries + number_tries; 80 int timeout_interval_dig = ((int)timeout_interval / config.number_tries) + config.number_tries;
94 81
82 char *command_line;
95 /* get the command to run */ 83 /* get the command to run */
96 xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG, dig_args, query_transport, server_port, dns_server, 84 xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG,
97 query_address, record_type, number_tries, timeout_interval_dig); 85 config.dig_args, config.query_transport, config.server_port, config.dns_server,
86 config.query_address, config.record_type, config.number_tries, timeout_interval_dig);
98 87
99 alarm(timeout_interval); 88 alarm(timeout_interval);
100 gettimeofday(&tv, NULL); 89 struct timeval start_time;
90 gettimeofday(&start_time, NULL);
101 91
102 if (verbose) { 92 if (verbose) {
103 printf("%s\n", command_line); 93 printf("%s\n", command_line);
104 if (expected_address != NULL) { 94 if (config.expected_address != NULL) {
105 printf(_("Looking for: '%s'\n"), expected_address); 95 printf(_("Looking for: '%s'\n"), config.expected_address);
106 } else { 96 } else {
107 printf(_("Looking for: '%s'\n"), query_address); 97 printf(_("Looking for: '%s'\n"), config.query_address);
108 } 98 }
109 } 99 }
110 100
101 output chld_out;
102 output chld_err;
103 char *msg = NULL;
104 mp_state_enum result = STATE_UNKNOWN;
111 /* run the command */ 105 /* run the command */
112 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { 106 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) {
113 result = STATE_WARNING; 107 result = STATE_WARNING;
114 msg = (char *)_("dig returned an error status"); 108 msg = (char *)_("dig returned an error status");
115 } 109 }
116 110
117 for (i = 0; i < chld_out.lines; i++) { 111 for (size_t i = 0; i < chld_out.lines; i++) {
118 /* the server is responding, we just got the host name... */ 112 /* the server is responding, we just got the host name... */
119 if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) { 113 if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) {
120 114
121 /* loop through the whole 'ANSWER SECTION' */ 115 /* loop through the whole 'ANSWER SECTION' */
122 for (; i < chld_out.lines; i++) { 116 for (; i < chld_out.lines; i++) {
123 /* get the host address */ 117 /* get the host address */
124 if (verbose) 118 if (verbose) {
125 printf("%s\n", chld_out.line[i]); 119 printf("%s\n", chld_out.line[i]);
120 }
126 121
127 if (strcasestr(chld_out.line[i], (expected_address == NULL ? query_address : expected_address)) != NULL) { 122 if (strcasestr(chld_out.line[i], (config.expected_address == NULL
123 ? config.query_address
124 : config.expected_address)) != NULL) {
128 msg = chld_out.line[i]; 125 msg = chld_out.line[i];
129 result = STATE_OK; 126 result = STATE_OK;
130 127
131 /* Translate output TAB -> SPACE */ 128 /* Translate output TAB -> SPACE */
132 t = msg; 129 char *temp = msg;
133 while ((t = strchr(t, '\t')) != NULL) 130 while ((temp = strchr(temp, '\t')) != NULL) {
134 *t = ' '; 131 *temp = ' ';
132 }
135 break; 133 break;
136 } 134 }
137 } 135 }
@@ -154,37 +152,38 @@ int main(int argc, char **argv) {
154 /* If we get anything on STDERR, at least set warning */ 152 /* If we get anything on STDERR, at least set warning */
155 if (chld_err.buflen > 0) { 153 if (chld_err.buflen > 0) {
156 result = max_state(result, STATE_WARNING); 154 result = max_state(result, STATE_WARNING);
157 if (!msg) 155 if (!msg) {
158 for (i = 0; i < chld_err.lines; i++) { 156 for (size_t i = 0; i < chld_err.lines; i++) {
159 msg = strchr(chld_err.line[0], ':'); 157 msg = strchr(chld_err.line[0], ':');
160 if (msg) { 158 if (msg) {
161 msg++; 159 msg++;
162 break; 160 break;
163 } 161 }
164 } 162 }
163 }
165 } 164 }
166 165
167 microsec = deltime(tv); 166 long microsec = deltime(start_time);
168 elapsed_time = (double)microsec / 1.0e6; 167 double elapsed_time = (double)microsec / 1.0e6;
169 168
170 if (critical_interval > UNDEFINED && elapsed_time > critical_interval) 169 if (config.critical_interval > UNDEFINED && elapsed_time > config.critical_interval) {
171 result = STATE_CRITICAL; 170 result = STATE_CRITICAL;
171 }
172 172
173 else if (warning_interval > UNDEFINED && elapsed_time > warning_interval) 173 else if (config.warning_interval > UNDEFINED && elapsed_time > config.warning_interval) {
174 result = STATE_WARNING; 174 result = STATE_WARNING;
175 }
175 176
176 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, 177 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time,
177 msg ? msg : _("Probably a non-existent host/domain"), 178 msg ? msg : _("Probably a non-existent host/domain"),
178 fperfdata("time", elapsed_time, "s", (warning_interval > UNDEFINED ? true : false), warning_interval, 179 fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED),
179 (critical_interval > UNDEFINED ? true : false), critical_interval, true, 0, false, 0)); 180 config.warning_interval, (config.critical_interval > UNDEFINED),
180 return result; 181 config.critical_interval, true, 0, false, 0));
182 exit(result);
181} 183}
182 184
183/* process command-line arguments */ 185/* process command-line arguments */
184int process_arguments(int argc, char **argv) { 186check_dig_config_wrapper process_arguments(int argc, char **argv) {
185 int c;
186
187 int option = 0;
188 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 187 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
189 {"query_address", required_argument, 0, 'l'}, 188 {"query_address", required_argument, 0, 'l'},
190 {"warning", required_argument, 0, 'w'}, 189 {"warning", required_argument, 0, 'w'},
@@ -201,16 +200,25 @@ int process_arguments(int argc, char **argv) {
201 {"use-ipv6", no_argument, 0, '6'}, 200 {"use-ipv6", no_argument, 0, '6'},
202 {0, 0, 0, 0}}; 201 {0, 0, 0, 0}};
203 202
204 if (argc < 2) 203 check_dig_config_wrapper result = {
205 return ERROR; 204 .errorcode = OK,
205 .config = check_dig_config_init(),
206 };
206 207
207 while (1) { 208 if (argc < 2) {
208 c = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option); 209 result.errorcode = ERROR;
210 return result;
211 }
209 212
210 if (c == -1 || c == EOF) 213 int option = 0;
214 while (true) {
215 int option_index = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option);
216
217 if (option_index == -1 || option_index == EOF) {
211 break; 218 break;
219 }
212 220
213 switch (c) { 221 switch (option_index) {
214 case 'h': /* help */ 222 case 'h': /* help */
215 print_help(); 223 print_help();
216 exit(STATE_UNKNOWN); 224 exit(STATE_UNKNOWN);
@@ -219,28 +227,28 @@ int process_arguments(int argc, char **argv) {
219 exit(STATE_UNKNOWN); 227 exit(STATE_UNKNOWN);
220 case 'H': /* hostname */ 228 case 'H': /* hostname */
221 host_or_die(optarg); 229 host_or_die(optarg);
222 dns_server = optarg; 230 result.config.dns_server = optarg;
223 break; 231 break;
224 case 'p': /* server port */ 232 case 'p': /* server port */
225 if (is_intpos(optarg)) { 233 if (is_intpos(optarg)) {
226 server_port = atoi(optarg); 234 result.config.server_port = atoi(optarg);
227 } else { 235 } else {
228 usage_va(_("Port must be a positive integer - %s"), optarg); 236 usage_va(_("Port must be a positive integer - %s"), optarg);
229 } 237 }
230 break; 238 break;
231 case 'l': /* address to lookup */ 239 case 'l': /* address to lookup */
232 query_address = optarg; 240 result.config.query_address = optarg;
233 break; 241 break;
234 case 'w': /* warning */ 242 case 'w': /* warning */
235 if (is_nonnegative(optarg)) { 243 if (is_nonnegative(optarg)) {
236 warning_interval = strtod(optarg, NULL); 244 result.config.warning_interval = strtod(optarg, NULL);
237 } else { 245 } else {
238 usage_va(_("Warning interval must be a positive integer - %s"), optarg); 246 usage_va(_("Warning interval must be a positive integer - %s"), optarg);
239 } 247 }
240 break; 248 break;
241 case 'c': /* critical */ 249 case 'c': /* critical */
242 if (is_nonnegative(optarg)) { 250 if (is_nonnegative(optarg)) {
243 critical_interval = strtod(optarg, NULL); 251 result.config.critical_interval = strtod(optarg, NULL);
244 } else { 252 } else {
245 usage_va(_("Critical interval must be a positive integer - %s"), optarg); 253 usage_va(_("Critical interval must be a positive integer - %s"), optarg);
246 } 254 }
@@ -253,48 +261,50 @@ int process_arguments(int argc, char **argv) {
253 } 261 }
254 break; 262 break;
255 case 'A': /* dig arguments */ 263 case 'A': /* dig arguments */
256 dig_args = strdup(optarg); 264 result.config.dig_args = strdup(optarg);
257 break; 265 break;
258 case 'v': /* verbose */ 266 case 'v': /* verbose */
259 verbose = true; 267 verbose++;
260 break; 268 break;
261 case 'T': 269 case 'T':
262 record_type = optarg; 270 result.config.record_type = optarg;
263 break; 271 break;
264 case 'a': 272 case 'a':
265 expected_address = optarg; 273 result.config.expected_address = optarg;
266 break; 274 break;
267 case '4': 275 case '4':
268 query_transport = "-4"; 276 result.config.query_transport = "-4";
269 break; 277 break;
270 case '6': 278 case '6':
271 query_transport = "-6"; 279 result.config.query_transport = "-6";
272 break; 280 break;
273 default: /* usage5 */ 281 default: /* usage5 */
274 usage5(); 282 usage5();
275 } 283 }
276 } 284 }
277 285
278 c = optind; 286 int index = optind;
279 if (dns_server == NULL) { 287 if (result.config.dns_server == NULL) {
280 if (c < argc) { 288 if (index < argc) {
281 host_or_die(argv[c]); 289 host_or_die(argv[index]);
282 dns_server = argv[c]; 290 result.config.dns_server = argv[index];
283 } else { 291 } else {
284 if (strcmp(query_transport, "-6") == 0) 292 if (strcmp(result.config.query_transport, "-6") == 0) {
285 dns_server = strdup("::1"); 293 result.config.dns_server = strdup("::1");
286 else 294 } else {
287 dns_server = strdup("127.0.0.1"); 295 result.config.dns_server = strdup("127.0.0.1");
296 }
288 } 297 }
289 } 298 }
290 299
291 return validate_arguments(); 300 return validate_arguments(result);
292} 301}
293 302
294int validate_arguments(void) { 303check_dig_config_wrapper validate_arguments(check_dig_config_wrapper config_wrapper) {
295 if (query_address != NULL) 304 if (config_wrapper.config.query_address == NULL) {
296 return OK; 305 config_wrapper.errorcode = ERROR;
297 return ERROR; 306 }
307 return config_wrapper;
298} 308}
299 309
300void print_help(void) { 310void print_help(void) {
@@ -328,7 +338,8 @@ void print_help(void) {
328 printf(" %s\n", "-T, --record_type=STRING"); 338 printf(" %s\n", "-T, --record_type=STRING");
329 printf(" %s\n", _("Record type to lookup (default: A)")); 339 printf(" %s\n", _("Record type to lookup (default: A)"));
330 printf(" %s\n", "-a, --expected_address=STRING"); 340 printf(" %s\n", "-a, --expected_address=STRING");
331 printf(" %s\n", _("An address expected to be in the answer section. If not set, uses whatever")); 341 printf(" %s\n",
342 _("An address expected to be in the answer section. If not set, uses whatever"));
332 printf(" %s\n", _("was in -l")); 343 printf(" %s\n", _("was in -l"));
333 printf(" %s\n", "-A, --dig-arguments=STRING"); 344 printf(" %s\n", "-A, --dig-arguments=STRING");
334 printf(" %s\n", _("Pass STRING as argument(s) to dig")); 345 printf(" %s\n", _("Pass STRING as argument(s) to dig"));
diff --git a/plugins/check_dig.d/config.h b/plugins/check_dig.d/config.h
new file mode 100644
index 00000000..a570b633
--- /dev/null
+++ b/plugins/check_dig.d/config.h
@@ -0,0 +1,40 @@
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 *query_address;
12 char *record_type;
13 char *expected_address;
14 char *dns_server;
15 char *query_transport;
16 int server_port;
17 char *dig_args;
18 int number_tries;
19
20 double warning_interval;
21 double critical_interval;
22} check_dig_config;
23
24check_dig_config check_dig_config_init() {
25 check_dig_config tmp = {
26 .query_address = NULL,
27 .record_type = "A",
28 .expected_address = NULL,
29 .dns_server = NULL,
30 .query_transport = "",
31 .server_port = DEFAULT_PORT,
32 .dig_args = "",
33 .number_tries = DEFAULT_TRIES,
34
35 .warning_interval = UNDEFINED,
36 .critical_interval = UNDEFINED,
37
38 };
39 return tmp;
40}
diff --git a/plugins/check_disk.c b/plugins/check_disk.c
index 037a6f7a..d42b5486 100644
--- a/plugins/check_disk.c
+++ b/plugins/check_disk.c
@@ -31,24 +31,35 @@ const char *program_name = "check_disk"; /* Required for coreutils libs */
31const char *copyright = "1999-2024"; 31const char *copyright = "1999-2024";
32const char *email = "devel@monitoring-plugins.org"; 32const char *email = "devel@monitoring-plugins.org";
33 33
34#include "states.h"
34#include "common.h" 35#include "common.h"
36#include "output.h"
37#include "perfdata.h"
38#include "utils_base.h"
39#include "lib/thresholds.h"
40
35#ifdef HAVE_SYS_STAT_H 41#ifdef HAVE_SYS_STAT_H
36# include <sys/stat.h> 42# include <sys/stat.h>
37#endif 43#endif
44
38#if HAVE_INTTYPES_H 45#if HAVE_INTTYPES_H
39# include <inttypes.h> 46# include <inttypes.h>
40#endif 47#endif
48
41#include <assert.h> 49#include <assert.h>
42#include "popen.h"
43#include "utils.h"
44#include "utils_disk.h"
45#include <stdarg.h> 50#include <stdarg.h>
46#include "fsusage.h" 51#include <stdint.h>
47#include "mountlist.h"
48#include <float.h> 52#include <float.h>
53#include "./popen.h"
54#include "./utils.h"
55#include "../gl/fsusage.h"
56#include "../gl/mountlist.h"
57#include "./check_disk.d/utils_disk.h"
58
49#if HAVE_LIMITS_H 59#if HAVE_LIMITS_H
50# include <limits.h> 60# include <limits.h>
51#endif 61#endif
62
52#include "regex.h" 63#include "regex.h"
53 64
54#ifdef __CYGWIN__ 65#ifdef __CYGWIN__
@@ -57,424 +68,322 @@ const char *email = "devel@monitoring-plugins.org";
57# define ERROR -1 68# define ERROR -1
58#endif 69#endif
59 70
60/* If nonzero, show even filesystems with zero size or
61 uninteresting types. */
62static int show_all_fs = 1;
63
64/* If nonzero, show only local filesystems. */
65static int show_local_fs = 0;
66
67/* If nonzero, show only local filesystems but call stat() on remote ones. */
68static int stat_remote_fs = 0;
69
70/* If positive, the units to use when printing sizes;
71 if negative, the human-readable base. */
72/* static int output_block_size; */
73
74/* If nonzero, invoke the `sync' system call before getting any usage data.
75 Using this option can make df very slow, especially with many or very
76 busy disks. Note that this may make a difference on some systems --
77 SunOs4.1.3, for one. It is *not* necessary on Linux. */
78/* static int require_sync = 0; */
79
80/* Linked list of filesystem types to display.
81 If `fs_select_list' is NULL, list all types.
82 This table is generated dynamically from command-line options,
83 rather than hardcoding into the program what it thinks are the
84 valid filesystem types; let the user specify any filesystem type
85 they want to, and if there are any filesystems of that type, they
86 will be shown.
87
88 Some filesystem types:
89 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
90
91/* static struct parameter_list *fs_select_list; */
92
93/* Linked list of filesystem types to omit.
94 If the list is empty, don't exclude any types. */
95static struct regex_list *fs_exclude_list = NULL;
96
97/* Linked list of filesystem types to check.
98 If the list is empty, include all types. */
99static struct regex_list *fs_include_list;
100
101static struct name_list *dp_exclude_list;
102
103static struct parameter_list *path_select_list = NULL;
104
105/* Linked list of mounted filesystems. */
106static struct mount_entry *mount_list;
107
108/* For long options that have no equivalent short option, use a
109 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
110enum {
111 SYNC_OPTION = CHAR_MAX + 1,
112 NO_SYNC_OPTION,
113 BLOCK_SIZE_OPTION
114};
115
116#ifdef _AIX 71#ifdef _AIX
117# pragma alloca 72# pragma alloca
118#endif 73#endif
119 74
120static int process_arguments(int /*argc*/, char ** /*argv*/); 75typedef struct {
121static void set_all_thresholds(struct parameter_list *path); 76 int errorcode;
122static void print_help(void); 77 check_disk_config config;
78} check_disk_config_wrapper;
79static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
80
81static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
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
123void print_usage(void); 97void print_usage(void);
124static double calculate_percent(uintmax_t, uintmax_t); 98static void print_help(void);
125static bool stat_path(struct parameter_list *p);
126static void get_stats(struct parameter_list *p, struct fs_usage *fsp);
127static void get_path_stats(struct parameter_list *p, struct fs_usage *fsp);
128 99
129static char *units;
130static uintmax_t mult = 1024 * 1024;
131static int verbose = 0; 100static int verbose = 0;
132static bool erronly = false;
133static bool display_mntp = false;
134static bool exact_match = false;
135static bool ignore_missing = false;
136static bool freespace_ignore_reserved = false;
137static bool display_inodes_perfdata = false;
138static char *warn_freespace_units = NULL;
139static char *crit_freespace_units = NULL;
140static char *warn_freespace_percent = NULL;
141static char *crit_freespace_percent = NULL;
142static char *warn_usedspace_units = NULL;
143static char *crit_usedspace_units = NULL;
144static char *warn_usedspace_percent = NULL;
145static char *crit_usedspace_percent = NULL;
146static char *warn_usedinodes_percent = NULL;
147static char *crit_usedinodes_percent = NULL;
148static char *warn_freeinodes_percent = NULL;
149static char *crit_freeinodes_percent = NULL;
150static bool path_selected = false;
151static bool path_ignored = false;
152static char *group = NULL;
153static struct stat *stat_buf;
154static struct name_list *seen = NULL;
155 101
156int main(int argc, char **argv) { 102// This would not be necessary in C23!!
157 int result = STATE_UNKNOWN; 103const byte_unit Bytes_Factor = 1;
158 int disk_result = STATE_UNKNOWN; 104const byte_unit KibiBytes_factor = 1024;
159 char *output = NULL; 105const byte_unit MebiBytes_factor = 1048576;
160 char *ignored = NULL; 106const byte_unit GibiBytes_factor = 1073741824;
161 char *details = NULL; 107const byte_unit TebiBytes_factor = 1099511627776;
162 char *perf = NULL; 108const byte_unit PebiBytes_factor = 1125899906842624;
163 char *perf_ilabel = NULL; 109const byte_unit ExbiBytes_factor = 1152921504606846976;
164 char *preamble = " - free space:"; 110const byte_unit KiloBytes_factor = 1000;
165 char *ignored_preamble = " - ignored paths:"; 111const byte_unit MegaBytes_factor = 1000000;
166 char *flag_header = NULL; 112const byte_unit GigaBytes_factor = 1000000000;
167 int temp_result = STATE_UNKNOWN; 113const byte_unit TeraBytes_factor = 1000000000000;
168 114const byte_unit PetaBytes_factor = 1000000000000000;
169 struct mount_entry *me = NULL; 115const byte_unit ExaBytes_factor = 1000000000000000000;
170 struct fs_usage fsp = {0};
171 struct parameter_list *temp_list = NULL;
172 struct parameter_list *path = NULL;
173
174#ifdef __CYGWIN__
175 char mountdir[32];
176#endif
177
178 output = strdup("");
179 ignored = strdup("");
180 details = strdup("");
181 perf = strdup("");
182 perf_ilabel = strdup("");
183 stat_buf = malloc(sizeof *stat_buf);
184 116
117int main(int argc, char **argv) {
185 setlocale(LC_ALL, ""); 118 setlocale(LC_ALL, "");
186 bindtextdomain(PACKAGE, LOCALEDIR); 119 bindtextdomain(PACKAGE, LOCALEDIR);
187 textdomain(PACKAGE); 120 textdomain(PACKAGE);
188 121
189 mount_list = read_file_system_list(0); 122#ifdef __CYGWIN__
123 char mountdir[32];
124#endif
190 125
191 /* Parse extra opts if any */ 126 // Parse extra opts if any
192 argv = np_extra_opts(&argc, argv, progname); 127 argv = np_extra_opts(&argc, argv, progname);
193 128
194 if (process_arguments(argc, argv) == ERROR) 129 check_disk_config_wrapper tmp_config = process_arguments(argc, argv);
130 if (tmp_config.errorcode == ERROR) {
195 usage4(_("Could not parse arguments")); 131 usage4(_("Could not parse arguments"));
132 }
196 133
197 /* If a list of paths has not been selected, find entire 134 check_disk_config config = tmp_config.config;
198 mount list and create list of paths 135
199 */ 136 if (config.output_format_is_set) {
200 if (path_selected == false && path_ignored == false) { 137 mp_set_format(config.output_format);
201 for (me = mount_list; me; me = me->me_next) {
202 if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) {
203 path = np_add_parameter(&path_select_list, me->me_mountdir);
204 }
205 path->best_match = me;
206 path->group = group;
207 set_all_thresholds(path);
208 }
209 } 138 }
210 139
211 if (path_ignored == false) { 140 if (config.erronly) {
212 np_set_best_match(path_select_list, mount_list, exact_match); 141 mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY);
213 } 142 }
214 143
215 /* Error if no match found for specified paths */ 144 if (!config.path_ignored) {
216 temp_list = path_select_list; 145 mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list,
146 config.exact_match);
147 }
217 148
218 while (path_select_list) { 149 // Error if no match found for specified paths
219 if (!path_select_list->best_match && ignore_missing == true) { 150 for (parameter_list_elem *elem = config.path_select_list.first; elem;) {
220 /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ 151 if (!elem->best_match && config.ignore_missing) {
221 if (path_select_list == temp_list) {
222 temp_list = path_select_list->name_next;
223 }
224 /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */
225 xasprintf(&ignored, "%s %s;", ignored, path_select_list->name);
226 /* Delete the path from the list so that it is not stat-checked later in the code. */ 152 /* Delete the path from the list so that it is not stat-checked later in the code. */
227 path_select_list = np_del_parameter(path_select_list, path_select_list->name_prev); 153 elem = mp_int_fs_list_del(&config.path_select_list, elem);
228 } else if (!path_select_list->best_match) { 154 continue;
155 }
156 if (!elem->best_match) {
229 /* Without --ignore-missing option, exit with Critical state. */ 157 /* Without --ignore-missing option, exit with Critical state. */
230 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list->name); 158 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name);
231 } else {
232 /* Continue jumping through the list */
233 path_select_list = path_select_list->name_next;
234 } 159 }
235 }
236 160
237 path_select_list = temp_list; 161 elem = mp_int_fs_list_get_next(elem);
162 }
238 163
239 if (!path_select_list && ignore_missing == true) { 164 mp_check overall = mp_check_init();
240 result = STATE_OK; 165 if (config.path_select_list.length == 0) {
241 if (verbose >= 2) { 166 mp_subcheck none_sc = mp_subcheck_init();
242 printf("None of the provided paths were found\n"); 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 }
243 } 175 }
176 mp_add_subcheck_to_check(&overall, none_sc);
177 mp_exit(overall);
244 } 178 }
245 179
246 /* Process for every path in list */ 180 // Filter list first
247 for (path = path_select_list; path; path = path->name_next) { 181 for (parameter_list_elem *path = config.path_select_list.first; path;) {
248 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) 182 if (!path->best_match) {
249 printf("Thresholds(pct) for %s warn: %f crit %f\n", path->name, path->freespace_percent->warning->end, 183 path = mp_int_fs_list_del(&config.path_select_list, path);
250 path->freespace_percent->critical->end);
251
252 if (verbose >= 3 && path->group != NULL)
253 printf("Group of %s: %s\n", path->name, path->group);
254
255 /* reset disk result */
256 disk_result = STATE_UNKNOWN;
257
258 me = path->best_match;
259
260 if (!me) {
261 continue; 184 continue;
262 } 185 }
263 186
187 struct mount_entry *mount_entry = path->best_match;
188
264#ifdef __CYGWIN__ 189#ifdef __CYGWIN__
265 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) 190 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) {
191 path = mp_int_fs_list_del(&config.path_select_list, path);
266 continue; 192 continue;
193 }
194
195 char *mountdir = NULL;
267 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); 196 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10);
268 if (GetDriveType(mountdir) != DRIVE_FIXED) 197 if (GetDriveType(mountdir) != DRIVE_FIXED) {
269 me->me_remote = 1; 198 mount_entry->me_remote = 1;
199 }
270#endif 200#endif
271 /* Filters */
272 201
273 /* Remove filesystems already seen */ 202 /* Remove filesystems already seen */
274 if (np_seen_name(seen, me->me_mountdir)) { 203 if (np_seen_name(config.seen, mount_entry->me_mountdir)) {
204 path = mp_int_fs_list_del(&config.path_select_list, path);
275 continue; 205 continue;
276 } 206 }
277 np_add_name(&seen, me->me_mountdir);
278 207
279 if (path->group == NULL) { 208 if (path->group == NULL) {
280 /* Skip remote filesystems if we're not interested in them */ 209 if (config.fs_exclude_list &&
281 if (me->me_remote && show_local_fs) { 210 np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) {
282 if (stat_remote_fs) { 211 // Skip excluded fs's
283 if (!stat_path(path) && ignore_missing == true) { 212 path = mp_int_fs_list_del(&config.path_select_list, path);
284 result = STATE_OK;
285 xasprintf(&ignored, "%s %s;", ignored, path->name);
286 }
287 }
288 continue; 213 continue;
289 /* Skip pseudo fs's if we haven't asked for all fs's */
290 } 214 }
291 if (me->me_dummy && !show_all_fs) { 215
292 continue; 216 if (config.device_path_exclude_list &&
293 /* Skip excluded fstypes */ 217 (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) ||
294 } 218 np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) {
295 if (fs_exclude_list && np_find_regmatch(fs_exclude_list, me->me_type)) { 219 // Skip excluded device or mount paths
220 path = mp_int_fs_list_del(&config.path_select_list, path);
296 continue; 221 continue;
297 /* Skip excluded fs's */
298 } 222 }
299 if (dp_exclude_list && (np_find_name(dp_exclude_list, me->me_devname) || np_find_name(dp_exclude_list, me->me_mountdir))) { 223
224 if (config.fs_include_list &&
225 !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) {
226 // Skip not included fstypes
227 path = mp_int_fs_list_del(&config.path_select_list, path);
300 continue; 228 continue;
301 /* Skip not included fstypes */
302 } 229 }
303 if (fs_include_list && !np_find_regmatch(fs_include_list, me->me_type)) { 230
231 /* Skip remote filesystems if we're not interested in them */
232 if (mount_entry->me_remote && config.show_local_fs) {
233 if (config.stat_remote_fs) {
234 // TODO Stat here
235 if (!stat_path(path, config.ignore_missing) && config.ignore_missing) {
236 }
237 }
304 continue; 238 continue;
305 } 239 }
306 }
307 240
308 if (!stat_path(path)) { 241 // TODO why stat here? remove unstatable fs?
309 if (ignore_missing == true) { 242 if (!stat_path(path, config.ignore_missing)) {
310 result = STATE_OK; 243 // if (config.ignore_missing) {
311 xasprintf(&ignored, "%s %s;", ignored, path->name); 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;
312 } 249 }
313 continue;
314 } 250 }
315 get_fs_usage(me->me_mountdir, me->me_devname, &fsp);
316
317 if (fsp.fsu_blocks && strcmp("none", me->me_mountdir)) {
318 get_stats(path, &fsp);
319
320 if (verbose >= 3) {
321 printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f "
322 "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n",
323 me->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units,
324 path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult);
325 }
326
327 /* Threshold comparisons */
328
329 temp_result = get_status(path->dfree_units, path->freespace_units);
330 if (verbose >= 3)
331 printf("Freespace_units result=%d\n", temp_result);
332 disk_result = max_state(disk_result, temp_result);
333
334 temp_result = get_status(path->dfree_pct, path->freespace_percent);
335 if (verbose >= 3)
336 printf("Freespace%% result=%d\n", temp_result);
337 disk_result = max_state(disk_result, temp_result);
338 251
339 temp_result = get_status(path->dused_units, path->usedspace_units); 252 path = mp_int_fs_list_get_next(path);
340 if (verbose >= 3) 253 }
341 printf("Usedspace_units result=%d\n", temp_result);
342 disk_result = max_state(disk_result, temp_result);
343
344 temp_result = get_status(path->dused_pct, path->usedspace_percent);
345 if (verbose >= 3)
346 printf("Usedspace_percent result=%d\n", temp_result);
347 disk_result = max_state(disk_result, temp_result);
348
349 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
350 if (verbose >= 3)
351 printf("Usedinodes_percent result=%d\n", temp_result);
352 disk_result = max_state(disk_result, temp_result);
353
354 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
355 if (verbose >= 3)
356 printf("Freeinodes_percent result=%d\n", temp_result);
357 disk_result = max_state(disk_result, temp_result);
358
359 result = max_state(result, disk_result);
360
361 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
362 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
363 data. Assumption that start=0. Roll on new syntax...
364 */
365
366 /* *_high_tide must be reinitialized at each run */
367 uint64_t warning_high_tide = UINT64_MAX;
368 254
369 if (path->freespace_units->warning != NULL) { 255 // now get the actual measurements
370 warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * mult; 256 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) {
371 } 257 // Get actual metrics here
372 if (path->freespace_percent->warning != NULL) { 258 struct mount_entry *mount_entry = filesystem->best_match;
373 warning_high_tide = 259 struct fs_usage fsp = {0};
374 min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * (path->dtotal_units * mult))); 260 get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp);
375 }
376 261
377 uint64_t critical_high_tide = UINT64_MAX; 262 if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) {
263 *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved);
378 264
379 if (path->freespace_units->critical != NULL) { 265 if (verbose >= 3) {
380 critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * mult; 266 printf("For %s, used_units=%lu free_units=%lu total_units=%lu "
381 } 267 "fsp.fsu_blocksize=%lu\n",
382 if (path->freespace_percent->critical != NULL) { 268 mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes,
383 critical_high_tide = 269 filesystem->total_bytes, fsp.fsu_blocksize);
384 min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * (path->dtotal_units * mult)));
385 } 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 }
386 278
387 /* Nb: *_high_tide are unset when == UINT64_MAX */ 279 if (verbose > 2) {
388 xasprintf(&perf, "%s %s", perf, 280 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
389 perfdata_uint64((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, 281 filesystem = mp_int_fs_list_get_next(filesystem)) {
390 path->dused_units * mult, "B", (warning_high_tide == UINT64_MAX ? false : true), warning_high_tide, 282 assert(filesystem->best_match != NULL);
391 (critical_high_tide == UINT64_MAX ? false : true), critical_high_tide, true, 0, true, 283 if (filesystem->best_match == NULL) {
392 path->dtotal_units * mult)); 284 printf("Filesystem path %s has no mount_entry!\n", filesystem->name);
393 285 } else {
394 if (display_inodes_perfdata) { 286 // printf("Filesystem path %s has a mount_entry!\n", filesystem->name);
395 /* *_high_tide must be reinitialized at each run */
396 warning_high_tide = UINT64_MAX;
397 critical_high_tide = UINT64_MAX;
398
399 if (path->freeinodes_percent->warning != NULL) {
400 warning_high_tide = (uint64_t)fabs(
401 min((double)warning_high_tide, (double)(1.0 - path->freeinodes_percent->warning->end / 100) * path->inodes_total));
402 }
403 if (path->freeinodes_percent->critical != NULL) {
404 critical_high_tide = (uint64_t)fabs(min(
405 (double)critical_high_tide, (double)(1.0 - path->freeinodes_percent->critical->end / 100) * path->inodes_total));
406 }
407
408 xasprintf(&perf_ilabel, "%s (inodes)",
409 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir);
410 /* Nb: *_high_tide are unset when == UINT64_MAX */
411 xasprintf(&perf, "%s %s", perf,
412 perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX ? true : false),
413 warning_high_tide, (critical_high_tide != UINT64_MAX ? true : false), critical_high_tide, true, 0,
414 true, path->inodes_total));
415 } 287 }
288 }
289 }
416 290
417 if (disk_result == STATE_OK && erronly && !verbose) 291 measurement_unit_list *measurements = NULL;
418 continue; 292 measurement_unit_list *current = NULL;
419 293 // create measuring units, because of groups
420 if (disk_result && verbose >= 1) { 294 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
421 xasprintf(&flag_header, " %s [", state_text(disk_result)); 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);
422 } else { 304 } else {
423 xasprintf(&flag_header, ""); 305 current = add_measurement_list(measurements, unit);
424 } 306 }
425 xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, 307 } else {
426 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, path->dfree_units, units, 308 // Grouped elements are consecutive
427 path->dfree_pct); 309 if (measurements == NULL) {
428 if (path->dused_inodes_percent < 0) { 310 // first entry
429 xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : "")); 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);
430 } else { 315 } else {
431 xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : "")); 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 }
432 } 329 }
433 free(flag_header);
434 } 330 }
435 } 331 }
436 332
437 if (verbose >= 2) 333 /* Process for every path in list */
438 xasprintf(&output, "%s%s", output, details); 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");
439 344
440 if (strcmp(output, "") == 0 && !erronly) { 345 if (config.ignore_missing) {
441 preamble = ""; 346 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
442 xasprintf(&output, " - No disks were found for provided parameters"); 347 } else {
348 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
349 }
350 mp_add_subcheck_to_check(&overall, none_sc);
443 } 351 }
444 352
445 printf("DISK %s%s%s%s%s|%s\n", state_text(result), ((erronly && result == STATE_OK)) ? "" : preamble, output, 353 mp_exit(overall);
446 (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf);
447 return result;
448} 354}
449 355
450double calculate_percent(uintmax_t value, uintmax_t total) { 356double calculate_percent(uintmax_t value, uintmax_t total) {
451 double pct = -1; 357 double pct = -1;
452 if (value <= DBL_MAX && total != 0) { 358 if (value <= DBL_MAX && total != 0) {
453 pct = (double)value / total * 100.0; 359 pct = (double)value / (double)total * 100.0;
454 } 360 }
361
455 return pct; 362 return pct;
456} 363}
457 364
458/* process command-line arguments */ 365/* process command-line arguments */
459int process_arguments(int argc, char **argv) { 366check_disk_config_wrapper process_arguments(int argc, char **argv) {
460 int c; 367
461 int err; 368 check_disk_config_wrapper result = {
462 struct parameter_list *se; 369 .errorcode = OK,
463 struct parameter_list *temp_list = NULL; 370 .config = check_disk_config_init(),
464 struct parameter_list *previous = NULL; 371 };
465 struct mount_entry *me; 372
466 regex_t re; 373 if (argc < 2) {
467 int cflags = REG_NOSUB | REG_EXTENDED; 374 result.errorcode = ERROR;
468 int default_cflags = cflags; 375 return result;
469 char errbuf[MAX_INPUT_BUFFER]; 376 }
470 int fnd = 0; 377
378 enum {
379 output_format_index = CHAR_MAX + 1,
380 display_unit_index,
381 };
471 382
472 int option = 0;
473 static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, 383 static struct option longopts[] = {{"timeout", required_argument, 0, 't'},
474 {"warning", required_argument, 0, 'w'}, 384 {"warning", required_argument, 0, 'w'},
475 {"critical", required_argument, 0, 'c'}, 385 {"critical", required_argument, 0, 'c'},
476 {"iwarning", required_argument, 0, 'W'}, 386 {"iwarning", required_argument, 0, 'W'},
477 /* Dang, -C is taken. We might want to reshuffle this. */
478 {"icritical", required_argument, 0, 'K'}, 387 {"icritical", required_argument, 0, 'K'},
479 {"kilobytes", no_argument, 0, 'k'}, 388 {"kilobytes", no_argument, 0, 'k'},
480 {"megabytes", no_argument, 0, 'm'}, 389 {"megabytes", no_argument, 0, 'm'},
@@ -507,24 +416,43 @@ int process_arguments(int argc, char **argv) {
507 {"clear", no_argument, 0, 'C'}, 416 {"clear", no_argument, 0, 'C'},
508 {"version", no_argument, 0, 'V'}, 417 {"version", no_argument, 0, 'V'},
509 {"help", no_argument, 0, 'h'}, 418 {"help", no_argument, 0, 'h'},
419 {"output-format", required_argument, 0, output_format_index},
420 {"display-unit", required_argument, 0, display_unit_index},
510 {0, 0, 0, 0}}; 421 {0, 0, 0, 0}};
511 422
512 if (argc < 2) 423 for (int index = 1; index < argc; index++) {
513 return ERROR; 424 if (strcmp("-to", argv[index]) == 0) {
425 strcpy(argv[index], "-t");
426 }
427 }
428
429 int cflags = REG_NOSUB | REG_EXTENDED;
430 int default_cflags = cflags;
431 char *warn_freespace_units = NULL;
432 char *crit_freespace_units = NULL;
433 char *warn_freespace_percent = NULL;
434 char *crit_freespace_percent = NULL;
435 char *warn_freeinodes_percent = NULL;
436 char *crit_freeinodes_percent = NULL;
514 437
515 np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); 438 bool path_selected = false;
439 char *group = NULL;
440 byte_unit unit = MebiBytes_factor;
516 441
517 for (c = 1; c < argc; c++) 442 result.config.mount_list = read_file_system_list(false);
518 if (strcmp("-to", argv[c]) == 0)
519 strcpy(argv[c], "-t");
520 443
521 while (1) { 444 np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED);
522 c = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
523 445
524 if (c == -1 || c == EOF) 446 while (true) {
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 (option_index == -1 || option_index == EOF) {
525 break; 452 break;
453 }
526 454
527 switch (c) { 455 switch (option_index) {
528 case 't': /* timeout period */ 456 case 't': /* timeout period */
529 if (is_integer(optarg)) { 457 if (is_integer(optarg)) {
530 timeout_interval = atoi(optarg); 458 timeout_interval = atoi(optarg);
@@ -555,10 +483,10 @@ int process_arguments(int argc, char **argv) {
555 break; 483 break;
556 484
557 /* Awful mistake where the range values do not make sense. Normally, 485 /* Awful mistake where the range values do not make sense. Normally,
558 you alert if the value is within the range, but since we are using 486 * you alert if the value is within the range, but since we are using
559 freespace, we have to alert if outside the range. Thus we artificially 487 * freespace, we have to alert if outside the range. Thus we artificially
560 force @ at the beginning of the range, so that it is backwards compatible 488 * force @ at the beginning of the range, so that it is backwards compatible
561 */ 489 */
562 case 'c': /* critical threshold */ 490 case 'c': /* critical threshold */
563 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { 491 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
564 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);
@@ -594,181 +522,193 @@ int process_arguments(int argc, char **argv) {
594 } 522 }
595 break; 523 break;
596 case 'u': 524 case 'u':
597 if (units)
598 free(units);
599 if (!strcasecmp(optarg, "bytes")) { 525 if (!strcasecmp(optarg, "bytes")) {
600 mult = (uintmax_t)1; 526 unit = Bytes_Factor;
601 units = strdup("B");
602 } else if (!strcmp(optarg, "KiB")) { 527 } else if (!strcmp(optarg, "KiB")) {
603 mult = (uintmax_t)1024; 528 unit = KibiBytes_factor;
604 units = strdup("KiB");
605 } else if (!strcmp(optarg, "kB")) { 529 } else if (!strcmp(optarg, "kB")) {
606 mult = (uintmax_t)1000; 530 unit = KiloBytes_factor;
607 units = strdup("kB");
608 } else if (!strcmp(optarg, "MiB")) { 531 } else if (!strcmp(optarg, "MiB")) {
609 mult = (uintmax_t)1024 * 1024; 532 unit = MebiBytes_factor;
610 units = strdup("MiB");
611 } else if (!strcmp(optarg, "MB")) { 533 } else if (!strcmp(optarg, "MB")) {
612 mult = (uintmax_t)1000 * 1000; 534 unit = MegaBytes_factor;
613 units = strdup("MB");
614 } else if (!strcmp(optarg, "GiB")) { 535 } else if (!strcmp(optarg, "GiB")) {
615 mult = (uintmax_t)1024 * 1024 * 1024; 536 unit = GibiBytes_factor;
616 units = strdup("GiB");
617 } else if (!strcmp(optarg, "GB")) { 537 } else if (!strcmp(optarg, "GB")) {
618 mult = (uintmax_t)1000 * 1000 * 1000; 538 unit = GigaBytes_factor;
619 units = strdup("GB");
620 } else if (!strcmp(optarg, "TiB")) { 539 } else if (!strcmp(optarg, "TiB")) {
621 mult = (uintmax_t)1024 * 1024 * 1024 * 1024; 540 unit = TebiBytes_factor;
622 units = strdup("TiB");
623 } else if (!strcmp(optarg, "TB")) { 541 } else if (!strcmp(optarg, "TB")) {
624 mult = (uintmax_t)1000 * 1000 * 1000 * 1000; 542 unit = TeraBytes_factor;
625 units = strdup("TB");
626 } else if (!strcmp(optarg, "PiB")) { 543 } else if (!strcmp(optarg, "PiB")) {
627 mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; 544 unit = PebiBytes_factor;
628 units = strdup("PiB");
629 } else if (!strcmp(optarg, "PB")) { 545 } else if (!strcmp(optarg, "PB")) {
630 mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; 546 unit = PetaBytes_factor;
631 units = strdup("PB");
632 } else { 547 } else {
633 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); 548 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
634 } 549 }
635 if (units == NULL)
636 die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
637 break; 550 break;
638 case 'k': /* display mountpoint */ 551 case 'k':
639 mult = 1024; 552 unit = KibiBytes_factor;
640 if (units)
641 free(units);
642 units = strdup("kiB");
643 break; 553 break;
644 case 'm': /* display mountpoint */ 554 case 'm':
645 mult = 1024 * 1024; 555 unit = MebiBytes_factor;
646 if (units) 556 break;
647 free(units); 557 case display_unit_index:
648 units = strdup("MiB"); 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 }
649 break; 583 break;
650 case 'L': 584 case 'L':
651 stat_remote_fs = 1; 585 result.config.stat_remote_fs = true;
652 /* fallthrough */ 586 /* fallthrough */
653 case 'l': 587 case 'l':
654 show_local_fs = 1; 588 result.config.show_local_fs = true;
655 break; 589 break;
656 case 'P': 590 case 'P':
657 display_inodes_perfdata = 1; 591 result.config.display_inodes_perfdata = true;
658 break; 592 break;
659 case 'p': /* select path */ 593 case 'p': /* select path */ {
660 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || 594 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
661 warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || 595 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
662 warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { 596 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
663 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); 597 _("Must set a threshold value before using -p\n"));
664 } 598 }
665 599
666 /* add parameter if not found. overwrite thresholds if path has already been added */ 600 /* add parameter if not found. overwrite thresholds if path has already been added */
667 if (!(se = np_find_parameter(path_select_list, optarg))) { 601 parameter_list_elem *search_entry;
668 se = np_add_parameter(&path_select_list, optarg); 602 if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) {
669 603 search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg);
670 if (stat(optarg, &stat_buf[0]) && ignore_missing == true) { 604
671 path_ignored = true; 605 // struct stat stat_buf = {};
672 break; 606 // if (stat(optarg, &stat_buf) && result.config.ignore_missing) {
673 } 607 // result.config.path_ignored = true;
608 // break;
609 // }
674 } 610 }
675 se->group = group; 611 search_entry->group = group;
676 set_all_thresholds(se); 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);
677 616
678 /* With autofs, it is required to stat() the path before re-populating the mount_list */ 617 /* With autofs, it is required to stat() the path before re-populating the mount_list */
679 if (!stat_path(se)) { 618 // if (!stat_path(se, result.config.ignore_missing)) {
680 break; 619 // break;
681 } 620 // }
682 /* NB: We can't free the old mount_list "just like that": both list pointers and struct 621 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
683 * pointers are copied around. One of the reason it wasn't done yet is that other parts 622 result.config.exact_match);
684 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
685 mount_list = read_file_system_list(0);
686 np_set_best_match(se, mount_list, exact_match);
687 623
688 path_selected = true; 624 path_selected = true;
689 break; 625 } break;
690 case 'x': /* exclude path or partition */ 626 case 'x': /* exclude path or partition */
691 np_add_name(&dp_exclude_list, optarg); 627 np_add_name(&result.config.device_path_exclude_list, optarg);
692 break; 628 break;
693 case 'X': /* exclude file system type */ 629 case 'X': /* exclude file system type */ {
694 err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED); 630 int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED);
695 if (err != 0) { 631 if (err != 0) {
696 regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); 632 char errbuf[MAX_INPUT_BUFFER];
697 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 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);
698 } 636 }
699 break; 637 break;
700 case 'N': /* include file system type */ 638 case 'N': /* include file system type */
701 err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED); 639 err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED);
702 if (err != 0) { 640 if (err != 0) {
703 regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); 641 char errbuf[MAX_INPUT_BUFFER];
704 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 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);
705 } 645 }
706 break; 646 } break;
707 case 'v': /* verbose */ 647 case 'v': /* verbose */
708 verbose++; 648 verbose++;
709 break; 649 break;
710 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */ 650 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
711 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */ 651 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
712 erronly = true; 652 result.config.erronly = true;
713 break; 653 break;
714 case 'e': 654 case 'e':
715 erronly = true; 655 result.config.erronly = true;
716 break; 656 break;
717 case 'E': 657 case 'E':
718 if (path_selected) 658 if (path_selected) {
719 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); 659 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
720 exact_match = true; 660 _("Must set -E before selecting paths\n"));
661 }
662 result.config.exact_match = true;
721 break; 663 break;
722 case 'f': 664 case 'f':
723 freespace_ignore_reserved = true; 665 result.config.freespace_ignore_reserved = true;
724 break; 666 break;
725 case 'g': 667 case 'g':
726 if (path_selected) 668 if (path_selected) {
727 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); 669 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
670 _("Must set group value before selecting paths\n"));
671 }
728 group = optarg; 672 group = optarg;
729 break; 673 break;
730 case 'I': 674 case 'I':
731 cflags |= REG_ICASE; 675 cflags |= REG_ICASE;
732 // Intentional fallthrough 676 // Intentional fallthrough
733 case 'i': 677 case 'i': {
734 if (!path_selected) 678 if (!path_selected) {
735 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), 679 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"),
736 _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); 680 _("Paths need to be selected before using -i/-I. Use -A to select all paths "
737 err = regcomp(&re, optarg, cflags); 681 "explicitly"));
682 }
683 regex_t regex;
684 int err = regcomp(&regex, optarg, cflags);
738 if (err != 0) { 685 if (err != 0) {
739 regerror(err, &re, errbuf, MAX_INPUT_BUFFER); 686 char errbuf[MAX_INPUT_BUFFER];
740 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 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);
741 } 690 }
742 691
743 temp_list = path_select_list; 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)) {
744 695
745 previous = NULL; 696 if (verbose >= 3) {
746 while (temp_list) { 697 printf("ignoring %s matching regex\n", elem->name);
747 if (temp_list->best_match) { 698 }
748 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
749 699
750 if (verbose >= 3) 700 elem = mp_int_fs_list_del(&result.config.path_select_list, elem);
751 printf("ignoring %s matching regex\n", temp_list->name); 701 continue;
752
753 temp_list = np_del_parameter(temp_list, previous);
754 /* pointer to first element needs to be updated if first item gets deleted */
755 if (previous == NULL)
756 path_select_list = temp_list;
757 } else {
758 previous = temp_list;
759 temp_list = temp_list->name_next;
760 } 702 }
761 } else {
762 previous = temp_list;
763 temp_list = temp_list->name_next;
764 } 703 }
704
705 elem = mp_int_fs_list_get_next(elem);
765 } 706 }
766 707
767 cflags = default_cflags; 708 cflags = default_cflags;
768 break; 709 } break;
769
770 case 'n': 710 case 'n':
771 ignore_missing = true; 711 result.config.ignore_missing = true;
772 break; 712 break;
773 case 'A': 713 case 'A':
774 optarg = strdup(".*"); 714 optarg = strdup(".*");
@@ -776,80 +716,96 @@ int process_arguments(int argc, char **argv) {
776 case 'R': 716 case 'R':
777 cflags |= REG_ICASE; 717 cflags |= REG_ICASE;
778 // Intentional fallthrough 718 // Intentional fallthrough
779 case 'r': 719 case 'r': {
780 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || 720 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
781 warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || 721 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
782 warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
783 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), 722 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
784 _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); 723 _("Must set a threshold value before using -r/-R/-A "
724 "(--ereg-path/--eregi-path/--all)\n"));
785 } 725 }
786 726
787 err = regcomp(&re, optarg, cflags); 727 regex_t regex;
728 int err = regcomp(&regex, optarg, cflags);
788 if (err != 0) { 729 if (err != 0) {
789 regerror(err, &re, errbuf, MAX_INPUT_BUFFER); 730 char errbuf[MAX_INPUT_BUFFER];
790 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 731 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
732 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
733 _("Could not compile regular expression"), errbuf);
791 } 734 }
792 735
793 for (me = mount_list; me; me = me->me_next) { 736 bool found = false;
794 if (np_regex_match_mount_entry(me, &re)) { 737 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
795 fnd = true; 738 if (np_regex_match_mount_entry(me, &regex)) {
796 if (verbose >= 3) 739 found = true;
797 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); 740 if (verbose >= 3) {
741 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir,
742 optarg);
743 }
798 744
799 /* add parameter if not found. overwrite thresholds if path has already been added */ 745 /* add parameter if not found. overwrite thresholds if path has already been
800 if (!(se = np_find_parameter(path_select_list, me->me_mountdir))) { 746 * added */
801 se = np_add_parameter(&path_select_list, me->me_mountdir); 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);
802 } 752 }
803 se->group = group; 753 se->group = group;
804 set_all_thresholds(se); 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);
805 } 757 }
806 } 758 }
807 759
808 if (!fnd && ignore_missing == true) { 760 if (!found) {
809 path_ignored = true; 761 if (result.config.ignore_missing) {
810 path_selected = true; 762 result.config.path_ignored = true;
811 break; 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);
812 } 769 }
813 if (!fnd)
814 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg);
815 770
816 fnd = false;
817 path_selected = true; 771 path_selected = true;
818 np_set_best_match(path_select_list, mount_list, exact_match); 772 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
773 result.config.exact_match);
819 cflags = default_cflags; 774 cflags = default_cflags;
820 775
821 break; 776 } break;
822 case 'M': /* display mountpoint */ 777 case 'M': /* display mountpoint */
823 display_mntp = true; 778 result.config.display_mntp = true;
824 break; 779 break;
825 case 'C': 780 case 'C': {
826 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ 781 /* add all mount entries to path_select list if no partitions have been explicitly
827 if (path_selected == false) { 782 * defined using -p */
828 struct parameter_list *path; 783 if (!path_selected) {
829 for (me = mount_list; me; me = me->me_next) { 784 parameter_list_elem *path;
830 if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) 785 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
831 path = np_add_parameter(&path_select_list, me->me_mountdir); 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 }
832 path->best_match = me; 791 path->best_match = me;
833 path->group = group; 792 path->group = group;
834 set_all_thresholds(path); 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);
835 } 796 }
836 } 797 }
798
837 warn_freespace_units = NULL; 799 warn_freespace_units = NULL;
838 crit_freespace_units = NULL; 800 crit_freespace_units = NULL;
839 warn_usedspace_units = NULL;
840 crit_usedspace_units = NULL;
841 warn_freespace_percent = NULL; 801 warn_freespace_percent = NULL;
842 crit_freespace_percent = NULL; 802 crit_freespace_percent = NULL;
843 warn_usedspace_percent = NULL;
844 crit_usedspace_percent = NULL;
845 warn_usedinodes_percent = NULL;
846 crit_usedinodes_percent = NULL;
847 warn_freeinodes_percent = NULL; 803 warn_freeinodes_percent = NULL;
848 crit_freeinodes_percent = NULL; 804 crit_freeinodes_percent = NULL;
849 805
850 path_selected = false; 806 path_selected = false;
851 group = NULL; 807 group = NULL;
852 break; 808 } break;
853 case 'V': /* version */ 809 case 'V': /* version */
854 print_revision(progname, NP_VERSION); 810 print_revision(progname, NP_VERSION);
855 exit(STATE_UNKNOWN); 811 exit(STATE_UNKNOWN);
@@ -858,50 +814,152 @@ int process_arguments(int argc, char **argv) {
858 exit(STATE_UNKNOWN); 814 exit(STATE_UNKNOWN);
859 case '?': /* help */ 815 case '?': /* help */
860 usage(_("Unknown argument")); 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 }
861 } 829 }
862 } 830 }
863 831
864 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ 832 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
865 c = optind; 833 int index = optind;
866 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) 834
867 warn_usedspace_percent = argv[c++]; 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_SUCCES) {
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;
868 848
869 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) 849 warn_freespace_percent = mp_range_to_string(tmp_range);
870 crit_usedspace_percent = argv[c++];
871 850
872 if (argc > c) { 851 if (verbose > 0) {
873 se = np_add_parameter(&path_select_list, strdup(argv[c++])); 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_SUCCES) {
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++]));
874 path_selected = true; 883 path_selected = true;
875 set_all_thresholds(se); 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);
876 } 887 }
877 888
878 if (units == NULL) { 889 // If a list of paths has not been explicitly selected, find entire
879 units = strdup("MiB"); 890 // mount list and create list of paths
880 mult = (uintmax_t)1024 * 1024; 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 }
881 } 908 }
882 909
883 return true; 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;
884} 926}
885 927
886void set_all_thresholds(struct parameter_list *path) { 928void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
887 if (path->freespace_units != NULL) 929 char *crit_freespace_units, char *warn_freespace_percent,
888 free(path->freespace_units); 930 char *crit_freespace_percent, char *warn_freeinodes_percent,
889 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); 931 char *crit_freeinodes_percent) {
890 if (path->freespace_percent != NULL) 932 mp_range_parsed tmp;
891 free(path->freespace_percent); 933
892 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); 934 if (warn_freespace_units) {
893 if (path->usedspace_units != NULL) 935 tmp = mp_parse_range_string(warn_freespace_units);
894 free(path->usedspace_units); 936 path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range);
895 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); 937 }
896 if (path->usedspace_percent != NULL) 938
897 free(path->usedspace_percent); 939 if (crit_freespace_units) {
898 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); 940 tmp = mp_parse_range_string(crit_freespace_units);
899 if (path->usedinodes_percent != NULL) 941 path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range);
900 free(path->usedinodes_percent); 942 }
901 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); 943
902 if (path->freeinodes_percent != NULL) 944 if (warn_freespace_percent) {
903 free(path->freeinodes_percent); 945 tmp = mp_parse_range_string(warn_freespace_percent);
904 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); 946 path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range);
947 }
948
949 if (crit_freespace_percent) {
950 tmp = mp_parse_range_string(crit_freespace_percent);
951 path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range);
952 }
953
954 if (warn_freeinodes_percent) {
955 tmp = mp_parse_range_string(warn_freeinodes_percent);
956 path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range);
957 }
958
959 if (crit_freeinodes_percent) {
960 tmp = mp_parse_range_string(crit_freeinodes_percent);
961 path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range);
962 }
905} 963}
906 964
907void print_help(void) { 965void print_help(void) {
@@ -911,7 +969,8 @@ void print_help(void) {
911 printf(COPYRIGHT, copyright, email); 969 printf(COPYRIGHT, copyright, email);
912 970
913 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system")); 971 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
914 printf("%s\n", _("and generates an alert if free space is less than one of the threshold values")); 972 printf("%s\n",
973 _("and generates an alert if free space is less than one of the threshold values"));
915 974
916 printf("\n\n"); 975 printf("\n\n");
917 976
@@ -933,7 +992,8 @@ void print_help(void) {
933 printf(" %s\n", "-K, --icritical=PERCENT%"); 992 printf(" %s\n", "-K, --icritical=PERCENT%");
934 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free")); 993 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
935 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION"); 994 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION");
936 printf(" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)")); 995 printf(" %s\n",
996 _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
937 printf(" %s\n", "-x, --exclude_device=PATH <STRING>"); 997 printf(" %s\n", "-x, --exclude_device=PATH <STRING>");
938 printf(" %s\n", _("Ignore device (only works if -p unspecified)")); 998 printf(" %s\n", _("Ignore device (only works if -p unspecified)"));
939 printf(" %s\n", "-C, --clear"); 999 printf(" %s\n", "-C, --clear");
@@ -947,167 +1007,298 @@ void print_help(void) {
947 printf(" %s\n", "-P, --iperfdata"); 1007 printf(" %s\n", "-P, --iperfdata");
948 printf(" %s\n", _("Display inode usage in perfdata")); 1008 printf(" %s\n", _("Display inode usage in perfdata"));
949 printf(" %s\n", "-g, --group=NAME"); 1009 printf(" %s\n", "-g, --group=NAME");
950 printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together")); 1010 printf(" %s\n",
951 printf(" %s\n", "-k, --kilobytes"); 1011 _("Group paths. Thresholds apply to (free-)space of all partitions together"));
952 printf(" %s\n", _("Same as '--units kB'"));
953 printf(" %s\n", "-l, --local"); 1012 printf(" %s\n", "-l, --local");
954 printf(" %s\n", _("Only check local filesystems")); 1013 printf(" %s\n", _("Only check local filesystems"));
955 printf(" %s\n", "-L, --stat-remote-fs"); 1014 printf(" %s\n", "-L, --stat-remote-fs");
956 printf(" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems")); 1015 printf(
1016 " %s\n",
1017 _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
957 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); 1018 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
958 printf(" %s\n", "-M, --mountpoint"); 1019 printf(" %s\n", "-M, --mountpoint");
959 printf(" %s\n", _("Display the (block) device instead of the mount point")); 1020 printf(" %s\n", _("Display the (block) device instead of the mount point"));
960 printf(" %s\n", "-m, --megabytes");
961 printf(" %s\n", _("Same as '--units MB'"));
962 printf(" %s\n", "-A, --all"); 1021 printf(" %s\n", "-A, --all");
963 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); 1022 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
964 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); 1023 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
965 printf(" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)")); 1024 printf(" %s\n",
1025 _("Case insensitive regular expression for path/partition (may be repeated)"));
966 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION"); 1026 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
967 printf(" %s\n", _("Regular expression for path or partition (may be repeated)")); 1027 printf(" %s\n", _("Regular expression for path or partition (may be repeated)"));
968 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION"); 1028 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
969 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)")); 1029 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) "
1030 "(may be repeated)"));
970 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION"); 1031 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
971 printf(" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)")); 1032 printf(" %s\n",
1033 _("Regular expression to ignore selected path or partition (may be repeated)"));
972 printf(" %s\n", "-n, --ignore-missing"); 1034 printf(" %s\n", "-n, --ignore-missing");
973 printf(" %s\n", _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible.")); 1035 printf(" %s\n",
1036 _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
974 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); 1037 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
975 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1038 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
976 printf(" %s\n", "-u, --units=STRING"); 1039 printf(" %s\n", "-u, --units=STRING");
977 printf(" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); 1040 printf(" %s\n", _("Select the unit used for the absolute value thresholds"));
1041 printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", "
1042 "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1043 printf(" %s\n", "-k, --kilobytes");
1044 printf(" %s\n", _("Same as '--units kB'"));
1045 printf(" %s\n", "--display-unit");
1046 printf(" %s\n", _("Select the unit used for in the output"));
1047 printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", "
1048 "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1049 printf(" %s\n", "-m, --megabytes");
1050 printf(" %s\n", _("Same as '--units MB'"));
978 printf(UT_VERBOSE); 1051 printf(UT_VERBOSE);
979 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); 1052 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX");
980 printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); 1053 printf(" %s\n",
1054 _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
981 printf(" %s\n", "-N, --include-type=TYPE_REGEX"); 1055 printf(" %s\n", "-N, --include-type=TYPE_REGEX");
982 printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); 1056 printf(
1057 " %s\n",
1058 _("Check only filesystems where the type matches this given regex(7) (may be repeated)"));
1059 printf(UT_OUTPUT_FORMAT);
983 1060
984 printf("\n"); 1061 printf("\n");
985 printf("%s\n", _("General usage hints:")); 1062 printf("%s\n", _("General usage hints:"));
986 printf(" %s\n", _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as")); 1063 printf(
1064 " %s\n",
1065 _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
987 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\".")); 1066 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
988 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} {thresholds b} ...\"")); 1067 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} "
1068 "{thresholds b} ...\""));
989 1069
990 printf("\n"); 1070 printf("\n");
991 printf("%s\n", _("Examples:")); 1071 printf("%s\n", _("Examples:"));
992 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /"); 1072 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
993 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB")); 1073 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
994 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'"); 1074 printf(" %s\n",
995 printf(" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex")); 1075 "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
996 printf(" %s\n\n", _("are grouped which means the freespace thresholds are applied to all disks together")); 1076 printf(
1077 " %s\n",
1078 _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
1079 printf(" %s\n\n",
1080 _("are grouped which means the freespace thresholds are applied to all disks together"));
997 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar"); 1081 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
998 printf(" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M")); 1082 printf(" %s\n",
1083 _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
999 1084
1000 printf(UT_SUPPORT); 1085 printf(UT_SUPPORT);
1001} 1086}
1002 1087
1003void print_usage(void) { 1088void print_usage(void) {
1004 printf("%s\n", _("Usage:")); 1089 printf("%s\n", _("Usage:"));
1005 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c absolute_limit|-c percentage_limit%% | -K " 1090 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c "
1091 "absolute_limit|-c percentage_limit%% | -K "
1006 "inode_percentage_limit } {-p path | -x device}\n", 1092 "inode_percentage_limit } {-p path | -x device}\n",
1007 progname); 1093 progname);
1008 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); 1094 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
1009 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); 1095 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n");
1010} 1096}
1011 1097
1012bool stat_path(struct parameter_list *p) { 1098bool stat_path(parameter_list_elem *parameters, bool ignore_missing) {
1013 /* Stat entry to check that dir exists and is accessible */ 1099 /* Stat entry to check that dir exists and is accessible */
1014 if (verbose >= 3) 1100 if (verbose >= 3) {
1015 printf("calling stat on %s\n", p->name); 1101 printf("calling stat on %s\n", parameters->name);
1016 if (stat(p->name, &stat_buf[0])) { 1102 }
1017 if (verbose >= 3) 1103
1018 printf("stat failed on %s\n", p->name); 1104 struct stat stat_buf = {0};
1019 if (ignore_missing == true) { 1105 if (stat(parameters->name, &stat_buf)) {
1106 if (verbose >= 3) {
1107 printf("stat failed on %s\n", parameters->name);
1108 }
1109 if (ignore_missing) {
1020 return false; 1110 return false;
1021 } 1111 }
1022 printf("DISK %s - ", _("CRITICAL")); 1112 printf("DISK %s - ", _("CRITICAL"));
1023 die(STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); 1113 die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"),
1114 strerror(errno));
1024 } 1115 }
1116
1025 return true; 1117 return true;
1026} 1118}
1027 1119
1028void get_stats(struct parameter_list *p, struct fs_usage *fsp) { 1120static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp,
1029 struct parameter_list *p_list; 1121 bool freespace_ignore_reserved) {
1030 struct fs_usage tmpfsp; 1122 uintmax_t available = fsp.fsu_bavail;
1031 int first = 1; 1123 uintmax_t available_to_root = fsp.fsu_bfree;
1124 uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree;
1125 uintmax_t total;
1032 1126
1033 if (p->group == NULL) {
1034 get_path_stats(p, fsp);
1035 } else {
1036 /* find all group members */
1037 for (p_list = path_select_list; p_list; p_list = p_list->name_next) {
1038#ifdef __CYGWIN__
1039 if (strncmp(p_list->name, "/cygdrive/", 10) != 0)
1040 continue;
1041#endif
1042 if (p_list->group && !(strcmp(p_list->group, p->group))) {
1043 if (!stat_path(p_list))
1044 continue;
1045 get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
1046 get_path_stats(p_list, &tmpfsp);
1047 if (verbose >= 3)
1048 printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n",
1049 p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units,
1050 p_list->dfree_units, p_list->dtotal_units, mult);
1051
1052 /* prevent counting the first FS of a group twice since its parameter_list entry
1053 * is used to carry the information of all file systems of the entire group */
1054 if (!first) {
1055 p->total += p_list->total;
1056 p->available += p_list->available;
1057 p->available_to_root += p_list->available_to_root;
1058 p->used += p_list->used;
1059
1060 p->dused_units += p_list->dused_units;
1061 p->dfree_units += p_list->dfree_units;
1062 p->dtotal_units += p_list->dtotal_units;
1063 p->inodes_total += p_list->inodes_total;
1064 p->inodes_free += p_list->inodes_free;
1065 p->inodes_free_to_root += p_list->inodes_free_to_root;
1066 p->inodes_used += p_list->inodes_used;
1067 }
1068 first = 0;
1069 }
1070 if (verbose >= 3)
1071 printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", p->group,
1072 p->dused_units, p->dfree_units, p->dtotal_units, tmpfsp.fsu_blocksize, mult);
1073 }
1074 /* modify devname and mountdir for output */
1075 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
1076 }
1077 /* finally calculate percentages for either plain FS or summed up group */
1078 p->dused_pct = calculate_percent(p->used, p->used + p->available); /* used + available can never be > uintmax */
1079 p->dfree_pct = 100.0 - p->dused_pct;
1080 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1081 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1082}
1083
1084void get_path_stats(struct parameter_list *p, struct fs_usage *fsp) {
1085 p->available = fsp->fsu_bavail;
1086 p->available_to_root = fsp->fsu_bfree;
1087 p->used = fsp->fsu_blocks - fsp->fsu_bfree;
1088 if (freespace_ignore_reserved) { 1127 if (freespace_ignore_reserved) {
1089 /* option activated : we subtract the root-reserved space from the total */ 1128 /* option activated : we subtract the root-reserved space from the total */
1090 p->total = fsp->fsu_blocks - p->available_to_root + p->available; 1129 total = fsp.fsu_blocks - available_to_root + available;
1091 } else { 1130 } else {
1092 /* default behaviour : take all the blocks into account */ 1131 /* default behaviour : take all the blocks into account */
1093 p->total = fsp->fsu_blocks; 1132 total = fsp.fsu_blocks;
1094 } 1133 }
1095 1134
1096 p->dused_units = p->used * fsp->fsu_blocksize / mult; 1135 parameters.used_bytes = used * fsp.fsu_blocksize;
1097 p->dfree_units = p->available * fsp->fsu_blocksize / mult; 1136 parameters.free_bytes = available * fsp.fsu_blocksize;
1098 p->dtotal_units = p->total * fsp->fsu_blocksize / mult; 1137 parameters.total_bytes = total * fsp.fsu_blocksize;
1138
1099 /* Free file nodes. Not sure the workaround is required, but in case...*/ 1139 /* Free file nodes. Not sure the workaround is required, but in case...*/
1100 p->inodes_free = fsp->fsu_ffree; 1140 parameters.inodes_free = fsp.fsu_ffree;
1101 p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ 1141 parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */
1102 p->inodes_used = fsp->fsu_files - fsp->fsu_ffree; 1142 parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree;
1143
1103 if (freespace_ignore_reserved) { 1144 if (freespace_ignore_reserved) {
1104 /* option activated : we subtract the root-reserved inodes from the total */ 1145 /* option activated : we subtract the root-reserved inodes from the total */
1105 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ 1146 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */
1106 /* for others, fsp->fsu_ffree == fsp->fsu_favail */ 1147 /* for others, fsp->fsu_ffree == fsp->fsu_favail */
1107 p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free; 1148 parameters.inodes_total =
1149 fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free;
1108 } else { 1150 } else {
1109 /* default behaviour : take all the inodes into account */ 1151 /* default behaviour : take all the inodes into account */
1110 p->inodes_total = fsp->fsu_files; 1152 parameters.inodes_total = fsp.fsu_files;
1111 } 1153 }
1112 np_add_name(&seen, p->best_match->me_mountdir); 1154
1155 return parameters;
1156}
1157
1158mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata,
1159 byte_unit unit) {
1160 mp_subcheck result = mp_subcheck_init();
1161 result = mp_set_subcheck_default_state(result, STATE_UNKNOWN);
1162 xasprintf(&result.output, "%s", measurement_unit.name);
1163
1164 if (!measurement_unit.is_group && measurement_unit.filesystem_type) {
1165 xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type);
1166 }
1167
1168 /* Threshold comparisons */
1169
1170 // ===============================
1171 // Free space absolute values test
1172 mp_subcheck freespace_bytes_sc = mp_subcheck_init();
1173 freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK);
1174
1175 if (unit != Humanized) {
1176 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)",
1177 (uintmax_t)(measurement_unit.free_bytes / unit), get_unit_string(unit),
1178 (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit));
1179 } else {
1180 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)",
1181 humanize_byte_value(measurement_unit.free_bytes, false),
1182 humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false));
1183 }
1184
1185 mp_perfdata used_space = perfdata_init();
1186 used_space.label = measurement_unit.name;
1187 used_space.value = mp_create_pd_value(measurement_unit.free_bytes);
1188 used_space = mp_set_pd_max_value(used_space, mp_create_pd_value(measurement_unit.total_bytes));
1189 used_space = mp_set_pd_min_value(used_space, mp_create_pd_value(0));
1190 used_space.uom = "B";
1191 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1192 freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(used_space));
1193
1194 // special case for absolute space thresholds here:
1195 // if absolute values are not set, compute the thresholds from percentage thresholds
1196 mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds;
1197 if (!temp_thlds.critical_is_set &&
1198 measurement_unit.freespace_percent_thresholds.critical_is_set) {
1199 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical;
1200
1201 if (!tmp_range.end_infinity) {
1202 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 *
1203 measurement_unit.total_bytes);
1204 }
1205
1206 if (!tmp_range.start_infinity) {
1207 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 *
1208 measurement_unit.total_bytes);
1209 }
1210 measurement_unit.freespace_bytes_thresholds =
1211 mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range);
1212 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1213 }
1214
1215 if (!temp_thlds.warning_is_set &&
1216 measurement_unit.freespace_percent_thresholds.warning_is_set) {
1217 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning;
1218 if (!tmp_range.end_infinity) {
1219 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 *
1220 measurement_unit.total_bytes);
1221 }
1222 if (!tmp_range.start_infinity) {
1223 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 *
1224 measurement_unit.total_bytes);
1225 }
1226 measurement_unit.freespace_bytes_thresholds =
1227 mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range);
1228 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1229 }
1230
1231 mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space);
1232 mp_add_subcheck_to_subcheck(&result, freespace_bytes_sc);
1233
1234 // ==========================
1235 // Free space percentage test
1236 mp_subcheck freespace_percent_sc = mp_subcheck_init();
1237 freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK);
1238
1239 double free_percentage =
1240 calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes);
1241 xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage);
1242
1243 // Using perfdata here just to get to the test result
1244 mp_perfdata free_space_percent_pd = perfdata_init();
1245 free_space_percent_pd.value = mp_create_pd_value(free_percentage);
1246 free_space_percent_pd =
1247 mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds);
1248
1249 freespace_percent_sc =
1250 mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd));
1251 mp_add_subcheck_to_subcheck(&result, freespace_percent_sc);
1252
1253 // ================
1254 // Free inodes test
1255 // Only ever useful if the number of inodes is static (e.g. ext4),
1256 // not when it is dynamic (e.g btrfs)
1257 // Assumption: if the total number of inodes == 0, we have such a case and just skip the test
1258 if (measurement_unit.inodes_total > 0) {
1259 mp_subcheck freeindodes_percent_sc = mp_subcheck_init();
1260 freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK);
1261
1262 double free_inode_percentage =
1263 calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total);
1264
1265 if (verbose > 0) {
1266 printf("free inode percentage computed: %g\n", free_inode_percentage);
1267 }
1268
1269 xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)",
1270 free_inode_percentage, measurement_unit.inodes_free,
1271 measurement_unit.inodes_total);
1272
1273 mp_perfdata inodes_pd = perfdata_init();
1274 xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name);
1275 inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used);
1276 inodes_pd =
1277 mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total));
1278 inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0));
1279
1280 mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds;
1281
1282 if (absolut_inode_thresholds.critical_is_set) {
1283 absolut_inode_thresholds.critical =
1284 mp_range_multiply(absolut_inode_thresholds.critical,
1285 mp_create_pd_value(measurement_unit.inodes_total / 100));
1286 }
1287 if (absolut_inode_thresholds.warning_is_set) {
1288 absolut_inode_thresholds.warning =
1289 mp_range_multiply(absolut_inode_thresholds.warning,
1290 mp_create_pd_value(measurement_unit.inodes_total / 100));
1291 }
1292
1293 inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds);
1294
1295 freeindodes_percent_sc =
1296 mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd));
1297 if (display_inodes_perfdata) {
1298 mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd);
1299 }
1300 mp_add_subcheck_to_subcheck(&result, freeindodes_percent_sc);
1301 }
1302
1303 return result;
1113} 1304}
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 95f33083..56f91dae 100644
--- a/plugins/check_dns.c
+++ b/plugins/check_dns.c
@@ -48,7 +48,8 @@ typedef struct {
48} check_dns_config_wrapper; 48} check_dns_config_wrapper;
49static check_dns_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); 49static check_dns_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50static check_dns_config_wrapper validate_arguments(check_dns_config_wrapper /*config_wrapper*/); 50static check_dns_config_wrapper validate_arguments(check_dns_config_wrapper /*config_wrapper*/);
51static mp_state_enum error_scan(char * /*input_buffer*/, bool * /*is_nxdomain*/, const char /*dns_server*/[ADDRESS_LENGTH]); 51static mp_state_enum error_scan(char * /*input_buffer*/, bool * /*is_nxdomain*/,
52 const char /*dns_server*/[ADDRESS_LENGTH]);
52static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/); 53static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/);
53static unsigned long ip2long(const char * /*src*/); 54static unsigned long ip2long(const char * /*src*/);
54static void print_help(void); 55static void print_help(void);
@@ -127,7 +128,8 @@ int main(int argc, char **argv) {
127 puts(chld_out.line[i]); 128 puts(chld_out.line[i]);
128 } 129 }
129 130
130 if (strcasestr(chld_out.line[i], ".in-addr.arpa") || strcasestr(chld_out.line[i], ".ip6.arpa")) { 131 if (strcasestr(chld_out.line[i], ".in-addr.arpa") ||
132 strcasestr(chld_out.line[i], ".ip6.arpa")) {
131 if ((strstr(chld_out.line[i], "canonical name = ") != NULL)) { 133 if ((strstr(chld_out.line[i], "canonical name = ") != NULL)) {
132 continue; 134 continue;
133 } 135 }
@@ -145,7 +147,8 @@ int main(int argc, char **argv) {
145 if (strstr(chld_out.line[i], "Server:") && strlen(config.dns_server) > 0) { 147 if (strstr(chld_out.line[i], "Server:") && strlen(config.dns_server) > 0) {
146 char *temp_buffer = strchr(chld_out.line[i], ':'); 148 char *temp_buffer = strchr(chld_out.line[i], ':');
147 if (temp_buffer == NULL) { 149 if (temp_buffer == NULL) {
148 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Server line\n"), NSLOOKUP_COMMAND); 150 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Server line\n"),
151 NSLOOKUP_COMMAND);
149 } 152 }
150 153
151 temp_buffer++; 154 temp_buffer++;
@@ -157,21 +160,25 @@ int main(int argc, char **argv) {
157 160
158 strip(temp_buffer); 161 strip(temp_buffer);
159 if (strlen(temp_buffer) == 0) { 162 if (strlen(temp_buffer) == 0) {
160 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"), NSLOOKUP_COMMAND); 163 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"),
164 NSLOOKUP_COMMAND);
161 } 165 }
162 166
163 if (strcmp(temp_buffer, config.dns_server) != 0) { 167 if (strcmp(temp_buffer, config.dns_server) != 0) {
164 die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), config.dns_server); 168 die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"),
169 config.dns_server);
165 } 170 }
166 } 171 }
167 172
168 /* the server is responding, we just got the host name... */ 173 /* the server is responding, we just got the host name... */
169 if (strstr(chld_out.line[i], "Name:")) { 174 if (strstr(chld_out.line[i], "Name:")) {
170 parse_address = true; 175 parse_address = true;
171 } else if (parse_address && (strstr(chld_out.line[i], "Address:") || strstr(chld_out.line[i], "Addresses:"))) { 176 } else if (parse_address && (strstr(chld_out.line[i], "Address:") ||
177 strstr(chld_out.line[i], "Addresses:"))) {
172 char *temp_buffer = strchr(chld_out.line[i], ':'); 178 char *temp_buffer = strchr(chld_out.line[i], ':');
173 if (temp_buffer == NULL) { 179 if (temp_buffer == NULL) {
174 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Address line\n"), NSLOOKUP_COMMAND); 180 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Address line\n"),
181 NSLOOKUP_COMMAND);
175 } 182 }
176 183
177 temp_buffer++; 184 temp_buffer++;
@@ -183,7 +190,8 @@ int main(int argc, char **argv) {
183 190
184 strip(temp_buffer); 191 strip(temp_buffer);
185 if (strlen(temp_buffer) == 0) { 192 if (strlen(temp_buffer) == 0) {
186 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"), NSLOOKUP_COMMAND); 193 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"),
194 NSLOOKUP_COMMAND);
187 } 195 }
188 196
189 addresses[n_addresses++] = strdup(temp_buffer); 197 addresses[n_addresses++] = strdup(temp_buffer);
@@ -209,7 +217,8 @@ int main(int argc, char **argv) {
209 } 217 }
210 218
211 if (error_scan(chld_err.line[i], &is_nxdomain, config.dns_server) != STATE_OK) { 219 if (error_scan(chld_err.line[i], &is_nxdomain, config.dns_server) != STATE_OK) {
212 result = max_state(result, error_scan(chld_err.line[i], &is_nxdomain, config.dns_server)); 220 result =
221 max_state(result, error_scan(chld_err.line[i], &is_nxdomain, config.dns_server));
213 msg = strchr(input_buffer, ':'); 222 msg = strchr(input_buffer, ':');
214 if (msg) { 223 if (msg) {
215 msg++; 224 msg++;
@@ -242,7 +251,8 @@ int main(int argc, char **argv) {
242 } 251 }
243 *adrp = 0; 252 *adrp = 0;
244 } else { 253 } else {
245 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), NSLOOKUP_COMMAND); 254 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"),
255 NSLOOKUP_COMMAND);
246 } 256 }
247 257
248 /* compare to expected address */ 258 /* compare to expected address */
@@ -255,7 +265,8 @@ int main(int argc, char **argv) {
255 for (size_t i = 0; i < config.expected_address_cnt; i++) { 265 for (size_t i = 0; i < config.expected_address_cnt; i++) {
256 /* check if we get a match on 'raw' ip or cidr */ 266 /* check if we get a match on 'raw' ip or cidr */
257 for (size_t j = 0; j < n_addresses; j++) { 267 for (size_t j = 0; j < n_addresses; j++) {
258 if (strcmp(addresses[j], config.expected_address[i]) == 0 || ip_match_cidr(addresses[j], config.expected_address[i])) { 268 if (strcmp(addresses[j], config.expected_address[i]) == 0 ||
269 ip_match_cidr(addresses[j], config.expected_address[i])) {
259 result = STATE_OK; 270 result = STATE_OK;
260 addr_match &= ~(1 << j); 271 addr_match &= ~(1 << j);
261 expect_match &= ~(1 << i); 272 expect_match &= ~(1 << i);
@@ -279,7 +290,8 @@ int main(int argc, char **argv) {
279 if (config.expect_nxdomain) { 290 if (config.expect_nxdomain) {
280 if (!is_nxdomain) { 291 if (!is_nxdomain) {
281 result = STATE_CRITICAL; 292 result = STATE_CRITICAL;
282 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), config.query_address, address); 293 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), config.query_address,
294 address);
283 } else { 295 } else {
284 if (address != NULL) { 296 if (address != NULL) {
285 free(address); 297 free(address);
@@ -291,7 +303,8 @@ int main(int argc, char **argv) {
291 /* check if authoritative */ 303 /* check if authoritative */
292 if (result == STATE_OK && config.expect_authority && non_authoritative) { 304 if (result == STATE_OK && config.expect_authority && non_authoritative) {
293 result = STATE_CRITICAL; 305 result = STATE_CRITICAL;
294 xasprintf(&msg, _("server %s is not authoritative for %s"), config.dns_server, config.query_address); 306 xasprintf(&msg, _("server %s is not authoritative for %s"), config.dns_server,
307 config.query_address);
295 } 308 }
296 309
297 long microsec = deltime(tv); 310 long microsec = deltime(tv);
@@ -306,24 +319,36 @@ int main(int argc, char **argv) {
306 } else if (result == STATE_CRITICAL) { 319 } else if (result == STATE_CRITICAL) {
307 printf("DNS %s: ", _("CRITICAL")); 320 printf("DNS %s: ", _("CRITICAL"));
308 } 321 }
309 printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), elapsed_time); 322 printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time),
323 elapsed_time);
310 printf(_(". %s returns %s"), config.query_address, address); 324 printf(_(". %s returns %s"), config.query_address, address);
311 if ((config.time_thresholds->warning != NULL) && (config.time_thresholds->critical != NULL)) { 325 if ((config.time_thresholds->warning != NULL) &&
312 printf("|%s\n", fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end, true, 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,
313 config.time_thresholds->critical->end, true, 0, false, 0)); 333 config.time_thresholds->critical->end, true, 0, false, 0));
314 } else if ((config.time_thresholds->warning == NULL) && (config.time_thresholds->critical != NULL)) { 334 } else if ((config.time_thresholds->warning != NULL) &&
315 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true, config.time_thresholds->critical->end, true, 0, false, 0)); 335 (config.time_thresholds->critical == NULL)) {
316 } else if ((config.time_thresholds->warning != NULL) && (config.time_thresholds->critical == NULL)) { 336 printf("|%s\n",
317 printf("|%s\n", fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end, false, 0, true, 0, false, 0)); 337 fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end,
338 false, 0, true, 0, false, 0));
318 } else { 339 } else {
319 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0)); 340 printf("|%s\n",
341 fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0));
320 } 342 }
321 } else if (result == STATE_WARNING) { 343 } else if (result == STATE_WARNING) {
322 printf(_("DNS WARNING - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); 344 printf(_("DNS WARNING - %s\n"),
345 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
323 } else if (result == STATE_CRITICAL) { 346 } else if (result == STATE_CRITICAL) {
324 printf(_("DNS CRITICAL - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); 347 printf(_("DNS CRITICAL - %s\n"),
348 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
325 } else { 349 } else {
326 printf(_("DNS UNKNOWN - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); 350 printf(_("DNS UNKNOWN - %s\n"),
351 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
327 } 352 }
328 353
329 exit(result); 354 exit(result);
@@ -342,29 +367,34 @@ bool ip_match_cidr(const char *addr, const char *cidr_ro) {
342 mask = atoi(mask_c); 367 mask = atoi(mask_c);
343 368
344 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ 369 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */
345 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);
346} 372}
347 373
348unsigned long ip2long(const char *src) { 374unsigned long ip2long(const char *src) {
349 unsigned long ip[4]; 375 unsigned long ip[4];
350 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ 376 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */
351 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && 377 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 &&
352 ip[3] < 256) 378 ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && ip[3] < 256)
353 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3] 379 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]
354 : 0; 380 : 0;
355} 381}
356 382
357mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain, const char dns_server[ADDRESS_LENGTH]) { 383mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain,
384 const char dns_server[ADDRESS_LENGTH]) {
358 385
359 const int nxdomain = strstr(input_buffer, "Non-existent") || strstr(input_buffer, "** server can't find") || 386 const int nxdomain = strstr(input_buffer, "Non-existent") ||
387 strstr(input_buffer, "** server can't find") ||
360 strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN"); 388 strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN");
361 if (nxdomain) { 389 if (nxdomain) {
362 *is_nxdomain = true; 390 *is_nxdomain = true;
363 } 391 }
364 392
365 /* the DNS lookup timed out */ 393 /* the DNS lookup timed out */
366 if (strstr(input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) || 394 if (strstr(input_buffer,
367 strstr(input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) || 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")) ||
368 strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing."))) { 398 strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing."))) {
369 return STATE_OK; 399 return STATE_OK;
370 } 400 }
@@ -382,8 +412,9 @@ mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain, const char dns_s
382 } 412 }
383 413
384 /* Connection was refused */ 414 /* Connection was refused */
385 else if (strstr(input_buffer, "Connection refused") || strstr(input_buffer, "Couldn't find server") || 415 else if (strstr(input_buffer, "Connection refused") ||
386 strstr(input_buffer, "Refused") || (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) { 416 strstr(input_buffer, "Couldn't find server") || strstr(input_buffer, "Refused") ||
417 (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) {
387 die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server); 418 die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server);
388 } 419 }
389 420
@@ -504,20 +535,24 @@ check_dns_config_wrapper process_arguments(int argc, char **argv) {
504 if (strchr(optarg, ',') != NULL) { 535 if (strchr(optarg, ',') != NULL) {
505 char *comma = strchr(optarg, ','); 536 char *comma = strchr(optarg, ',');
506 while (comma != NULL) { 537 while (comma != NULL) {
507 result.config.expected_address = 538 result.config.expected_address = (char **)realloc(
508 (char **)realloc(result.config.expected_address, (result.config.expected_address_cnt + 1) * sizeof(char **)); 539 result.config.expected_address,
509 result.config.expected_address[result.config.expected_address_cnt] = strndup(optarg, comma - optarg); 540 (result.config.expected_address_cnt + 1) * sizeof(char **));
541 result.config.expected_address[result.config.expected_address_cnt] =
542 strndup(optarg, comma - optarg);
510 result.config.expected_address_cnt++; 543 result.config.expected_address_cnt++;
511 optarg = comma + 1; 544 optarg = comma + 1;
512 comma = strchr(optarg, ','); 545 comma = strchr(optarg, ',');
513 } 546 }
514 result.config.expected_address = 547 result.config.expected_address =
515 (char **)realloc(result.config.expected_address, (result.config.expected_address_cnt + 1) * sizeof(char **)); 548 (char **)realloc(result.config.expected_address,
549 (result.config.expected_address_cnt + 1) * sizeof(char **));
516 result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg); 550 result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg);
517 result.config.expected_address_cnt++; 551 result.config.expected_address_cnt++;
518 } else { 552 } else {
519 result.config.expected_address = 553 result.config.expected_address =
520 (char **)realloc(result.config.expected_address, (result.config.expected_address_cnt + 1) * sizeof(char **)); 554 (char **)realloc(result.config.expected_address,
555 (result.config.expected_address_cnt + 1) * sizeof(char **));
521 result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg); 556 result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg);
522 result.config.expected_address_cnt++; 557 result.config.expected_address_cnt++;
523 } 558 }
@@ -586,9 +621,11 @@ void print_help(void) {
586 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 621 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
587 printf(COPYRIGHT, copyright, email); 622 printf(COPYRIGHT, copyright, email);
588 623
589 printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given host/domain query.")); 624 printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given "
625 "host/domain query."));
590 printf("%s\n", _("An optional DNS server to use may be specified.")); 626 printf("%s\n", _("An optional DNS server to use may be specified."));
591 printf("%s\n", _("If no DNS server is specified, the default server(s) specified in /etc/resolv.conf will be used.")); 627 printf("%s\n", _("If no DNS server is specified, the default server(s) specified in "
628 "/etc/resolv.conf will be used."));
592 629
593 printf("\n\n"); 630 printf("\n\n");
594 631
@@ -602,11 +639,14 @@ void print_help(void) {
602 printf(" -s, --server=HOST\n"); 639 printf(" -s, --server=HOST\n");
603 printf(" %s\n", _("Optional DNS server you want to use for the lookup")); 640 printf(" %s\n", _("Optional DNS server you want to use for the lookup"));
604 printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n"); 641 printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n");
605 printf(" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end")); 642 printf(" %s\n",
606 printf(" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any")); 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"));
607 printf(" %s\n", _("value matches).")); 646 printf(" %s\n", _("value matches)."));
608 printf(" -n, --expect-nxdomain\n"); 647 printf(" -n, --expect-nxdomain\n");
609 printf(" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)")); 648 printf(" %s\n",
649 _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)"));
610 printf(" %s\n", _("Cannot be used together with -a")); 650 printf(" %s\n", _("Cannot be used together with -a"));
611 printf(" -A, --expect-authority\n"); 651 printf(" -A, --expect-authority\n");
612 printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup")); 652 printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup"));
@@ -615,7 +655,8 @@ void print_help(void) {
615 printf(" -c, --critical=seconds\n"); 655 printf(" -c, --critical=seconds\n");
616 printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off")); 656 printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off"));
617 printf(" -L, --all\n"); 657 printf(" -L, --all\n");
618 printf(" %s\n", _("Return critical if the list of expected addresses does not match all addresses")); 658 printf(" %s\n",
659 _("Return critical if the list of expected addresses does not match all addresses"));
619 printf(" %s\n", _("returned. Default off")); 660 printf(" %s\n", _("returned. Default off"));
620 661
621 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 662 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
@@ -625,5 +666,7 @@ void print_help(void) {
625 666
626void print_usage(void) { 667void print_usage(void) {
627 printf("%s\n", _("Usage:")); 668 printf("%s\n", _("Usage:"));
628 printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname); 669 printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c "
670 "crit] [-L]\n",
671 progname);
629} 672}
diff --git a/plugins/check_dummy.c b/plugins/check_dummy.c
index 19f6c046..602d5868 100644
--- a/plugins/check_dummy.c
+++ b/plugins/check_dummy.c
@@ -45,18 +45,19 @@ int main(int argc, char **argv) {
45 bindtextdomain(PACKAGE, LOCALEDIR); 45 bindtextdomain(PACKAGE, LOCALEDIR);
46 textdomain(PACKAGE); 46 textdomain(PACKAGE);
47 47
48 if (argc < 2) 48 if (argc < 2) {
49 usage4(_("Could not parse arguments")); 49 usage4(_("Could not parse arguments"));
50 else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) { 50 } else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) {
51 print_revision(progname, NP_VERSION); 51 print_revision(progname, NP_VERSION);
52 exit(STATE_UNKNOWN); 52 exit(STATE_UNKNOWN);
53 } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { 53 } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
54 print_help(); 54 print_help();
55 exit(STATE_UNKNOWN); 55 exit(STATE_UNKNOWN);
56 } else if (!is_integer(argv[1])) 56 } else if (!is_integer(argv[1])) {
57 usage4(_("Arguments to check_dummy must be an integer")); 57 usage4(_("Arguments to check_dummy must be an integer"));
58 else 58 } else {
59 result = atoi(argv[1]); 59 result = atoi(argv[1]);
60 }
60 61
61 switch (result) { 62 switch (result) {
62 case STATE_OK: 63 case STATE_OK:
@@ -78,8 +79,9 @@ int main(int argc, char **argv) {
78 return STATE_UNKNOWN; 79 return STATE_UNKNOWN;
79 } 80 }
80 81
81 if (argc >= 3) 82 if (argc >= 3) {
82 printf(": %s", argv[2]); 83 printf(": %s", argv[2]);
84 }
83 85
84 printf("\n"); 86 printf("\n");
85 87
@@ -92,7 +94,8 @@ void print_help(void) {
92 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 94 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
93 printf(COPYRIGHT, copyright, email); 95 printf(COPYRIGHT, copyright, email);
94 96
95 printf("%s\n", _("This plugin will simply return the state corresponding to the numeric value")); 97 printf("%s\n",
98 _("This plugin will simply return the state corresponding to the numeric value"));
96 99
97 printf("%s\n", _("of the <state> argument with optional text")); 100 printf("%s\n", _("of the <state> argument with optional text"));
98 101
diff --git a/plugins/check_fping.c b/plugins/check_fping.c
index c1d03ece..6160c2cb 100644
--- a/plugins/check_fping.c
+++ b/plugins/check_fping.c
@@ -38,52 +38,30 @@ 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,
44 PACKET_SIZE = 56,
45 PL = 0, 45 PL = 0,
46 RTA = 1 46 RTA = 1
47}; 47};
48 48
49static int textscan(char *buf); 49static mp_state_enum textscan(char *buf, const char * /*server_name*/, bool /*crta_p*/,
50static int process_arguments(int /*argc*/, char ** /*argv*/); 50 double /*crta*/, bool /*wrta_p*/, double /*wrta*/, bool /*cpl_p*/,
51 int /*cpl*/, bool /*wpl_p*/, int /*wpl*/, bool /*alive_p*/);
52
53typedef struct {
54 int errorcode;
55 check_fping_config config;
56} check_fping_config_wrapper;
57static check_fping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
51static int get_threshold(char *arg, char *rv[2]); 58static int get_threshold(char *arg, char *rv[2]);
52static void print_help(void); 59static void print_help(void);
53void print_usage(void); 60void print_usage(void);
54 61
55static char *server_name = NULL;
56static char *sourceip = NULL;
57static char *sourceif = NULL;
58static int packet_size = PACKET_SIZE;
59static int packet_count = PACKET_COUNT;
60static int target_timeout = 0;
61static int packet_interval = 0;
62static bool verbose = false; 62static bool verbose = false;
63static bool dontfrag = false;
64static bool randomize_packet_data = false;
65static int cpl;
66static int wpl;
67static double crta;
68static double wrta;
69static bool cpl_p = false;
70static bool wpl_p = false;
71static bool alive_p = false;
72static bool crta_p = false;
73static bool wrta_p = false;
74 63
75int main(int argc, char **argv) { 64int main(int argc, char **argv) {
76 /* normally should be int result = STATE_UNKNOWN; */
77
78 int status = STATE_UNKNOWN;
79 int result = 0;
80 char *fping_prog = NULL;
81 char *server = NULL;
82 char *command_line = NULL;
83 char *input_buffer = NULL;
84 char *option_string = "";
85 input_buffer = malloc(MAX_INPUT_BUFFER);
86
87 setlocale(LC_ALL, ""); 65 setlocale(LC_ALL, "");
88 bindtextdomain(PACKAGE, LOCALEDIR); 66 bindtextdomain(PACKAGE, LOCALEDIR);
89 textdomain(PACKAGE); 67 textdomain(PACKAGE);
@@ -91,39 +69,81 @@ int main(int argc, char **argv) {
91 /* Parse extra opts if any */ 69 /* Parse extra opts if any */
92 argv = np_extra_opts(&argc, argv, progname); 70 argv = np_extra_opts(&argc, argv, progname);
93 71
94 if (process_arguments(argc, argv) == ERROR) 72 check_fping_config_wrapper tmp_config = process_arguments(argc, argv);
73 if (tmp_config.errorcode == ERROR) {
95 usage4(_("Could not parse arguments")); 74 usage4(_("Could not parse arguments"));
75 }
76
77 const check_fping_config config = tmp_config.config;
78
79 char *server = NULL;
80 server = strscpy(server, config.server_name);
96 81
97 server = strscpy(server, server_name); 82 char *option_string = "";
83 char *fping_prog = NULL;
84
85 /* First determine if the target is dualstack or ipv6 only. */
86 bool server_is_inet6_addr = is_inet6_addr(server);
87
88 /*
89 * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack
90 * -> we use ipv6
91 * If the user requested -4 OR the user made no assertion and the address is v4 ONLY
92 * -> we use ipv4
93 */
94 if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) {
95 xasprintf(&option_string, "%s-6 ", option_string);
96 } else {
97 xasprintf(&option_string, "%s-4 ", option_string);
98 }
99 fping_prog = strdup(PATH_TO_FPING);
98 100
99 /* compose the command */ 101 /* compose the command */
100 if (target_timeout) 102 if (config.target_timeout) {
101 xasprintf(&option_string, "%s-t %d ", option_string, target_timeout); 103 xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout);
102 if (packet_interval) 104 }
103 xasprintf(&option_string, "%s-p %d ", option_string, packet_interval); 105 if (config.packet_interval) {
104 if (sourceip) 106 xasprintf(&option_string, "%s-p %d ", option_string, config.packet_interval);
105 xasprintf(&option_string, "%s-S %s ", option_string, sourceip); 107 }
106 if (sourceif) 108 if (config.sourceip) {
107 xasprintf(&option_string, "%s-I %s ", option_string, sourceif); 109 xasprintf(&option_string, "%s-S %s ", option_string, config.sourceip);
108 if (dontfrag) 110 }
111 if (config.sourceif) {
112 xasprintf(&option_string, "%s-I %s ", option_string, config.sourceif);
113 }
114 if (config.dontfrag) {
109 xasprintf(&option_string, "%s-M ", option_string); 115 xasprintf(&option_string, "%s-M ", option_string);
110 if (randomize_packet_data) 116 }
117 if (config.randomize_packet_data) {
111 xasprintf(&option_string, "%s-R ", option_string); 118 xasprintf(&option_string, "%s-R ", option_string);
119 }
112 120
121 if (config.fwmark_set) {
122 xasprintf(&option_string, "%s--fwmark %u ", option_string, config.fwmark);
123 }
113 124
114#ifdef PATH_TO_FPING6 125 if (config.icmp_timestamp) {
115 if (address_family != AF_INET && is_inet6_addr(server)) 126 xasprintf(&option_string, "%s--icmp-timestamp ", option_string);
116 fping_prog = strdup(PATH_TO_FPING6); 127 }
117 else
118 fping_prog = strdup(PATH_TO_FPING);
119#else
120 fping_prog = strdup(PATH_TO_FPING);
121#endif
122 128
123 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, packet_size, packet_count, server); 129 if (config.check_source) {
130 xasprintf(&option_string, "%s--check-source ", option_string);
131 }
132
133 char *command_line = NULL;
124 134
125 if (verbose) 135 if (config.icmp_timestamp) {
136 // no packet size settable for ICMP timestamp
137 xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count,
138 server);
139 } else {
140 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string,
141 config.packet_size, config.packet_count, server);
142 }
143
144 if (verbose) {
126 printf("%s\n", command_line); 145 printf("%s\n", command_line);
146 }
127 147
128 /* run the command */ 148 /* run the command */
129 child_process = spopen(command_line); 149 child_process = spopen(command_line);
@@ -137,23 +157,31 @@ int main(int argc, char **argv) {
137 printf(_("Could not open stderr for %s\n"), command_line); 157 printf(_("Could not open stderr for %s\n"), command_line);
138 } 158 }
139 159
160 char *input_buffer = malloc(MAX_INPUT_BUFFER);
161 mp_state_enum status = STATE_UNKNOWN;
140 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 162 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
141 if (verbose) 163 if (verbose) {
142 printf("%s", input_buffer); 164 printf("%s", input_buffer);
143 status = max_state(status, textscan(input_buffer)); 165 }
166 status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p,
167 config.crta, config.wrta_p, config.wrta, config.cpl_p,
168 config.cpl, config.wpl_p, config.wpl, config.alive_p));
144 } 169 }
145 170
146 /* If we get anything on STDERR, at least set warning */ 171 /* If we get anything on STDERR, at least set warning */
147 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { 172 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
148 status = max_state(status, STATE_WARNING); 173 status = max_state(status, STATE_WARNING);
149 if (verbose) 174 if (verbose) {
150 printf("%s", input_buffer); 175 printf("%s", input_buffer);
151 status = max_state(status, textscan(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));
152 } 180 }
153 (void)fclose(child_stderr); 181 (void)fclose(child_stderr);
154 182
155 /* close the pipe */ 183 /* close the pipe */
156 result = spclose(child_process); 184 int result = spclose(child_process);
157 if (result) { 185 if (result) {
158 /* need to use max_state not max */ 186 /* need to use max_state not max */
159 status = max_state(status, STATE_WARNING); 187 status = max_state(status, STATE_WARNING);
@@ -172,21 +200,17 @@ int main(int argc, char **argv) {
172 } 200 }
173 } 201 }
174 202
175 printf("FPING %s - %s\n", state_text(status), server_name); 203 printf("FPING %s - %s\n", state_text(status), config.server_name);
176 204
177 return status; 205 return status;
178} 206}
179 207
180int textscan(char *buf) { 208mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double crta, bool wrta_p,
181 char *rtastr = NULL; 209 double wrta, bool cpl_p, int cpl, bool wpl_p, int wpl, bool alive_p) {
182 char *losstr = NULL;
183 char *xmtstr = NULL;
184 double loss;
185 double rta;
186 double xmt;
187 int status = STATE_UNKNOWN;
188
189 /* stops testing after the first successful reply. */ 210 /* stops testing after the first successful reply. */
211 double rta;
212 double loss;
213 char *rtastr = NULL;
190 if (alive_p && strstr(buf, "avg, 0% loss)")) { 214 if (alive_p && strstr(buf, "avg, 0% loss)")) {
191 rtastr = strstr(buf, "ms ("); 215 rtastr = strstr(buf, "ms (");
192 rtastr = 1 + index(rtastr, '('); 216 rtastr = 1 + index(rtastr, '(');
@@ -195,9 +219,14 @@ int textscan(char *buf) {
195 die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta, 219 die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta,
196 /* No loss since we only waited for the first reply 220 /* No loss since we only waited for the first reply
197 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */ 221 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */
198 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, false, 0)); 222 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0,
223 false, 0));
199 } 224 }
200 225
226 mp_state_enum status = STATE_UNKNOWN;
227 char *xmtstr = NULL;
228 double xmt;
229 char *losstr = NULL;
201 if (strstr(buf, "not found")) { 230 if (strstr(buf, "not found")) {
202 die(STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name); 231 die(STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name);
203 232
@@ -221,19 +250,22 @@ int textscan(char *buf) {
221 rtastr = 1 + index(rtastr, '/'); 250 rtastr = 1 + index(rtastr, '/');
222 loss = strtod(losstr, NULL); 251 loss = strtod(losstr, NULL);
223 rta = strtod(rtastr, NULL); 252 rta = strtod(rtastr, NULL);
224 if (cpl_p && loss > cpl) 253 if (cpl_p && loss > cpl) {
225 status = STATE_CRITICAL; 254 status = STATE_CRITICAL;
226 else if (crta_p && rta > crta) 255 } else if (crta_p && rta > crta) {
227 status = STATE_CRITICAL; 256 status = STATE_CRITICAL;
228 else if (wpl_p && loss > wpl) 257 } else if (wpl_p && loss > wpl) {
229 status = STATE_WARNING; 258 status = STATE_WARNING;
230 else if (wrta_p && rta > wrta) 259 } else if (wrta_p && rta > wrta) {
231 status = STATE_WARNING; 260 status = STATE_WARNING;
232 else 261 } else {
233 status = STATE_OK; 262 status = STATE_OK;
234 die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status), server_name, loss, rta, 263 }
235 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), 264 die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status),
236 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, false, 0)); 265 server_name, loss, rta,
266 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0),
267 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0,
268 false, 0));
237 269
238 } else if (strstr(buf, "xmt/rcv/%loss")) { 270 } else if (strstr(buf, "xmt/rcv/%loss")) {
239 /* no min/max/avg if host was unreachable in fping v2.2.b1 */ 271 /* no min/max/avg if host was unreachable in fping v2.2.b1 */
@@ -241,22 +273,24 @@ int textscan(char *buf) {
241 losstr = strstr(buf, "="); 273 losstr = strstr(buf, "=");
242 xmtstr = 1 + losstr; 274 xmtstr = 1 + losstr;
243 xmt = strtod(xmtstr, NULL); 275 xmt = strtod(xmtstr, NULL);
244 if (xmt == 0) 276 if (xmt == 0) {
245 die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name); 277 die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name);
278 }
246 losstr = 1 + strstr(losstr, "/"); 279 losstr = 1 + strstr(losstr, "/");
247 losstr = 1 + strstr(losstr, "/"); 280 losstr = 1 + strstr(losstr, "/");
248 loss = strtod(losstr, NULL); 281 loss = strtod(losstr, NULL);
249 if (atoi(losstr) == 100) 282 if (atoi(losstr) == 100) {
250 status = STATE_CRITICAL; 283 status = STATE_CRITICAL;
251 else if (cpl_p && loss > cpl) 284 } else if (cpl_p && loss > cpl) {
252 status = STATE_CRITICAL; 285 status = STATE_CRITICAL;
253 else if (wpl_p && loss > wpl) 286 } else if (wpl_p && loss > wpl) {
254 status = STATE_WARNING; 287 status = STATE_WARNING;
255 else 288 } else {
256 status = STATE_OK; 289 status = STATE_OK;
290 }
257 /* loss=%.0f%%;%d;%d;0;100 */ 291 /* loss=%.0f%%;%d;%d;0;100 */
258 die(status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), state_text(status), server_name, loss, 292 die(status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), state_text(status), server_name, loss,
259 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100)); 293 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0));
260 294
261 } else { 295 } else {
262 status = max_state(status, STATE_WARNING); 296 status = max_state(status, STATE_WARNING);
@@ -266,11 +300,12 @@ int textscan(char *buf) {
266} 300}
267 301
268/* process command-line arguments */ 302/* process command-line arguments */
269int process_arguments(int argc, char **argv) { 303check_fping_config_wrapper process_arguments(int argc, char **argv) {
270 int c; 304 enum {
271 char *rv[2]; 305 FWMARK_OPT = CHAR_MAX + 1,
272 306 ICMP_TIMESTAMP_OPT,
273 int option = 0; 307 CHECK_SOURCE_OPT,
308 };
274 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 309 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
275 {"sourceip", required_argument, 0, 'S'}, 310 {"sourceip", required_argument, 0, 'S'},
276 {"sourceif", required_argument, 0, 'I'}, 311 {"sourceif", required_argument, 0, 'I'},
@@ -288,32 +323,53 @@ int process_arguments(int argc, char **argv) {
288 {"use-ipv6", no_argument, 0, '6'}, 323 {"use-ipv6", no_argument, 0, '6'},
289 {"dontfrag", no_argument, 0, 'M'}, 324 {"dontfrag", no_argument, 0, 'M'},
290 {"random", no_argument, 0, 'R'}, 325 {"random", no_argument, 0, 'R'},
326#ifdef FPING_VERSION_5_2_OR_HIGHER
327 // only available with fping version >= 5.2
328 {"fwmark", required_argument, NULL, FWMARK_OPT},
329# ifdef FPING_VERSION_5_3_OR_HIGHER
330 // only available with fping version >= 5.3
331 {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT},
332 {"check-source", no_argument, NULL, CHECK_SOURCE_OPT},
333# endif
334#endif
291 {0, 0, 0, 0}}; 335 {0, 0, 0, 0}};
292 336
337 char *rv[2];
293 rv[PL] = NULL; 338 rv[PL] = NULL;
294 rv[RTA] = NULL; 339 rv[RTA] = NULL;
295 340
296 if (argc < 2) 341 int option = 0;
297 return ERROR; 342
343 check_fping_config_wrapper result = {
344 .errorcode = OK,
345 .config = check_fping_config_init(),
346 };
347
348 if (argc < 2) {
349 result.errorcode = ERROR;
350 return result;
351 }
298 352
299 if (!is_option(argv[1])) { 353 if (!is_option(argv[1])) {
300 server_name = argv[1]; 354 result.config.server_name = argv[1];
301 argv[1] = argv[0]; 355 argv[1] = argv[0];
302 argv = &argv[1]; 356 argv = &argv[1];
303 argc--; 357 argc--;
304 } 358 }
305 359
306 while (1) { 360 while (true) {
307 c = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); 361 int option_index =
362 getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option);
308 363
309 if (c == -1 || c == EOF || c == 1) 364 if (option_index == -1 || option_index == EOF || option_index == 1) {
310 break; 365 break;
366 }
311 367
312 switch (c) { 368 switch (option_index) {
313 case '?': /* print short usage statement if args not parsable */ 369 case '?': /* print short usage statement if args not parsable */
314 usage5(); 370 usage5();
315 case 'a': /* host alive mode */ 371 case 'a': /* host alive mode */
316 alive_p = true; 372 result.config.alive_p = true;
317 break; 373 break;
318 case 'h': /* help */ 374 case 'h': /* help */
319 print_help(); 375 print_help();
@@ -325,109 +381,128 @@ int process_arguments(int argc, char **argv) {
325 verbose = true; 381 verbose = true;
326 break; 382 break;
327 case 'H': /* hostname */ 383 case 'H': /* hostname */
328 if (is_host(optarg) == false) { 384 if (!is_host(optarg)) {
329 usage2(_("Invalid hostname/address"), optarg); 385 usage2(_("Invalid hostname/address"), optarg);
330 } 386 }
331 server_name = strscpy(server_name, optarg); 387 result.config.server_name = optarg;
332 break; 388 break;
333 case 'S': /* sourceip */ 389 case 'S': /* sourceip */
334 if (is_host(optarg) == false) { 390 if (!is_host(optarg)) {
335 usage2(_("Invalid hostname/address"), optarg); 391 usage2(_("Invalid hostname/address"), optarg);
336 } 392 }
337 sourceip = strscpy(sourceip, optarg); 393 result.config.sourceip = optarg;
338 break; 394 break;
339 case 'I': /* sourceip */ 395 case 'I': /* sourceip */
340 sourceif = strscpy(sourceif, optarg); 396 result.config.sourceif = optarg;
341 break; 397 break;
342 case '4': /* IPv4 only */ 398 case '4': /* IPv4 only */
343 address_family = AF_INET; 399 address_family = AF_INET;
344 break; 400 break;
345 case '6': /* IPv6 only */ 401 case '6': /* IPv6 only */
346#ifdef USE_IPV6
347 address_family = AF_INET6; 402 address_family = AF_INET6;
348#else
349 usage(_("IPv6 support not available\n"));
350#endif
351 break; 403 break;
352 case 'c': 404 case 'c':
353 get_threshold(optarg, rv); 405 get_threshold(optarg, rv);
354 if (rv[RTA]) { 406 if (rv[RTA]) {
355 crta = strtod(rv[RTA], NULL); 407 result.config.crta = strtod(rv[RTA], NULL);
356 crta_p = true; 408 result.config.crta_p = true;
357 rv[RTA] = NULL; 409 rv[RTA] = NULL;
358 } 410 }
359 if (rv[PL]) { 411 if (rv[PL]) {
360 cpl = atoi(rv[PL]); 412 result.config.cpl = atoi(rv[PL]);
361 cpl_p = true; 413 result.config.cpl_p = true;
362 rv[PL] = NULL; 414 rv[PL] = NULL;
363 } 415 }
364 break; 416 break;
365 case 'w': 417 case 'w':
366 get_threshold(optarg, rv); 418 get_threshold(optarg, rv);
367 if (rv[RTA]) { 419 if (rv[RTA]) {
368 wrta = strtod(rv[RTA], NULL); 420 result.config.wrta = strtod(rv[RTA], NULL);
369 wrta_p = true; 421 result.config.wrta_p = true;
370 rv[RTA] = NULL; 422 rv[RTA] = NULL;
371 } 423 }
372 if (rv[PL]) { 424 if (rv[PL]) {
373 wpl = atoi(rv[PL]); 425 result.config.wpl = atoi(rv[PL]);
374 wpl_p = true; 426 result.config.wpl_p = true;
375 rv[PL] = NULL; 427 rv[PL] = NULL;
376 } 428 }
377 break; 429 break;
378 case 'b': /* bytes per packet */ 430 case 'b': /* bytes per packet */
379 if (is_intpos(optarg)) 431 if (is_intpos(optarg)) {
380 packet_size = atoi(optarg); 432 result.config.packet_size = atoi(optarg);
381 else 433 } else {
382 usage(_("Packet size must be a positive integer")); 434 usage(_("Packet size must be a positive integer"));
435 }
383 break; 436 break;
384 case 'n': /* number of packets */ 437 case 'n': /* number of packets */
385 if (is_intpos(optarg)) 438 if (is_intpos(optarg)) {
386 packet_count = atoi(optarg); 439 result.config.packet_count = atoi(optarg);
387 else 440 } else {
388 usage(_("Packet count must be a positive integer")); 441 usage(_("Packet count must be a positive integer"));
442 }
389 break; 443 break;
390 case 'T': /* timeout in msec */ 444 case 'T': /* timeout in msec */
391 if (is_intpos(optarg)) 445 if (is_intpos(optarg)) {
392 target_timeout = atoi(optarg); 446 result.config.target_timeout = atoi(optarg);
393 else 447 } else {
394 usage(_("Target timeout must be a positive integer")); 448 usage(_("Target timeout must be a positive integer"));
449 }
395 break; 450 break;
396 case 'i': /* interval in msec */ 451 case 'i': /* interval in msec */
397 if (is_intpos(optarg)) 452 if (is_intpos(optarg)) {
398 packet_interval = atoi(optarg); 453 result.config.packet_interval = atoi(optarg);
399 else 454 } else {
400 usage(_("Interval must be a positive integer")); 455 usage(_("Interval must be a positive integer"));
456 }
401 break; 457 break;
402 case 'R': 458 case 'R':
403 randomize_packet_data = true; 459 result.config.randomize_packet_data = true;
404 break; 460 break;
405 case 'M': 461 case 'M':
406 dontfrag = true; 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;
407 break; 477 break;
408 } 478 }
409 } 479 }
410 480
411 if (server_name == NULL) 481 if (result.config.server_name == NULL) {
412 usage4(_("Hostname was not supplied")); 482 usage4(_("Hostname was not supplied"));
483 }
413 484
414 return OK; 485 return result;
415} 486}
416 487
417int get_threshold(char *arg, char *rv[2]) { 488int get_threshold(char *arg, char *rv[2]) {
418 char *arg1 = NULL;
419 char *arg2 = NULL; 489 char *arg2 = NULL;
420 490
421 arg1 = strscpy(arg1, arg); 491 char *arg1 = strdup(arg);
422 if (strpbrk(arg1, ",:")) 492 if (strpbrk(arg1, ",:")) {
423 arg2 = 1 + strpbrk(arg1, ",:"); 493 arg2 = 1 + strpbrk(arg1, ",:");
494 }
424 495
425 if (arg2) { 496 if (arg2) {
426 arg1[strcspn(arg1, ",:")] = 0; 497 arg1[strcspn(arg1, ",:")] = 0;
427 if (strstr(arg1, "%") && strstr(arg2, "%")) 498 if (strstr(arg1, "%") && strstr(arg2, "%")) {
428 die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname, arg); 499 die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname,
429 if (!strstr(arg1, "%") && !strstr(arg2, "%")) 500 arg);
430 die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname, 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 }
431 } 506 }
432 507
433 if (arg2 && strstr(arg2, "%")) { 508 if (arg2 && strstr(arg2, "%")) {
@@ -452,7 +527,8 @@ void print_help(void) {
452 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");
453 printf(COPYRIGHT, copyright, email); 528 printf(COPYRIGHT, copyright, email);
454 529
455 printf("%s\n", _("This plugin will use the fping command to ping the specified host for a fast check")); 530 printf("%s\n",
531 _("This plugin will use the fping command to ping the specified host for a fast check"));
456 532
457 printf("%s\n", _("Note that it is necessary to set the suid flag on fping.")); 533 printf("%s\n", _("Note that it is necessary to set the suid flag on fping."));
458 534
@@ -466,7 +542,8 @@ void print_help(void) {
466 printf(UT_IPv46); 542 printf(UT_IPv46);
467 543
468 printf(" %s\n", "-H, --hostname=HOST"); 544 printf(" %s\n", "-H, --hostname=HOST");
469 printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, reducing system load)")); 545 printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, "
546 "reducing system load)"));
470 printf(" %s\n", "-w, --warning=THRESHOLD"); 547 printf(" %s\n", "-w, --warning=THRESHOLD");
471 printf(" %s\n", _("warning threshold pair")); 548 printf(" %s\n", _("warning threshold pair"));
472 printf(" %s\n", "-c, --critical=THRESHOLD"); 549 printf(" %s\n", "-c, --critical=THRESHOLD");
@@ -480,7 +557,8 @@ void print_help(void) {
480 printf(" %s\n", "-T, --target-timeout=INTEGER"); 557 printf(" %s\n", "-T, --target-timeout=INTEGER");
481 printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)")); 558 printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)"));
482 printf(" %s\n", "-i, --interval=INTEGER"); 559 printf(" %s\n", "-i, --interval=INTEGER");
483 printf(" %s (default: fping's default for -p)\n", _("Interval (ms) between sending packets")); 560 printf(" %s (default: fping's default for -p)\n",
561 _("Interval (ms) between sending packets"));
484 printf(" %s\n", "-S, --sourceip=HOST"); 562 printf(" %s\n", "-S, --sourceip=HOST");
485 printf(" %s\n", _("name or IP Address of sourceip")); 563 printf(" %s\n", _("name or IP Address of sourceip"));
486 printf(" %s\n", "-I, --sourceif=IF"); 564 printf(" %s\n", "-I, --sourceif=IF");
@@ -489,9 +567,20 @@ void print_help(void) {
489 printf(" %s\n", _("set the Don't Fragment flag")); 567 printf(" %s\n", _("set the Don't Fragment flag"));
490 printf(" %s\n", "-R, --random"); 568 printf(" %s\n", "-R, --random");
491 printf(" %s\n", _("random packet data (to foil link data compression)")); 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
492 printf(UT_VERBOSE); 580 printf(UT_VERBOSE);
493 printf("\n"); 581 printf("\n");
494 printf(" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)")); 582 printf(" %s\n",
583 _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)"));
495 printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of")); 584 printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of"));
496 printf(" %s\n", _("packet loss to trigger an alarm state.")); 585 printf(" %s\n", _("packet loss to trigger an alarm state."));
497 586
@@ -503,5 +592,6 @@ void print_help(void) {
503 592
504void print_usage(void) { 593void print_usage(void) {
505 printf("%s\n", _("Usage:")); 594 printf("%s\n", _("Usage:"));
506 printf(" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", progname); 595 printf(" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n",
596 progname);
507} 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 c0193b03..974a7253 100644
--- a/plugins/check_game.c
+++ b/plugins/check_game.c
@@ -77,7 +77,8 @@ int main(int argc, char **argv) {
77 77
78 /* create the command line to execute */ 78 /* create the command line to execute */
79 char *command_line = NULL; 79 char *command_line = NULL;
80 xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, config.game_type, config.server_ip); 80 xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER,
81 config.game_type, config.server_ip);
81 82
82 if (config.port) { 83 if (config.port) {
83 xasprintf(&command_line, "%s:%-d", command_line, config.port); 84 xasprintf(&command_line, "%s:%-d", command_line, config.port);
@@ -130,11 +131,13 @@ int main(int argc, char **argv) {
130 printf(_("CRITICAL - Game server timeout\n")); 131 printf(_("CRITICAL - Game server timeout\n"));
131 result = STATE_CRITICAL; 132 result = STATE_CRITICAL;
132 } else { 133 } else {
133 printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[config.qstat_game_players], ret[config.qstat_game_players_max], 134 printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[config.qstat_game_players],
134 ret[config.qstat_game_field], ret[config.qstat_map_field], ret[config.qstat_ping_field], 135 ret[config.qstat_game_players_max], ret[config.qstat_game_field],
135 perfdata("players", atol(ret[config.qstat_game_players]), "", false, 0, false, 0, true, 0, true, 136 ret[config.qstat_map_field], ret[config.qstat_ping_field],
136 atol(ret[config.qstat_game_players_max])), 137 perfdata("players", atol(ret[config.qstat_game_players]), "", false, 0, false, 0,
137 fperfdata("ping", strtod(ret[config.qstat_ping_field], NULL), "", false, 0, false, 0, true, 0, false, 0)); 138 true, 0, true, atol(ret[config.qstat_game_players_max])),
139 fperfdata("ping", strtod(ret[config.qstat_ping_field], NULL), "", false, 0, false, 0,
140 true, 0, false, 0));
138 } 141 }
139 142
140 exit(result); 143 exit(result);
@@ -144,19 +147,20 @@ int main(int argc, char **argv) {
144#define max_players_field_index 130 147#define max_players_field_index 130
145 148
146check_game_config_wrapper process_arguments(int argc, char **argv) { 149check_game_config_wrapper process_arguments(int argc, char **argv) {
147 static struct option long_opts[] = {{"help", no_argument, 0, 'h'}, 150 static struct option long_opts[] = {
148 {"version", no_argument, 0, 'V'}, 151 {"help", no_argument, 0, 'h'},
149 {"verbose", no_argument, 0, 'v'}, 152 {"version", no_argument, 0, 'V'},
150 {"timeout", required_argument, 0, 't'}, 153 {"verbose", no_argument, 0, 'v'},
151 {"hostname", required_argument, 0, 'H'}, 154 {"timeout", required_argument, 0, 't'},
152 {"port", required_argument, 0, 'P'}, 155 {"hostname", required_argument, 0, 'H'},
153 {"game-type", required_argument, 0, 'G'}, 156 {"port", required_argument, 0, 'P'},
154 {"map-field", required_argument, 0, 'm'}, 157 {"game-type", required_argument, 0, 'G'},
155 {"ping-field", required_argument, 0, 'p'}, 158 {"map-field", required_argument, 0, 'm'},
156 {"game-field", required_argument, 0, 'g'}, 159 {"ping-field", required_argument, 0, 'p'},
157 {"players-field", required_argument, 0, players_field_index}, 160 {"game-field", required_argument, 0, 'g'},
158 {"max-players-field", required_argument, 0, max_players_field_index}, 161 {"players-field", required_argument, 0, players_field_index},
159 {0, 0, 0, 0}}; 162 {"max-players-field", required_argument, 0, max_players_field_index},
163 {0, 0, 0, 0}};
160 164
161 check_game_config_wrapper result = { 165 check_game_config_wrapper result = {
162 .config = check_game_config_init(), 166 .config = check_game_config_init(),
@@ -216,21 +220,24 @@ check_game_config_wrapper process_arguments(int argc, char **argv) {
216 break; 220 break;
217 case 'p': /* index of ping field */ 221 case 'p': /* index of ping field */
218 result.config.qstat_ping_field = atoi(optarg); 222 result.config.qstat_ping_field = atoi(optarg);
219 if (result.config.qstat_ping_field < 0 || result.config.qstat_ping_field > QSTAT_MAX_RETURN_ARGS) { 223 if (result.config.qstat_ping_field < 0 ||
224 result.config.qstat_ping_field > QSTAT_MAX_RETURN_ARGS) {
220 result.errorcode = ERROR; 225 result.errorcode = ERROR;
221 return result; 226 return result;
222 } 227 }
223 break; 228 break;
224 case 'm': /* index on map field */ 229 case 'm': /* index on map field */
225 result.config.qstat_map_field = atoi(optarg); 230 result.config.qstat_map_field = atoi(optarg);
226 if (result.config.qstat_map_field < 0 || result.config.qstat_map_field > QSTAT_MAX_RETURN_ARGS) { 231 if (result.config.qstat_map_field < 0 ||
232 result.config.qstat_map_field > QSTAT_MAX_RETURN_ARGS) {
227 result.errorcode = ERROR; 233 result.errorcode = ERROR;
228 return result; 234 return result;
229 } 235 }
230 break; 236 break;
231 case 'g': /* index of game field */ 237 case 'g': /* index of game field */
232 result.config.qstat_game_field = atoi(optarg); 238 result.config.qstat_game_field = atoi(optarg);
233 if (result.config.qstat_game_field < 0 || result.config.qstat_game_field > QSTAT_MAX_RETURN_ARGS) { 239 if (result.config.qstat_game_field < 0 ||
240 result.config.qstat_game_field > QSTAT_MAX_RETURN_ARGS) {
234 result.errorcode = ERROR; 241 result.errorcode = ERROR;
235 return result; 242 return result;
236 } 243 }
@@ -240,14 +247,16 @@ check_game_config_wrapper process_arguments(int argc, char **argv) {
240 if (result.config.qstat_game_players_max == 0) { 247 if (result.config.qstat_game_players_max == 0) {
241 result.config.qstat_game_players_max = result.config.qstat_game_players - 1; 248 result.config.qstat_game_players_max = result.config.qstat_game_players - 1;
242 } 249 }
243 if (result.config.qstat_game_players < 0 || result.config.qstat_game_players > QSTAT_MAX_RETURN_ARGS) { 250 if (result.config.qstat_game_players < 0 ||
251 result.config.qstat_game_players > QSTAT_MAX_RETURN_ARGS) {
244 result.errorcode = ERROR; 252 result.errorcode = ERROR;
245 return result; 253 return result;
246 } 254 }
247 break; 255 break;
248 case max_players_field_index: /* index of max players field */ 256 case max_players_field_index: /* index of max players field */
249 result.config.qstat_game_players_max = atoi(optarg); 257 result.config.qstat_game_players_max = atoi(optarg);
250 if (result.config.qstat_game_players_max < 0 || result.config.qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) { 258 if (result.config.qstat_game_players_max < 0 ||
259 result.config.qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) {
251 result.errorcode = ERROR; 260 result.errorcode = ERROR;
252 return result; 261 return result;
253 } 262 }
@@ -286,7 +295,7 @@ void print_help(void) {
286 printf(UT_HELP_VRSN); 295 printf(UT_HELP_VRSN);
287 printf(UT_EXTRA_OPTS); 296 printf(UT_EXTRA_OPTS);
288 printf(" -H, --hostname=ADDRESS\n" 297 printf(" -H, --hostname=ADDRESS\n"
289 " Host name, IP Address, or unix socket (must be an absolute path)\n"); 298 " Host name, IP Address, or unix socket (must be an absolute path)\n");
290 printf(" %s\n", "-P"); 299 printf(" %s\n", "-P");
291 printf(" %s\n", _("Optional port to connect to")); 300 printf(" %s\n", _("Optional port to connect to"));
292 printf(" %s\n", "-g"); 301 printf(" %s\n", "-g");
@@ -300,8 +309,10 @@ void print_help(void) {
300 309
301 printf("\n"); 310 printf("\n");
302 printf("%s\n", _("Notes:")); 311 printf("%s\n", _("Notes:"));
303 printf(" %s\n", _("This plugin uses the 'qstat' command, the popular game server status query tool.")); 312 printf(" %s\n",
304 printf(" %s\n", _("If you don't have the package installed, you will need to download it from")); 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"));
305 printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin.")); 316 printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin."));
306 317
307 printf(UT_SUPPORT); 318 printf(UT_SUPPORT);
@@ -309,7 +320,8 @@ void print_help(void) {
309 320
310void print_usage(void) { 321void print_usage(void) {
311 printf("%s\n", _("Usage:")); 322 printf("%s\n", _("Usage:"));
312 printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G game-time] [-H hostname] <game> " 323 printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G "
324 "game-time] [-H hostname] <game> "
313 "<ip_address>\n", 325 "<ip_address>\n",
314 progname); 326 progname);
315} 327}
diff --git a/plugins/check_hpjd.c b/plugins/check_hpjd.c
index b39bccff..9907abc5 100644
--- a/plugins/check_hpjd.c
+++ b/plugins/check_hpjd.c
@@ -37,9 +37,10 @@ const char *email = "devel@monitoring-plugins.org";
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 44
44#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"
45#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"
@@ -57,39 +58,15 @@ const char *email = "devel@monitoring-plugins.org";
57#define ONLINE 0 58#define ONLINE 0
58#define OFFLINE 1 59#define OFFLINE 1
59 60
60static int process_arguments(int /*argc*/, char ** /*argv*/); 61typedef struct {
61static int validate_arguments(void); 62 int errorcode;
63 check_hpjd_config config;
64} check_hpjd_config_wrapper;
65static check_hpjd_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
62static void print_help(void); 66static void print_help(void);
63void print_usage(void); 67void print_usage(void);
64 68
65static char *community = NULL;
66static char *address = NULL;
67static unsigned int port = 0;
68static int check_paper_out = 1;
69
70int main(int argc, char **argv) { 69int main(int argc, char **argv) {
71 char command_line[1024];
72 int result = STATE_UNKNOWN;
73 int line;
74 char input_buffer[MAX_INPUT_BUFFER];
75 char query_string[512];
76 char *errmsg;
77 char *temp_buffer;
78 int line_status = ONLINE;
79 int paper_status = 0;
80 int intervention_required = 0;
81 int peripheral_error = 0;
82 int paper_jam = 0;
83 int paper_out = 0;
84 int toner_low = 0;
85 int page_punt = 0;
86 int memory_out = 0;
87 int door_open = 0;
88 int paper_output = 0;
89 char display_message[MAX_INPUT_BUFFER];
90
91 errmsg = malloc(MAX_INPUT_BUFFER);
92
93 setlocale(LC_ALL, ""); 70 setlocale(LC_ALL, "");
94 bindtextdomain(PACKAGE, LOCALEDIR); 71 bindtextdomain(PACKAGE, LOCALEDIR);
95 textdomain(PACKAGE); 72 textdomain(PACKAGE);
@@ -97,17 +74,27 @@ int main(int argc, char **argv) {
97 /* Parse extra opts if any */ 74 /* Parse extra opts if any */
98 argv = np_extra_opts(&argc, argv, progname); 75 argv = np_extra_opts(&argc, argv, progname);
99 76
100 if (process_arguments(argc, argv) == ERROR) 77 check_hpjd_config_wrapper tmp_config = process_arguments(argc, argv);
78
79 if (tmp_config.errorcode == ERROR) {
101 usage4(_("Could not parse arguments")); 80 usage4(_("Could not parse arguments"));
81 }
82
83 const check_hpjd_config config = tmp_config.config;
102 84
85 char query_string[512];
103 /* removed ' 2>1' at end of command 10/27/1999 - EG */ 86 /* removed ' 2>1' at end of command 10/27/1999 - EG */
104 /* create the query string */ 87 /* create the query string */
105 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", HPJD_LINE_STATUS, HPJD_PAPER_STATUS, 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",
106 HPJD_INTERVENTION_REQUIRED, HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW, 89 HPJD_LINE_STATUS, HPJD_PAPER_STATUS, HPJD_INTERVENTION_REQUIRED,
107 HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT, HPJD_GD_STATUS_DISPLAY); 90 HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW,
91 HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT,
92 HPJD_GD_STATUS_DISPLAY);
108 93
109 /* get the command to run */ 94 /* get the command to run */
110 sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, community, address, port, query_string); 95 char command_line[1024];
96 sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, config.community,
97 config.address, config.port, query_string);
111 98
112 /* run the command */ 99 /* run the command */
113 child_process = spopen(command_line); 100 child_process = spopen(command_line);
@@ -121,29 +108,41 @@ int main(int argc, char **argv) {
121 printf(_("Could not open stderr for %s\n"), command_line); 108 printf(_("Could not open stderr for %s\n"), command_line);
122 } 109 }
123 110
124 result = STATE_OK; 111 mp_state_enum result = STATE_OK;
125 112
126 line = 0; 113 int line_status = ONLINE;
127 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];
128 125
126 char input_buffer[MAX_INPUT_BUFFER];
127 char *errmsg = malloc(MAX_INPUT_BUFFER);
128 int line = 0;
129
130 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
129 /* strip the newline character from the end of the input */ 131 /* strip the newline character from the end of the input */
130 if (input_buffer[strlen(input_buffer) - 1] == '\n') 132 if (input_buffer[strlen(input_buffer) - 1] == '\n') {
131 input_buffer[strlen(input_buffer) - 1] = 0; 133 input_buffer[strlen(input_buffer) - 1] = 0;
134 }
132 135
133 line++; 136 line++;
134 137
135 temp_buffer = strtok(input_buffer, "="); 138 char *temp_buffer = strtok(input_buffer, "=");
136 temp_buffer = strtok(NULL, "="); 139 temp_buffer = strtok(NULL, "=");
137 140
138 if (temp_buffer == NULL && line < 13) { 141 if (temp_buffer == NULL && line < 13) {
139
140 result = STATE_UNKNOWN; 142 result = STATE_UNKNOWN;
141 strcpy(errmsg, input_buffer); 143 strcpy(errmsg, input_buffer);
142
143 } else { 144 } else {
144
145 switch (line) { 145 switch (line) {
146
147 case 1: /* 1st line should contain the line status */ 146 case 1: /* 1st line should contain the line status */
148 line_status = atoi(temp_buffer); 147 line_status = atoi(temp_buffer);
149 break; 148 break;
@@ -181,21 +180,24 @@ int main(int argc, char **argv) {
181 strcpy(display_message, temp_buffer + 1); 180 strcpy(display_message, temp_buffer + 1);
182 break; 181 break;
183 default: /* fold multiline message */ 182 default: /* fold multiline message */
184 strncat(display_message, input_buffer, sizeof(display_message) - strlen(display_message) - 1); 183 strncat(display_message, input_buffer,
184 sizeof(display_message) - strlen(display_message) - 1);
185 } 185 }
186 } 186 }
187 187
188 /* break out of the read loop if we encounter an error */ 188 /* break out of the read loop if we encounter an error */
189 if (result != STATE_OK) 189 if (result != STATE_OK) {
190 break; 190 break;
191 }
191 } 192 }
192 193
193 /* WARNING if output found on stderr */ 194 /* WARNING if output found on stderr */
194 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { 195 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
195 result = max_state(result, STATE_WARNING); 196 result = max_state(result, STATE_WARNING);
196 /* remove CRLF */ 197 /* remove CRLF */
197 if (input_buffer[strlen(input_buffer) - 1] == '\n') 198 if (input_buffer[strlen(input_buffer) - 1] == '\n') {
198 input_buffer[strlen(input_buffer) - 1] = 0; 199 input_buffer[strlen(input_buffer) - 1] = 0;
200 }
199 sprintf(errmsg, "%s", input_buffer); 201 sprintf(errmsg, "%s", input_buffer);
200 } 202 }
201 203
@@ -203,15 +205,15 @@ int main(int argc, char **argv) {
203 (void)fclose(child_stderr); 205 (void)fclose(child_stderr);
204 206
205 /* close the pipe */ 207 /* close the pipe */
206 if (spclose(child_process)) 208 if (spclose(child_process)) {
207 result = max_state(result, STATE_WARNING); 209 result = max_state(result, STATE_WARNING);
210 }
208 211
209 /* if there wasn't any output, display an error */ 212 /* if there wasn't any output, display an error */
210 if (line == 0) { 213 if (line == 0) {
211
212 /* might not be the problem, but most likely is. */ 214 /* might not be the problem, but most likely is. */
213 result = STATE_UNKNOWN; 215 result = STATE_UNKNOWN;
214 xasprintf(&errmsg, "%s : Timeout from host %s\n", errmsg, address); 216 xasprintf(&errmsg, "%s : Timeout from host %s\n", errmsg, config.address);
215 } 217 }
216 218
217 /* if we had no read errors, check the printer status results... */ 219 /* if we had no read errors, check the printer status results... */
@@ -221,8 +223,9 @@ int main(int argc, char **argv) {
221 result = STATE_WARNING; 223 result = STATE_WARNING;
222 strcpy(errmsg, _("Paper Jam")); 224 strcpy(errmsg, _("Paper Jam"));
223 } else if (paper_out) { 225 } else if (paper_out) {
224 if (check_paper_out) 226 if (config.check_paper_out) {
225 result = STATE_WARNING; 227 result = STATE_WARNING;
228 }
226 strcpy(errmsg, _("Out of Paper")); 229 strcpy(errmsg, _("Out of Paper"));
227 } else if (line_status == OFFLINE) { 230 } else if (line_status == OFFLINE) {
228 if (strcmp(errmsg, "POWERSAVE ON") != 0) { 231 if (strcmp(errmsg, "POWERSAVE ON") != 0) {
@@ -256,29 +259,23 @@ int main(int argc, char **argv) {
256 } 259 }
257 } 260 }
258 261
259 if (result == STATE_OK) 262 if (result == STATE_OK) {
260 printf(_("Printer ok - (%s)\n"), display_message); 263 printf(_("Printer ok - (%s)\n"), display_message);
261 264 } else if (result == STATE_UNKNOWN) {
262 else if (result == STATE_UNKNOWN) {
263
264 printf("%s\n", errmsg); 265 printf("%s\n", errmsg);
265
266 /* if printer could not be reached, escalate to critical */ 266 /* if printer could not be reached, escalate to critical */
267 if (strstr(errmsg, "Timeout")) 267 if (strstr(errmsg, "Timeout")) {
268 result = STATE_CRITICAL; 268 result = STATE_CRITICAL;
269 } 269 }
270 270 } else if (result == STATE_WARNING) {
271 else if (result == STATE_WARNING)
272 printf("%s (%s)\n", errmsg, display_message); 271 printf("%s (%s)\n", errmsg, display_message);
272 }
273 273
274 return result; 274 exit(result);
275} 275}
276 276
277/* process command-line arguments */ 277/* process command-line arguments */
278int process_arguments(int argc, char **argv) { 278check_hpjd_config_wrapper process_arguments(int argc, char **argv) {
279 int c;
280
281 int option = 0;
282 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 279 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
283 {"community", required_argument, 0, 'C'}, 280 {"community", required_argument, 0, 'C'},
284 /* {"critical", required_argument,0,'c'}, */ 281 /* {"critical", required_argument,0,'c'}, */
@@ -288,34 +285,44 @@ int process_arguments(int argc, char **argv) {
288 {"help", no_argument, 0, 'h'}, 285 {"help", no_argument, 0, 'h'},
289 {0, 0, 0, 0}}; 286 {0, 0, 0, 0}};
290 287
291 if (argc < 2) 288 check_hpjd_config_wrapper result = {
292 return ERROR; 289 .errorcode = OK,
290 .config = check_hpjd_config_init(),
291 };
292
293 if (argc < 2) {
294 result.errorcode = ERROR;
295 return result;
296 }
293 297
294 while (1) { 298 int option = 0;
295 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);
296 301
297 if (c == -1 || c == EOF || c == 1) 302 if (option_index == -1 || option_index == EOF || option_index == 1) {
298 break; 303 break;
304 }
299 305
300 switch (c) { 306 switch (option_index) {
301 case 'H': /* hostname */ 307 case 'H': /* hostname */
302 if (is_host(optarg)) { 308 if (is_host(optarg)) {
303 address = strscpy(address, optarg); 309 result.config.address = strscpy(result.config.address, optarg);
304 } else { 310 } else {
305 usage2(_("Invalid hostname/address"), optarg); 311 usage2(_("Invalid hostname/address"), optarg);
306 } 312 }
307 break; 313 break;
308 case 'C': /* community */ 314 case 'C': /* community */
309 community = strscpy(community, optarg); 315 result.config.community = strscpy(result.config.community, optarg);
310 break; 316 break;
311 case 'p': 317 case 'p':
312 if (!is_intpos(optarg)) 318 if (!is_intpos(optarg)) {
313 usage2(_("Port must be a positive short integer"), optarg); 319 usage2(_("Port must be a positive short integer"), optarg);
314 else 320 } else {
315 port = atoi(optarg); 321 result.config.port = atoi(optarg);
322 }
316 break; 323 break;
317 case 'D': /* disable paper out check*/ 324 case 'D': /* disable paper out check*/
318 check_paper_out = 0; 325 result.config.check_paper_out = false;
319 break; 326 break;
320 case 'V': /* version */ 327 case 'V': /* version */
321 print_revision(progname, NP_VERSION); 328 print_revision(progname, NP_VERSION);
@@ -328,31 +335,26 @@ int process_arguments(int argc, char **argv) {
328 } 335 }
329 } 336 }
330 337
331 c = optind; 338 int c = optind;
332 if (address == NULL) { 339 if (result.config.address == NULL) {
333 if (is_host(argv[c])) { 340 if (is_host(argv[c])) {
334 address = argv[c++]; 341 result.config.address = argv[c++];
335 } else { 342 } else {
336 usage2(_("Invalid hostname/address"), argv[c]); 343 usage2(_("Invalid hostname/address"), argv[c]);
337 } 344 }
338 } 345 }
339 346
340 if (community == NULL) { 347 if (result.config.community == NULL) {
341 if (argv[c] != NULL) 348 if (argv[c] != NULL) {
342 community = argv[c]; 349 result.config.community = argv[c];
343 else 350 } else {
344 community = strdup(DEFAULT_COMMUNITY); 351 result.config.community = strdup(DEFAULT_COMMUNITY);
345 } 352 }
346
347 if (port == 0) {
348 port = atoi(DEFAULT_PORT);
349 } 353 }
350 354
351 return validate_arguments(); 355 return result;
352} 356}
353 357
354int validate_arguments(void) { return OK; }
355
356void print_help(void) { 358void print_help(void) {
357 print_revision(progname, NP_VERSION); 359 print_revision(progname, NP_VERSION);
358 360
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 baff682a..d264b95d 100644
--- a/plugins/check_http.c
+++ b/plugins/check_http.c
@@ -1,35 +1,35 @@
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-2024 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_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-2024"; 35const char *copyright = "1999-2024";
@@ -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,1394 @@ 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;
545 break;
546 case '6':
528#ifdef USE_IPV6 547#ifdef USE_IPV6
529 address_family = AF_INET6; 548 address_family = AF_INET6;
530#else 549#else
531 usage4 (_("IPv6 support not available")); 550 usage4(_("IPv6 support not available"));
532#endif 551#endif
533 break; 552 break;
534 case 'v': /* verbose */ 553 case 'v': /* verbose */
535 verbose = true; 554 verbose = true;
536 break; 555 break;
537 case 'm': /* min_page_length */ 556 case 'm': /* min_page_length */
538 { 557 {
539 char *tmp; 558 char *tmp;
540 if (strchr(optarg, ':') != (char *)NULL) { 559 if (strchr(optarg, ':') != (char *)NULL) {
541 /* range, so get two values, min:max */ 560 /* range, so get two values, min:max */
542 tmp = strtok(optarg, ":"); 561 tmp = strtok(optarg, ":");
543 if (tmp == NULL) { 562 if (tmp == NULL) {
544 printf("Bad format: try \"-m min:max\"\n"); 563 printf("Bad format: try \"-m min:max\"\n");
545 exit (STATE_WARNING); 564 exit(STATE_WARNING);
546 } else 565 } else {
547 min_page_len = atoi(tmp); 566 min_page_len = atoi(tmp);
548 567 }
549 tmp = strtok(NULL, ":"); 568
550 if (tmp == NULL) { 569 tmp = strtok(NULL, ":");
551 printf("Bad format: try \"-m min:max\"\n"); 570 if (tmp == NULL) {
552 exit (STATE_WARNING); 571 printf("Bad format: try \"-m min:max\"\n");
553 } else 572 exit(STATE_WARNING);
554 max_page_len = atoi(tmp); 573 } else {
555 } else 574 max_page_len = atoi(tmp);
556 min_page_len = atoi (optarg); 575 }
557 break; 576 } else {
558 } 577 min_page_len = atoi(optarg);
559 case 'N': /* no-body */ 578 }
560 no_body = true; 579 break;
561 break; 580 }
562 case 'M': /* max-age */ 581 case 'N': /* no-body */
563 { 582 no_body = true;
564 int L = strlen(optarg); 583 break;
565 if (L && optarg[L-1] == 'm') 584 case 'M': /* max-age */
566 maximum_age = atoi (optarg) * 60; 585 {
567 else if (L && optarg[L-1] == 'h') 586 int L = strlen(optarg);
568 maximum_age = atoi (optarg) * 60 * 60; 587 if (L && optarg[L - 1] == 'm') {
569 else if (L && optarg[L-1] == 'd') 588 maximum_age = atoi(optarg) * 60;
570 maximum_age = atoi (optarg) * 60 * 60 * 24; 589 } else if (L && optarg[L - 1] == 'h') {
571 else if (L && (optarg[L-1] == 's' || 590 maximum_age = atoi(optarg) * 60 * 60;
572 isdigit (optarg[L-1]))) 591 } else if (L && optarg[L - 1] == 'd') {
573 maximum_age = atoi (optarg); 592 maximum_age = atoi(optarg) * 60 * 60 * 24;
574 else { 593 } else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) {
575 fprintf (stderr, "unparsable max-age: %s\n", optarg); 594 maximum_age = atoi(optarg);
576 exit (STATE_WARNING); 595 } else {
577 } 596 fprintf(stderr, "unparsable max-age: %s\n", optarg);
578 } 597 exit(STATE_WARNING);
579 break; 598 }
580 case 'E': /* show extended perfdata */ 599 } break;
581 show_extended_perfdata = true; 600 case 'E': /* show extended perfdata */
582 break; 601 show_extended_perfdata = true;
583 case 'B': /* print body content after status line */ 602 break;
584 show_body = true; 603 case 'B': /* print body content after status line */
585 break; 604 show_body = true;
586 } 605 break;
587 } 606 }
588 607 }
589 c = optind; 608
590 609 c = optind;
591 if (server_address == NULL && c < argc) 610
592 server_address = strdup (argv[c++]); 611 if (server_address == NULL && c < argc) {
593 612 server_address = strdup(argv[c++]);
594 if (host_name == NULL && c < argc) 613 }
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 614
615 if (host_name == NULL && c < argc) {
616 host_name = strdup(argv[c++]);
617 }
618
619 if (server_address == NULL) {
620 if (host_name == NULL) {
621 usage4(_("You must specify a server address or host name"));
622 } else {
623 server_address = strdup(host_name);
624 }
625 }
624 626
627 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
628
629 if (critical_thresholds && thlds->critical->end > (double)socket_timeout) {
630 socket_timeout = (int)thlds->critical->end + 1;
631 }
632
633 if (http_method == NULL) {
634 http_method = strdup("GET");
635 }
636
637 if (http_method_proxy == NULL) {
638 http_method_proxy = strdup("GET");
639 }
640
641 if (client_cert && !client_privkey) {
642 usage4(_("If you use a client certificate you must also specify a private key file"));
643 }
644
645 if (virtual_port == 0) {
646 virtual_port = server_port;
647 }
648
649 return true;
650}
625 651
626/* Returns 1 if we're done processing the document body; 0 to keep going */ 652/* Returns 1 if we're done processing the document body; 0 to keep going */
627static int 653static int document_headers_done(char *full_page) {
628document_headers_done (char *full_page) 654 const char *body;
629{
630 const char *body;
631 655
632 for (body = full_page; *body; body++) { 656 for (body = full_page; *body; body++) {
633 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3)) 657 if (!strncmp(body, "\n\n", 2) || !strncmp(body, "\n\r\n", 3)) {
634 break; 658 break;
635 } 659 }
660 }
636 661
637 if (!*body) 662 if (!*body) {
638 return 0; /* haven't read end of headers yet */ 663 return 0; /* haven't read end of headers yet */
664 }
639 665
640 full_page[body - full_page] = 0; 666 full_page[body - full_page] = 0;
641 return 1; 667 return 1;
642} 668}
643 669
644static time_t 670static time_t parse_time_string(const char *string) {
645parse_time_string (const char *string) 671 struct tm tm;
646{ 672 time_t t;
647 struct tm tm; 673 memset(&tm, 0, sizeof(tm));
648 time_t t; 674
649 memset (&tm, 0, sizeof(tm)); 675 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
650 676
651 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */ 677 if (isupper(string[0]) && /* Tue */
652 678 islower(string[1]) && islower(string[2]) && ',' == string[3] && ' ' == string[4] &&
653 if (isupper (string[0]) && /* Tue */ 679 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
654 islower (string[1]) && 680 isdigit(string[6]) && ' ' == string[7] && isupper(string[8]) && /* Dec */
655 islower (string[2]) && 681 islower(string[9]) && islower(string[10]) && ' ' == string[11] &&
656 ',' == string[3] && 682 isdigit(string[12]) && /* 2001 */
657 ' ' == string[4] && 683 isdigit(string[13]) && isdigit(string[14]) && isdigit(string[15]) && ' ' == string[16] &&
658 (isdigit(string[5]) || string[5] == ' ') && /* 25 */ 684 isdigit(string[17]) && /* 02: */
659 isdigit (string[6]) && 685 isdigit(string[18]) && ':' == string[19] && isdigit(string[20]) && /* 59: */
660 ' ' == string[7] && 686 isdigit(string[21]) && ':' == string[22] && isdigit(string[23]) && /* 03 */
661 isupper (string[8]) && /* Dec */ 687 isdigit(string[24]) && ' ' == string[25] && 'G' == string[26] && /* GMT */
662 islower (string[9]) && 688 'M' == string[27] && /* GMT */
663 islower (string[10]) && 689 'T' == string[28]) {
664 ' ' == string[11] && 690
665 isdigit (string[12]) && /* 2001 */ 691 tm.tm_sec = 10 * (string[23] - '0') + (string[24] - '0');
666 isdigit (string[13]) && 692 tm.tm_min = 10 * (string[20] - '0') + (string[21] - '0');
667 isdigit (string[14]) && 693 tm.tm_hour = 10 * (string[17] - '0') + (string[18] - '0');
668 isdigit (string[15]) && 694 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5] - '0') + (string[6] - '0');
669 ' ' == string[16] && 695 tm.tm_mon = (!strncmp(string + 8, "Jan", 3) ? 0
670 isdigit (string[17]) && /* 02: */ 696 : !strncmp(string + 8, "Feb", 3) ? 1
671 isdigit (string[18]) && 697 : !strncmp(string + 8, "Mar", 3) ? 2
672 ':' == string[19] && 698 : !strncmp(string + 8, "Apr", 3) ? 3
673 isdigit (string[20]) && /* 59: */ 699 : !strncmp(string + 8, "May", 3) ? 4
674 isdigit (string[21]) && 700 : !strncmp(string + 8, "Jun", 3) ? 5
675 ':' == string[22] && 701 : !strncmp(string + 8, "Jul", 3) ? 6
676 isdigit (string[23]) && /* 03 */ 702 : !strncmp(string + 8, "Aug", 3) ? 7
677 isdigit (string[24]) && 703 : !strncmp(string + 8, "Sep", 3) ? 8
678 ' ' == string[25] && 704 : !strncmp(string + 8, "Oct", 3) ? 9
679 'G' == string[26] && /* GMT */ 705 : !strncmp(string + 8, "Nov", 3) ? 10
680 'M' == string[27] && /* GMT */ 706 : !strncmp(string + 8, "Dec", 3) ? 11
681 'T' == string[28]) { 707 : -1);
682 708 tm.tm_year = ((1000 * (string[12] - '0') + 100 * (string[13] - '0') +
683 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0'); 709 10 * (string[14] - '0') + (string[15] - '0')) -
684 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0'); 710 1900);
685 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0'); 711
686 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0'); 712 tm.tm_isdst = 0; /* GMT is never in DST, right? */
687 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 : 713
688 !strncmp (string+8, "Feb", 3) ? 1 : 714 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) {
689 !strncmp (string+8, "Mar", 3) ? 2 : 715 return 0;
690 !strncmp (string+8, "Apr", 3) ? 3 : 716 }
691 !strncmp (string+8, "May", 3) ? 4 : 717
692 !strncmp (string+8, "Jun", 3) ? 5 : 718 /*
693 !strncmp (string+8, "Jul", 3) ? 6 : 719 This is actually wrong: we need to subtract the local timezone
694 !strncmp (string+8, "Aug", 3) ? 7 : 720 offset from GMT from this value. But, that's ok in this usage,
695 !strncmp (string+8, "Sep", 3) ? 8 : 721 because we only comparing these two GMT dates against each other,
696 !strncmp (string+8, "Oct", 3) ? 9 : 722 so it doesn't matter what time zone we parse them in.
697 !strncmp (string+8, "Nov", 3) ? 10 : 723 */
698 !strncmp (string+8, "Dec", 3) ? 11 : 724
699 -1); 725 t = mktime(&tm);
700 tm.tm_year = ((1000 * (string[12]-'0') + 726 if (t == (time_t)-1) {
701 100 * (string[13]-'0') + 727 t = 0;
702 10 * (string[14]-'0') + 728 }
703 (string[15]-'0')) 729
704 - 1900); 730 if (verbose) {
705 731 const char *s = string;
706 tm.tm_isdst = 0; /* GMT is never in DST, right? */ 732 while (*s && *s != '\r' && *s != '\n') {
707 733 fputc(*s++, stdout);
708 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) 734 }
709 return 0; 735 printf(" ==> %lu\n", (unsigned long)t);
710 736 }
711 /* 737
712 This is actually wrong: we need to subtract the local timezone 738 return t;
713 offset from GMT from this value. But, that's ok in this usage, 739 }
714 because we only comparing these two GMT dates against each other, 740 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} 741}
734 742
735/* Checks if the server 'reply' is one of the expected 'statuscodes' */ 743/* Checks if the server 'reply' is one of the expected 'statuscodes' */
736static int 744static int expected_statuscode(const char *reply, const char *statuscodes) {
737expected_statuscode (const char *reply, const char *statuscodes) 745 char *expected;
738{ 746 char *code;
739 char *expected, *code; 747 int result = 0;
740 int result = 0; 748
741 749 if ((expected = strdup(statuscodes)) == NULL) {
742 if ((expected = strdup (statuscodes)) == NULL) 750 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
743 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); 751 }
744 752
745 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ",")) 753 for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) {
746 if (strstr (reply, code) != NULL) { 754 if (strstr(reply, code) != NULL) {
747 result = 1; 755 result = 1;
748 break; 756 break;
749 } 757 }
750 758 }
751 free (expected); 759
752 return result; 760 free(expected);
761 return result;
753} 762}
754 763
755static int 764static int check_document_dates(const char *headers, char **msg) {
756check_document_dates (const char *headers, char **msg) 765 const char *s;
757{ 766 char *server_date = 0;
758 const char *s; 767 char *document_date = 0;
759 char *server_date = 0; 768 int date_result = STATE_OK;
760 char *document_date = 0; 769
761 int date_result = STATE_OK; 770 s = headers;
762 771 while (*s) {
763 s = headers; 772 const char *field = s;
764 while (*s) { 773 const char *value = 0;
765 const char *field = s; 774
766 const char *value = 0; 775 /* Find the end of the header field */
767 776 while (*s && !isspace(*s) && *s != ':') {
768 /* Find the end of the header field */ 777 s++;
769 while (*s && !isspace(*s) && *s != ':') 778 }
770 s++; 779
771 780 /* Remember the header value, if any. */
772 /* Remember the header value, if any. */ 781 if (*s == ':') {
773 if (*s == ':') 782 value = ++s;
774 value = ++s; 783 }
775 784
776 /* Skip to the end of the header, including continuation lines. */ 785 /* Skip to the end of the header, including continuation lines. */
777 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) 786 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) {
778 s++; 787 s++;
779 788 }
780 /* Avoid stepping over end-of-string marker */ 789
781 if (*s) 790 /* Avoid stepping over end-of-string marker */
782 s++; 791 if (*s) {
783 792 s++;
784 /* Process this header. */ 793 }
785 if (value && value > field+2) { 794
786 char *ff = (char *) malloc (value-field); 795 /* Process this header. */
787 char *ss = ff; 796 if (value && value > field + 2) {
788 while (field < value-1) 797 char *ff = (char *)malloc(value - field);
789 *ss++ = tolower(*field++); 798 char *ss = ff;
790 *ss++ = 0; 799 while (field < value - 1) {
791 800 *ss++ = tolower(*field++);
792 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) { 801 }
793 const char *e; 802 *ss++ = 0;
794 while (*value && isspace (*value)) 803
795 value++; 804 if (!strcmp(ff, "date") || !strcmp(ff, "last-modified")) {
796 for (e = value; *e && *e != '\r' && *e != '\n'; e++) 805 const char *e;
797 ; 806 while (*value && isspace(*value)) {
798 ss = (char *) malloc (e - value + 1); 807 value++;
799 strncpy (ss, value, e - value); 808 }
800 ss[e - value] = 0; 809 for (e = value; *e && *e != '\r' && *e != '\n'; e++) {
801 if (!strcmp (ff, "date")) { 810 ;
802 if (server_date) free (server_date); 811 }
803 server_date = ss; 812 ss = (char *)malloc(e - value + 1);
804 } else { 813 strncpy(ss, value, e - value);
805 if (document_date) free (document_date); 814 ss[e - value] = 0;
806 document_date = ss; 815 if (!strcmp(ff, "date")) {
807 } 816 if (server_date) {
808 } 817 free(server_date);
809 free (ff); 818 }
810 } 819 server_date = ss;
811 } 820 } else {
812 821 if (document_date) {
813 /* Done parsing the body. Now check the dates we (hopefully) parsed. */ 822 free(document_date);
814 if (!server_date || !*server_date) { 823 }
815 xasprintf (msg, _("%sServer date unknown, "), *msg); 824 document_date = ss;
816 date_result = max_state_alt(STATE_UNKNOWN, date_result); 825 }
817 } else if (!document_date || !*document_date) { 826 }
818 xasprintf (msg, _("%sDocument modification date unknown, "), *msg); 827 free(ff);
819 date_result = max_state_alt(STATE_CRITICAL, date_result); 828 }
820 } else { 829 }
821 time_t srv_data = parse_time_string (server_date); 830
822 time_t doc_data = parse_time_string (document_date); 831 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
823 832 if (!server_date || !*server_date) {
824 if (srv_data <= 0) { 833 xasprintf(msg, _("%sServer date unknown, "), *msg);
825 xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); 834 date_result = max_state_alt(STATE_UNKNOWN, date_result);
826 date_result = max_state_alt(STATE_CRITICAL, date_result); 835 } else if (!document_date || !*document_date) {
827 } else if (doc_data <= 0) { 836 xasprintf(msg, _("%sDocument modification date unknown, "), *msg);
828 xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); 837 date_result = max_state_alt(STATE_CRITICAL, date_result);
829 date_result = max_state_alt(STATE_CRITICAL, date_result); 838 } else {
830 } else if (doc_data > srv_data + 30) { 839 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); 840 time_t doc_data = parse_time_string(document_date);
832 date_result = max_state_alt(STATE_CRITICAL, date_result); 841
833 } else if (doc_data < srv_data - maximum_age) { 842 if (srv_data <= 0) {
834 int n = (srv_data - doc_data); 843 xasprintf(msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
835 if (n > (60 * 60 * 24 * 2)) { 844 date_result = max_state_alt(STATE_CRITICAL, date_result);
836 xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24)); 845 } else if (doc_data <= 0) {
837 date_result = max_state_alt(STATE_CRITICAL, date_result); 846 xasprintf(msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
838 } else { 847 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); 848 } else if (doc_data > srv_data + 30) {
840 date_result = max_state_alt(STATE_CRITICAL, date_result); 849 xasprintf(msg, _("%sDocument is %d seconds in the future, "), *msg,
841 } 850 (int)doc_data - (int)srv_data);
842 } 851 date_result = max_state_alt(STATE_CRITICAL, date_result);
843 free (server_date); 852 } else if (doc_data < srv_data - maximum_age) {
844 free (document_date); 853 int n = (srv_data - doc_data);
845 } 854 if (n > (60 * 60 * 24 * 2)) {
846 return date_result; 855 xasprintf(msg, _("%sLast modified %.1f days ago, "), *msg,
856 ((float)n) / (60 * 60 * 24));
857 date_result = max_state_alt(STATE_CRITICAL, date_result);
858 } else {
859 xasprintf(msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60),
860 (n / 60) % 60, n % 60);
861 date_result = max_state_alt(STATE_CRITICAL, date_result);
862 }
863 }
864 free(server_date);
865 free(document_date);
866 }
867 return date_result;
847} 868}
848 869
849int 870int get_content_length(const char *headers) {
850get_content_length (const char *headers) 871 const char *s;
851{ 872 int content_length = 0;
852 const char *s; 873
853 int content_length = 0; 874 s = headers;
854 875 while (*s) {
855 s = headers; 876 const char *field = s;
856 while (*s) { 877 const char *value = 0;
857 const char *field = s; 878
858 const char *value = 0; 879 /* Find the end of the header field */
859 880 while (*s && !isspace(*s) && *s != ':') {
860 /* Find the end of the header field */ 881 s++;
861 while (*s && !isspace(*s) && *s != ':') 882 }
862 s++; 883
863 884 /* Remember the header value, if any. */
864 /* Remember the header value, if any. */ 885 if (*s == ':') {
865 if (*s == ':') 886 value = ++s;
866 value = ++s; 887 }
867 888
868 /* Skip to the end of the header, including continuation lines. */ 889 /* Skip to the end of the header, including continuation lines. */
869 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) 890 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) {
870 s++; 891 s++;
871 892 }
872 /* Avoid stepping over end-of-string marker */ 893
873 if (*s) 894 /* Avoid stepping over end-of-string marker */
874 s++; 895 if (*s) {
875 896 s++;
876 /* Process this header. */ 897 }
877 if (value && value > field+2) { 898
878 char *ff = (char *) malloc (value-field); 899 /* Process this header. */
879 char *ss = ff; 900 if (value && value > field + 2) {
880 while (field < value-1) 901 char *ff = (char *)malloc(value - field);
881 *ss++ = tolower(*field++); 902 char *ss = ff;
882 *ss++ = 0; 903 while (field < value - 1) {
883 904 *ss++ = tolower(*field++);
884 if (!strcmp (ff, "content-length")) { 905 }
885 const char *e; 906 *ss++ = 0;
886 while (*value && isspace (*value)) 907
887 value++; 908 if (!strcmp(ff, "content-length")) {
888 for (e = value; *e && *e != '\r' && *e != '\n'; e++) 909 const char *e;
889 ; 910 while (*value && isspace(*value)) {
890 ss = (char *) malloc (e - value + 1); 911 value++;
891 strncpy (ss, value, e - value); 912 }
892 ss[e - value] = 0; 913 for (e = value; *e && *e != '\r' && *e != '\n'; e++) {
893 content_length = atoi(ss); 914 ;
894 free (ss); 915 }
895 } 916 ss = (char *)malloc(e - value + 1);
896 free (ff); 917 strncpy(ss, value, e - value);
897 } 918 ss[e - value] = 0;
898 } 919 content_length = atoi(ss);
899 return (content_length); 920 free(ss);
921 }
922 free(ff);
923 }
924 }
925 return (content_length);
900} 926}
901 927
902char * 928char *prepend_slash(char *path) {
903prepend_slash (char *path) 929 char *newpath;
904{
905 char *newpath;
906 930
907 if (path[0] == '/') 931 if (path[0] == '/') {
908 return path; 932 return path;
933 }
909 934
910 if ((newpath = malloc (strlen(path) + 2)) == NULL) 935 if ((newpath = malloc(strlen(path) + 2)) == NULL) {
911 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); 936 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
912 newpath[0] = '/'; 937 }
913 strcpy (newpath + 1, path); 938 newpath[0] = '/';
914 free (path); 939 strcpy(newpath + 1, path);
915 return newpath; 940 free(path);
941 return newpath;
916} 942}
917 943
918int 944int check_http(void) {
919check_http (void) 945 char *msg;
920{ 946 char *status_line;
921 char *msg; 947 char *status_code;
922 char *status_line; 948 char *header;
923 char *status_code; 949 char *page;
924 char *header; 950 char *auth;
925 char *page; 951 int http_status;
926 char *auth; 952 int i = 0;
927 int http_status; 953 size_t pagesize = 0;
928 int i = 0; 954 char *full_page;
929 size_t pagesize = 0; 955 char *full_page_new;
930 char *full_page; 956 char *buf;
931 char *full_page_new; 957 char *pos;
932 char *buf; 958 long microsec = 0L;
933 char *pos; 959 double elapsed_time = 0.0;
934 long microsec = 0L; 960 long microsec_connect = 0L;
935 double elapsed_time = 0.0; 961 double elapsed_time_connect = 0.0;
936 long microsec_connect = 0L; 962 long microsec_ssl = 0L;
937 double elapsed_time_connect = 0.0; 963 double elapsed_time_ssl = 0.0;
938 long microsec_ssl = 0L; 964 long microsec_firstbyte = 0L;
939 double elapsed_time_ssl = 0.0; 965 double elapsed_time_firstbyte = 0.0;
940 long microsec_firstbyte = 0L; 966 long microsec_headers = 0L;
941 double elapsed_time_firstbyte = 0.0; 967 double elapsed_time_headers = 0.0;
942 long microsec_headers = 0L; 968 long microsec_transfer = 0L;
943 double elapsed_time_headers = 0.0; 969 double elapsed_time_transfer = 0.0;
944 long microsec_transfer = 0L; 970 int page_len = 0;
945 double elapsed_time_transfer = 0.0; 971 int result = STATE_OK;
946 int page_len = 0; 972 char *force_host_header = NULL;
947 int result = STATE_OK; 973
948 char *force_host_header = NULL; 974 /* try to connect to the host at the given port number */
949 975 gettimeofday(&tv_temp, NULL);
950 /* try to connect to the host at the given port number */ 976 if (my_tcp_connect(server_address, server_port, &sd) != STATE_OK) {
951 gettimeofday (&tv_temp, NULL); 977 die(STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
952 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) 978 }
953 die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n")); 979 microsec_connect = deltime(tv_temp);
954 microsec_connect = deltime (tv_temp); 980
955 981 /* 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 */ 982 /* 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*/ 983 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */
958 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */ 984
959 985 if (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && host_name != NULL &&
960 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 986 use_ssl) {
961 && host_name != NULL && use_ssl == true) { 987
962 988 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); 989 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); 990 server_port, host_name, HTTPS_PORT);
965 if (strlen(proxy_auth)) { 991 }
966 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth); 992 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); 993 user_agent);
968 } 994 if (strlen(proxy_auth)) {
969 /* optionally send any other header tag */ 995 base64_encode_alloc(proxy_auth, strlen(proxy_auth), &auth);
970 if (http_opt_headers_count) { 996 xasprintf(&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
971 for (i = 0; i < http_opt_headers_count ; i++) { 997 }
972 if (force_host_header != http_opt_headers[i]) { 998 /* optionally send any other header tag */
973 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]); 999 if (http_opt_headers_count) {
974 } 1000 for (i = 0; i < http_opt_headers_count; i++) {
975 } 1001 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 */ 1002 xasprintf(&buf, "%s%s\r\n", buf, http_opt_headers[i]);
977 /* Covered in a testcase in tests/check_http.t */ 1003 }
978 /* free(http_opt_headers); */ 1004 }
979 } 1005 /* 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); 1006 * segfault */
981 asprintf (&buf, "%sHost: %s\r\n", buf, host_name); 1007 /* Covered in a testcase in tests/check_http.t */
982 /* we finished our request, send empty line with CRLF */ 1008 /* free(http_opt_headers); */
983 asprintf (&buf, "%s%s", buf, CRLF); 1009 }
984 if (verbose) printf ("%s\n", buf); 1010 asprintf(&buf, "%sProxy-Connection: keep-alive\r\n", buf);
985 send(sd, buf, strlen (buf), 0); 1011 asprintf(&buf, "%sHost: %s\r\n", buf, host_name);
986 buf[0]='\0'; 1012 /* we finished our request, send empty line with CRLF */
987 1013 asprintf(&buf, "%s%s", buf, CRLF);
988 if (verbose) printf ("Receive response from proxy\n"); 1014 if (verbose) {
989 read (sd, buffer, MAX_INPUT_BUFFER-1); 1015 printf("%s\n", buf);
990 if (verbose) printf ("%s", buffer); 1016 }
991 /* Here we should check if we got HTTP/1.1 200 Connection established */ 1017 send(sd, buf, strlen(buf), 0);
992 } 1018 buf[0] = '\0';
1019
1020 if (verbose) {
1021 printf("Receive response from proxy\n");
1022 }
1023 read(sd, buffer, MAX_INPUT_BUFFER - 1);
1024 if (verbose) {
1025 printf("%s", buffer);
1026 }
1027 /* Here we should check if we got HTTP/1.1 200 Connection established */
1028 }
993#ifdef HAVE_SSL 1029#ifdef HAVE_SSL
994 elapsed_time_connect = (double)microsec_connect / 1.0e6; 1030 elapsed_time_connect = (double)microsec_connect / 1.0e6;
995 if (use_ssl == true) { 1031 if (use_ssl) {
996 gettimeofday (&tv_temp, NULL); 1032 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); 1033 result = np_net_ssl_init_with_hostname_version_and_cert(
998 if (verbose) printf ("SSL initialized\n"); 1034 sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
999 if (result != STATE_OK) 1035 if (verbose) {
1000 die (STATE_CRITICAL, NULL); 1036 printf("SSL initialized\n");
1001 microsec_ssl = deltime (tv_temp); 1037 }
1002 elapsed_time_ssl = (double)microsec_ssl / 1.0e6; 1038 if (result != STATE_OK) {
1003 if (check_cert == true) { 1039 die(STATE_CRITICAL, NULL);
1004 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 1040 }
1005 if (continue_after_check_cert == false) { 1041 microsec_ssl = deltime(tv_temp);
1006 if (sd) close(sd); 1042 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
1007 np_net_ssl_cleanup(); 1043 if (check_cert) {
1008 return result; 1044 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
1009 } 1045 if (!continue_after_check_cert) {
1010 } 1046 if (sd) {
1011 } 1047 close(sd);
1048 }
1049 np_net_ssl_cleanup();
1050 return result;
1051 }
1052 }
1053 }
1012#endif /* HAVE_SSL */ 1054#endif /* HAVE_SSL */
1013 1055
1014 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 1056 if (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && host_name != NULL &&
1015 && host_name != NULL && use_ssl == true) 1057 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); 1058 asprintf(&buf, "%s %s %s\r\n%s\r\n", http_method_proxy, server_url,
1017 else 1059 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); 1060 } else {
1019 1061 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 */ 1062 host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1021 xasprintf (&buf, "%sConnection: close\r\n", buf); 1063 }
1022 1064
1023 /* check if Host header is explicitly set in options */ 1065 /* tell HTTP/1.1 servers not to keep the connection alive */
1024 if (http_opt_headers_count) { 1066 xasprintf(&buf, "%sConnection: close\r\n", buf);
1025 for (i = 0; i < http_opt_headers_count ; i++) { 1067
1026 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) { 1068 /* check if Host header is explicitly set in options */
1027 force_host_header = http_opt_headers[i]; 1069 if (http_opt_headers_count) {
1028 } 1070 for (i = 0; i < http_opt_headers_count; i++) {
1029 } 1071 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
1030 } 1072 force_host_header = http_opt_headers[i];
1031 1073 }
1032 /* optionally send the host header info */ 1074 }
1033 if (host_name) { 1075 }
1034 if (force_host_header) { 1076
1035 xasprintf (&buf, "%s%s\r\n", buf, force_host_header); 1077 /* optionally send the host header info */
1036 } 1078 if (host_name) {
1037 else { 1079 if (force_host_header) {
1038 /* 1080 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, 1081 } else {
1040 * 14.23). Some server applications/configurations cause trouble if the 1082 /*
1041 * (default) port is explicitly specified in the "Host:" header line. 1083 * Specify the port only if we're using a non-default port (see RFC 2616,
1042 */ 1084 * 14.23). Some server applications/configurations cause trouble if the
1043 if ((use_ssl == false && virtual_port == HTTP_PORT) || 1085 * (default) port is explicitly specified in the "Host:" header line.
1044 (use_ssl == true && virtual_port == HTTPS_PORT) || 1086 */
1045 (server_address != NULL && strcmp(http_method, "CONNECT") == 0 1087 if ((!use_ssl && virtual_port == HTTP_PORT) ||
1046 && host_name != NULL && use_ssl == true)) 1088 (use_ssl && virtual_port == HTTPS_PORT) ||
1047 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name); 1089 (server_address != NULL && strcmp(http_method, "CONNECT") == 0 &&
1048 else 1090 host_name != NULL && use_ssl)) {
1049 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port); 1091 xasprintf(&buf, "%sHost: %s\r\n", buf, host_name);
1050 } 1092 } else {
1051 } 1093 xasprintf(&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port);
1052 1094 }
1053 /* optionally send any other header tag */ 1095 }
1054 if (http_opt_headers_count) { 1096 }
1055 for (i = 0; i < http_opt_headers_count ; i++) { 1097
1056 if (force_host_header != http_opt_headers[i]) { 1098 /* optionally send any other header tag */
1057 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]); 1099 if (http_opt_headers_count) {
1058 } 1100 for (i = 0; i < http_opt_headers_count; i++) {
1059 } 1101 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 */ 1102 xasprintf(&buf, "%s%s\r\n", buf, http_opt_headers[i]);
1061 /* Covered in a testcase in tests/check_http.t */ 1103 }
1062 /* free(http_opt_headers); */ 1104 }
1063 } 1105 /* This cannot be free'd here because a redirection will then try to access this and
1064 1106 * segfault */
1065 /* optionally send the authentication info */ 1107 /* Covered in a testcase in tests/check_http.t */
1066 if (strlen(user_auth)) { 1108 /* free(http_opt_headers); */
1067 base64_encode_alloc (user_auth, strlen (user_auth), &auth); 1109 }
1068 xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth); 1110
1069 } 1111 /* optionally send the authentication info */
1070 1112 if (strlen(user_auth)) {
1071 /* optionally send the proxy authentication info */ 1113 base64_encode_alloc(user_auth, strlen(user_auth), &auth);
1072 if (strlen(proxy_auth)) { 1114 xasprintf(&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
1073 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth); 1115 }
1074 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); 1116
1075 } 1117 /* optionally send the proxy authentication info */
1076 1118 if (strlen(proxy_auth)) {
1077 /* either send http POST data (any data, not only POST)*/ 1119 base64_encode_alloc(proxy_auth, strlen(proxy_auth), &auth);
1078 if (http_post_data) { 1120 xasprintf(&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
1079 if (http_content_type) { 1121 }
1080 xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type); 1122
1081 } else { 1123 /* either send http POST data (any data, not only POST)*/
1082 xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf); 1124 if (http_post_data) {
1083 } 1125 if (http_content_type) {
1084 1126 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)); 1127 } else {
1086 xasprintf (&buf, "%s%s", buf, http_post_data); 1128 xasprintf(&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
1087 } else { 1129 }
1088 /* or just a newline so the server knows we're done with the request */ 1130
1089 xasprintf (&buf, "%s%s", buf, CRLF); 1131 xasprintf(&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen(http_post_data));
1090 } 1132 xasprintf(&buf, "%s%s", buf, http_post_data);
1091 1133 } else {
1092 if (verbose) printf ("%s\n", buf); 1134 /* or just a newline so the server knows we're done with the request */
1093 gettimeofday (&tv_temp, NULL); 1135 xasprintf(&buf, "%s%s", buf, CRLF);
1094 my_send (buf, strlen (buf)); 1136 }
1095 microsec_headers = deltime (tv_temp); 1137
1096 elapsed_time_headers = (double)microsec_headers / 1.0e6; 1138 if (verbose) {
1097 1139 printf("%s\n", buf);
1098 /* fetch the page */ 1140 }
1099 full_page = strdup(""); 1141 gettimeofday(&tv_temp, NULL);
1100 gettimeofday (&tv_temp, NULL); 1142 my_send(buf, strlen(buf));
1101 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) { 1143 microsec_headers = deltime(tv_temp);
1102 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) { 1144 elapsed_time_headers = (double)microsec_headers / 1.0e6;
1103 microsec_firstbyte = deltime (tv_temp); 1145
1104 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6; 1146 /* fetch the page */
1105 } 1147 full_page = strdup("");
1106 while ((pos = memchr(buffer, '\0', i))) { 1148 gettimeofday(&tv_temp, NULL);
1107 /* replace nul character with a blank */ 1149 while ((i = my_recv(buffer, MAX_INPUT_BUFFER - 1)) > 0) {
1108 *pos = ' '; 1150 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) {
1109 } 1151 microsec_firstbyte = deltime(tv_temp);
1110 buffer[i] = '\0'; 1152 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6;
1111 1153 }
1112 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL) 1154 while ((pos = memchr(buffer, '\0', i))) {
1113 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n")); 1155 /* replace nul character with a blank */
1114 1156 *pos = ' ';
1115 memmove(&full_page_new[pagesize], buffer, i + 1); 1157 }
1116 1158 buffer[i] = '\0';
1117 full_page = full_page_new; 1159
1118 1160 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL) {
1119 pagesize += i; 1161 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1120 1162 }
1121 if (no_body && document_headers_done (full_page)) { 1163
1122 i = 0; 1164 memmove(&full_page_new[pagesize], buffer, i + 1);
1123 break; 1165
1124 } 1166 full_page = full_page_new;
1125 } 1167
1126 microsec_transfer = deltime (tv_temp); 1168 pagesize += i;
1127 elapsed_time_transfer = (double)microsec_transfer / 1.0e6; 1169
1128 1170 if (no_body && document_headers_done(full_page)) {
1129 if (i < 0 && errno != ECONNRESET) { 1171 i = 0;
1130 die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n")); 1172 break;
1131 } 1173 }
1132 1174 }
1133 /* return a CRITICAL status if we couldn't read any data */ 1175 microsec_transfer = deltime(tv_temp);
1134 if (pagesize == (size_t) 0) 1176 elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
1135 die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n")); 1177
1136 1178 if (i < 0 && errno != ECONNRESET) {
1137 /* close the connection */ 1179 die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1138 if (sd) close(sd); 1180 }
1181
1182 /* return a CRITICAL status if we couldn't read any data */
1183 if (pagesize == (size_t)0) {
1184 die(STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
1185 }
1186
1187 /* close the connection */
1188 if (sd) {
1189 close(sd);
1190 }
1139#ifdef HAVE_SSL 1191#ifdef HAVE_SSL
1140 np_net_ssl_cleanup(); 1192 np_net_ssl_cleanup();
1141#endif 1193#endif
1142 1194
1143 /* Save check time */ 1195 /* Save check time */
1144 microsec = deltime (tv); 1196 microsec = deltime(tv);
1145 elapsed_time = (double)microsec / 1.0e6; 1197 elapsed_time = (double)microsec / 1.0e6;
1146 1198
1147 /* leave full_page untouched so we can free it later */ 1199 /* leave full_page untouched so we can free it later */
1148 page = full_page; 1200 page = full_page;
1149 1201
1150 if (verbose) 1202 if (verbose) {
1151 printf ("%s://%s:%d%s is %d characters\n", 1203 printf("%s://%s:%d%s is %d characters\n", use_ssl ? "https" : "http", server_address,
1152 use_ssl ? "https" : "http", server_address, 1204 server_port, server_url, (int)pagesize);
1153 server_port, server_url, (int)pagesize); 1205 }
1154 1206
1155 /* find status line and null-terminate it */ 1207 /* find status line and null-terminate it */
1156 status_line = page; 1208 status_line = page;
1157 page += (size_t) strcspn (page, "\r\n"); 1209 page += (size_t)strcspn(page, "\r\n");
1158 pos = page; 1210 pos = page;
1159 page += (size_t) strspn (page, "\r\n"); 1211 page += (size_t)strspn(page, "\r\n");
1160 status_line[strcspn(status_line, "\r\n")] = 0; 1212 status_line[strcspn(status_line, "\r\n")] = 0;
1161 strip (status_line); 1213 strip(status_line);
1162 if (verbose) 1214 if (verbose) {
1163 printf ("STATUS: %s\n", status_line); 1215 printf("STATUS: %s\n", status_line);
1164 1216 }
1165 /* find header info and null-terminate it */ 1217
1166 header = page; 1218 /* find header info and null-terminate it */
1167 while (strcspn (page, "\r\n") > 0) { 1219 header = page;
1168 page += (size_t) strcspn (page, "\r\n"); 1220 while (strcspn(page, "\r\n") > 0) {
1169 pos = page; 1221 page += (size_t)strcspn(page, "\r\n");
1170 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) || 1222 pos = page;
1171 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2)) 1223 if ((strspn(page, "\r") == 1 && strspn(page, "\r\n") >= 2) ||
1172 page += (size_t) 2; 1224 (strspn(page, "\n") == 1 && strspn(page, "\r\n") >= 2)) {
1173 else 1225 page += (size_t)2;
1174 page += (size_t) 1; 1226 } else {
1175 } 1227 page += (size_t)1;
1176 page += (size_t) strspn (page, "\r\n"); 1228 }
1177 header[pos - header] = 0; 1229 }
1178 if (verbose) 1230 page += (size_t)strspn(page, "\r\n");
1179 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, 1231 header[pos - header] = 0;
1180 (no_body ? " [[ skipped ]]" : page)); 1232 if (verbose) {
1181 1233 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
1182 /* make sure the status line matches the response we are looking for */ 1234 (no_body ? " [[ skipped ]]" : page));
1183 if (!expected_statuscode (status_line, server_expect)) { 1235 }
1184 if (server_port == HTTP_PORT) 1236
1185 xasprintf (&msg, 1237 /* make sure the status line matches the response we are looking for */
1186 _("Invalid HTTP response received from host: %s\n"), 1238 if (!expected_statuscode(status_line, server_expect)) {
1187 status_line); 1239 if (server_port == HTTP_PORT) {
1188 else 1240 xasprintf(&msg, _("Invalid HTTP response received from host: %s\n"), status_line);
1189 xasprintf (&msg, 1241 } else {
1190 _("Invalid HTTP response received from host on port %d: %s\n"), 1242 xasprintf(&msg, _("Invalid HTTP response received from host on port %d: %s\n"),
1191 server_port, status_line); 1243 server_port, status_line);
1192 if (show_body) 1244 }
1193 xasprintf (&msg, _("%s\n%s"), msg, page); 1245 if (show_body) {
1194 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg); 1246 xasprintf(&msg, _("%s\n%s"), msg, page);
1195 } 1247 }
1196 1248 die(STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
1197 /* Bypass normal status line check if server_expect was set by user and not default */ 1249 }
1198 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */ 1250
1199 if ( server_expect_yn ) { 1251 /* Bypass normal status line check if server_expect was set by user and not default */
1200 xasprintf (&msg, 1252 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
1201 _("Status line output matched \"%s\" - "), server_expect); 1253 if (server_expect_yn) {
1202 if (verbose) 1254 xasprintf(&msg, _("Status line output matched \"%s\" - "), server_expect);
1203 printf ("%s\n",msg); 1255 if (verbose) {
1204 } 1256 printf("%s\n", msg);
1205 else { 1257 }
1206 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ 1258 } else {
1207 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */ 1259 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1208 /* Status-Code = 3 DIGITS */ 1260 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1209 1261 /* Status-Code = 3 DIGITS */
1210 status_code = strchr (status_line, ' ') + sizeof (char); 1262
1211 if (strspn (status_code, "1234567890") != 3) 1263 status_code = strchr(status_line, ' ') + sizeof(char);
1212 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line); 1264 if (strspn(status_code, "1234567890") != 3) {
1213 1265 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
1214 http_status = atoi (status_code); 1266 }
1215 1267
1216 /* check the return code */ 1268 http_status = atoi(status_code);
1217 1269
1218 if (http_status >= 600 || http_status < 100) { 1270 /* check the return code */
1219 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line); 1271
1220 } 1272 if (http_status >= 600 || http_status < 100) {
1221 /* server errors result in a critical state */ 1273 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
1222 else if (http_status >= 500) { 1274 }
1223 xasprintf (&msg, _("%s - "), status_line); 1275 /* server errors result in a critical state */
1224 result = STATE_CRITICAL; 1276 else if (http_status >= 500) {
1225 } 1277 xasprintf(&msg, _("%s - "), status_line);
1226 /* client errors result in a warning state */ 1278 result = STATE_CRITICAL;
1227 else if (http_status >= 400) { 1279 }
1228 xasprintf (&msg, _("%s - "), status_line); 1280 /* client errors result in a warning state */
1229 result = max_state_alt(STATE_WARNING, result); 1281 else if (http_status >= 400) {
1230 } 1282 xasprintf(&msg, _("%s - "), status_line);
1231 /* check redirected page if specified */ 1283 result = max_state_alt(STATE_WARNING, result);
1232 else if (http_status >= 300) { 1284 }
1233 1285 /* check redirected page if specified */
1234 if (onredirect == STATE_DEPENDENT) 1286 else if (http_status >= 300) {
1235 redir (header, status_line); 1287
1236 else 1288 if (onredirect == STATE_DEPENDENT) {
1237 result = max_state_alt(onredirect, result); 1289 redir(header, status_line);
1238 xasprintf (&msg, _("%s - "), status_line); 1290 } else {
1239 } /* end if (http_status >= 300) */ 1291 result = max_state_alt(onredirect, result);
1240 else { 1292 }
1241 /* Print OK status anyway */ 1293 xasprintf(&msg, _("%s - "), status_line);
1242 xasprintf (&msg, _("%s - "), status_line); 1294 } /* end if (http_status >= 300) */
1243 } 1295 else {
1244 1296 /* Print OK status anyway */
1245 } /* end else (server_expect_yn) */ 1297 xasprintf(&msg, _("%s - "), status_line);
1246 1298 }
1247 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */ 1299
1248 alarm (0); 1300 } /* end else (server_expect_yn) */
1249 1301
1250 if (maximum_age >= 0) { 1302 /* 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); 1303 alarm(0);
1252 } 1304
1253 1305 if (maximum_age >= 0) {
1254 /* Page and Header content checks go here */ 1306 result = max_state_alt(check_document_dates(header, &msg), result);
1255 if (strlen(header_expect) > 0) { 1307 }
1256 if (strstr(header, header_expect) == NULL) { 1308
1257 // We did not find the header, the rest is for building the output and setting the state 1309 /* Page and Header content checks go here */
1258 char output_header_search[30] = ""; 1310 if (strlen(header_expect) > 0) {
1259 1311 if (strstr(header, header_expect) == NULL) {
1260 strncpy(&output_header_search[0], header_expect, 1312 // We did not find the header, the rest is for building the output and setting the state
1261 sizeof(output_header_search)); 1313 char output_header_search[30] = "";
1262 1314
1263 if (output_header_search[sizeof(output_header_search) - 1] != '\0') { 1315 strncpy(&output_header_search[0], header_expect, sizeof(output_header_search));
1264 bcopy("...", 1316
1265 &output_header_search[sizeof(output_header_search) - 4], 1317 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1266 4); 1318 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
1267 } 1319 }
1268 1320
1269 xasprintf (&msg, 1321 xasprintf(&msg, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg,
1270 _("%sheader '%s' not found on '%s://%s:%d%s', "), 1322 output_header_search, use_ssl ? "https" : "http",
1271 msg, 1323 host_name ? host_name : server_address, server_port, server_url);
1272 output_header_search, use_ssl ? "https" : "http", 1324
1273 host_name ? host_name : server_address, server_port, 1325 result = STATE_CRITICAL;
1274 server_url); 1326 }
1275 1327 }
1276 result = STATE_CRITICAL; 1328
1277 } 1329 // At this point we should test if the content is chunked and unchunk it, so
1278 } 1330 // it can be searched (and possibly printed)
1279 1331 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 1332 regex_t chunked_header_regex;
1281 // it can be searched (and possibly printed) 1333
1282 const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *"; 1334 if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) {
1283 regex_t chunked_header_regex; 1335 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN),
1284 1336 "Failed to compile chunked_header_regex regex");
1285 if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) { 1337 }
1286 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to compile chunked_header_regex regex"); 1338
1287 } 1339 regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF
1288 1340 // 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 1341
1290 1342 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) { 1343 if (verbose) {
1292 if (verbose) { 1344 printf("Found chunked content\n");
1293 printf("Found chunked content\n"); 1345 }
1294 } 1346 // We actually found the chunked header
1295 // We actually found the chunked header 1347 char *tmp = unchunk_content(page);
1296 char *tmp = unchunk_content(page); 1348 if (tmp == NULL) {
1297 if (tmp == NULL) { 1349 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"); 1350 "Failed to unchunk message body");
1299 } 1351 }
1300 page = tmp; 1352 page = tmp;
1301 } 1353 }
1302 1354
1303 if (strlen(string_expect) > 0) { 1355 if (strlen(string_expect) > 0) {
1304 if (!strstr(page, string_expect)) { 1356 if (!strstr(page, string_expect)) {
1305 // We found the string the body, the rest is for building the output 1357 // We found the string the body, the rest is for building the output
1306 char output_string_search[30] = ""; 1358 char output_string_search[30] = "";
1307 strncpy(&output_string_search[0], string_expect, 1359 strncpy(&output_string_search[0], string_expect, sizeof(output_string_search));
1308 sizeof(output_string_search)); 1360 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
1309 if (output_string_search[sizeof(output_string_search) - 1] != '\0') { 1361 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
1310 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 1362 }
1311 4); 1363 xasprintf(&msg, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg,
1312 } 1364 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); 1365 host_name ? host_name : server_address, server_port, server_url);
1314 result = STATE_CRITICAL; 1366 result = STATE_CRITICAL;
1315 } 1367 }
1316 } 1368 }
1317 1369
1318 if (strlen(regexp) > 0) { 1370 if (strlen(regexp) > 0) {
1319 errcode = regexec(&preg, page, REGS, pmatch, 0); 1371 errcode = regexec(&preg, page, REGS, pmatch, 0);
1320 if ((errcode == 0 && invert_regex == 0) || 1372 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1321 (errcode == REG_NOMATCH && invert_regex == 1)) { 1373 /* OK - No-op to avoid changing the logic around it */
1322 /* OK - No-op to avoid changing the logic around it */ 1374 result = max_state_alt(STATE_OK, result);
1323 result = max_state_alt(STATE_OK, result); 1375 } else if ((errcode == REG_NOMATCH && invert_regex == 0) ||
1324 } 1376 (errcode == 0 && invert_regex == 1)) {
1325 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) { 1377 if (invert_regex == 0) {
1326 if (invert_regex == 0) 1378 xasprintf(&msg, _("%spattern not found, "), msg);
1327 xasprintf (&msg, _("%spattern not found, "), msg); 1379 } else {
1328 else 1380 xasprintf(&msg, _("%spattern found, "), msg);
1329 xasprintf (&msg, _("%spattern found, "), msg); 1381 }
1330 result = state_regex; 1382 result = state_regex;
1331 } 1383 } else {
1332 else { 1384 /* FIXME: Shouldn't that be UNKNOWN? */
1333 /* FIXME: Shouldn't that be UNKNOWN? */ 1385 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1334 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 1386 xasprintf(&msg, _("%sExecute Error: %s, "), msg, errbuf);
1335 xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf); 1387 result = STATE_CRITICAL;
1336 result = STATE_CRITICAL; 1388 }
1337 } 1389 }
1338 } 1390
1339 1391 /* make sure the page is of an appropriate size */
1340 /* make sure the page is of an appropriate size */ 1392 /* page_len = get_content_length(header); */
1341 /* page_len = get_content_length(header); */ 1393 /* FIXME: Will this work with -N ? IMHO we should use
1342 /* FIXME: Will this work with -N ? IMHO we should use 1394 * 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 1395 * returned pagesize
1344 * returned pagesize 1396 */
1345 */ 1397 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1346 /* FIXME: IIRC pagesize returns headers - shouldn't we make 1398 * it == get_content_length(header) ??
1347 * it == get_content_length(header) ?? 1399 */
1348 */ 1400 page_len = pagesize;
1349 page_len = pagesize; 1401 if ((max_page_len > 0) && (page_len > max_page_len)) {
1350 if ((max_page_len > 0) && (page_len > max_page_len)) { 1402 xasprintf(&msg, _("%spage size %d too large, "), msg, page_len);
1351 xasprintf (&msg, _("%spage size %d too large, "), msg, page_len); 1403 result = max_state_alt(STATE_WARNING, result);
1352 result = max_state_alt(STATE_WARNING, result); 1404 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1353 } else if ((min_page_len > 0) && (page_len < min_page_len)) { 1405 xasprintf(&msg, _("%spage size %d too small, "), msg, page_len);
1354 xasprintf (&msg, _("%spage size %d too small, "), msg, page_len); 1406 result = max_state_alt(STATE_WARNING, result);
1355 result = max_state_alt(STATE_WARNING, result); 1407 }
1356 } 1408
1357 1409 /* Cut-off trailing characters */
1358 /* Cut-off trailing characters */ 1410 if (msg[strlen(msg) - 2] == ',') {
1359 if(msg[strlen(msg)-2] == ',') 1411 msg[strlen(msg) - 2] = '\0';
1360 msg[strlen(msg)-2] = '\0'; 1412 } else {
1361 else 1413 msg[strlen(msg) - 3] = '\0';
1362 msg[strlen(msg)-3] = '\0'; 1414 }
1363 1415
1364 /* check elapsed time */ 1416 /* check elapsed time */
1365 if (show_extended_perfdata) 1417 if (show_extended_perfdata) {
1366 xasprintf (&msg, 1418 xasprintf(
1367 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"), 1419 &msg, _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"), msg,
1368 msg, page_len, elapsed_time, 1420 page_len, elapsed_time, (display_html ? "</A>" : ""), perfd_time(elapsed_time),
1369 (display_html ? "</A>" : ""), 1421 perfd_size(page_len), perfd_time_connect(elapsed_time_connect),
1370 perfd_time (elapsed_time), 1422 use_ssl ? perfd_time_ssl(elapsed_time_ssl) : "",
1371 perfd_size (page_len), 1423 perfd_time_headers(elapsed_time_headers), perfd_time_firstbyte(elapsed_time_firstbyte),
1372 perfd_time_connect (elapsed_time_connect), 1424 perfd_time_transfer(elapsed_time_transfer));
1373 use_ssl == true ? perfd_time_ssl (elapsed_time_ssl) : "", 1425 } else {
1374 perfd_time_headers (elapsed_time_headers), 1426 xasprintf(&msg, _("%s - %d bytes in %.3f second response time %s|%s %s"), msg, page_len,
1375 perfd_time_firstbyte (elapsed_time_firstbyte), 1427 elapsed_time, (display_html ? "</A>" : ""), perfd_time(elapsed_time),
1376 perfd_time_transfer (elapsed_time_transfer)); 1428 perfd_size(page_len));
1377 else 1429 }
1378 xasprintf (&msg, 1430
1379 _("%s - %d bytes in %.3f second response time %s|%s %s"), 1431 if (show_body) {
1380 msg, page_len, elapsed_time, 1432 xasprintf(&msg, _("%s\n%s"), msg, page);
1381 (display_html ? "</A>" : ""), 1433 }
1382 perfd_time (elapsed_time), 1434
1383 perfd_size (page_len)); 1435 result = max_state_alt(get_status(elapsed_time, thlds), result);
1384 1436
1385 if (show_body) 1437 die(result, "HTTP %s: %s\n", state_text(result), msg);
1386 xasprintf (&msg, _("%s\n%s"), msg, page); 1438 /* die failed? */
1387 1439 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} 1440}
1394 1441
1395/* Receivces a pointer to the beginning of the body of a HTTP message 1442/* Receivces a pointer to the beginning of the body of a HTTP message
@@ -1398,94 +1445,95 @@ check_http (void)
1398 * The result must be freed by the caller. 1445 * The result must be freed by the caller.
1399 */ 1446 */
1400char *unchunk_content(const char *content) { 1447char *unchunk_content(const char *content) {
1401 // https://en.wikipedia.org/wiki/Chunked_transfer_encoding 1448 // https://en.wikipedia.org/wiki/Chunked_transfer_encoding
1402 // https://www.rfc-editor.org/rfc/rfc7230#section-4.1 1449 // https://www.rfc-editor.org/rfc/rfc7230#section-4.1
1403 char *result = NULL; 1450 char *result = NULL;
1404 char *start_of_chunk; 1451 char *start_of_chunk;
1405 char* end_of_chunk; 1452 char *end_of_chunk;
1406 long size_of_chunk; 1453 long size_of_chunk;
1407 const char *pointer = content; 1454 const char *pointer = content;
1408 char *endptr; 1455 char *endptr;
1409 long length_of_chunk = 0; 1456 long length_of_chunk = 0;
1410 size_t overall_size = 0; 1457 size_t overall_size = 0;
1411 1458
1412 while (true) { 1459 while (true) {
1413 size_of_chunk = strtol(pointer, &endptr, 16); 1460 size_of_chunk = strtol(pointer, &endptr, 16);
1414 if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) { 1461 if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) {
1415 // Apparently underflow or overflow, should not happen 1462 // Apparently underflow or overflow, should not happen
1416 if (verbose) { 1463 if (verbose) {
1417 printf("Got an underflow or overflow from strtol at: %u\n", __LINE__); 1464 printf("Got an underflow or overflow from strtol at: %u\n", __LINE__);
1418 } 1465 }
1419 return NULL; 1466 return NULL;
1420 } 1467 }
1421 if (endptr == pointer) { 1468 if (endptr == pointer) {
1422 // Apparently this was not a number 1469 // Apparently this was not a number
1423 if (verbose) { 1470 if (verbose) {
1424 printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__); 1471 printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__);
1425 } 1472 }
1426 return NULL; 1473 return NULL;
1427 } 1474 }
1428 1475
1429 // So, we got the length of the chunk 1476 // So, we got the length of the chunk
1430 if (*endptr == ';') { 1477 if (*endptr == ';') {
1431 // Chunk extension starts here 1478 // Chunk extension starts here
1432 while (*endptr != '\r') { 1479 while (*endptr != '\r') {
1433 endptr++; 1480 endptr++;
1434 } 1481 }
1435 } 1482 }
1436 1483
1437 start_of_chunk = endptr + 2; 1484 start_of_chunk = endptr + 2;
1438 end_of_chunk = start_of_chunk + size_of_chunk; 1485 end_of_chunk = start_of_chunk + size_of_chunk;
1439 length_of_chunk = (long)(end_of_chunk - start_of_chunk); 1486 length_of_chunk = (long)(end_of_chunk - start_of_chunk);
1440 pointer = end_of_chunk + 2; //Next number should be here 1487 pointer = end_of_chunk + 2; // Next number should be here
1441 1488
1442 if (length_of_chunk == 0) { 1489 if (length_of_chunk == 0) {
1443 // Chunk length is 0, so this is the last one 1490 // Chunk length is 0, so this is the last one
1444 break; 1491 break;
1445 } 1492 }
1446 1493
1447 overall_size += length_of_chunk; 1494 overall_size += length_of_chunk;
1448 1495
1449 if (result == NULL) { 1496 if (result == NULL) {
1450 // Size of the chunk plus the ending NULL byte 1497 // Size of the chunk plus the ending NULL byte
1451 result = (char *)malloc(length_of_chunk +1); 1498 result = (char *)malloc(length_of_chunk + 1);
1452 if (result == NULL) { 1499 if (result == NULL) {
1453 if (verbose) { 1500 if (verbose) {
1454 printf("Failed to allocate memory for unchunked body\n"); 1501 printf("Failed to allocate memory for unchunked body\n");
1455 } 1502 }
1456 return NULL; 1503 return NULL;
1457 } 1504 }
1458 } else { 1505 } else {
1459 // Enlarge memory to the new size plus the ending NULL byte 1506 // Enlarge memory to the new size plus the ending NULL byte
1460 void *tmp = realloc(result, overall_size +1); 1507 void *tmp = realloc(result, overall_size + 1);
1461 if (tmp == NULL) { 1508 if (tmp == NULL) {
1462 if (verbose) { 1509 if (verbose) {
1463 printf("Failed to allocate memory for unchunked body\n"); 1510 printf("Failed to allocate memory for unchunked body\n");
1464 } 1511 }
1465 return NULL; 1512 return NULL;
1466 } else { 1513 }
1467 result = tmp; 1514 result = tmp;
1468 } 1515 }
1469 } 1516
1470 1517 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); 1518 }
1472 } 1519
1473 1520 if (overall_size == 0 && result == NULL) {
1474 if (overall_size == 0 && result == NULL) { 1521 // 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 1522 // allocated
1476 result = calloc(1, sizeof(char)); 1523 result = calloc(1, sizeof(char));
1477 // No error handling here, we can only return NULL anyway 1524 // No error handling here, we can only return NULL anyway
1478 } else { 1525 } else {
1479 result[overall_size] = '\0'; 1526 result[overall_size] = '\0';
1480 } 1527 }
1481 return result; 1528 return result;
1482} 1529}
1483 1530
1484/* per RFC 2396 */ 1531/* per RFC 2396 */
1485#define URI_HTTP "%5[HTPShtps]" 1532#define URI_HTTP "%5[HTPShtps]"
1486#define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" 1533#define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1487#define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */ 1534#define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1488#define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" 1535#define URI_PATH \
1536 "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1489#define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH 1537#define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1490#define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH 1538#define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1491#define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT 1539#define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
@@ -1494,414 +1542,431 @@ char *unchunk_content(const char *content) {
1494#define HD5 "//" URI_HOST "/" URI_PATH 1542#define HD5 "//" URI_HOST "/" URI_PATH
1495#define HD6 URI_PATH 1543#define HD6 URI_PATH
1496 1544
1497void 1545void redir(char *pos, char *status_line) {
1498redir (char *pos, char *status_line) 1546 int i = 0;
1499{ 1547 char *x;
1500 int i = 0; 1548 char xx[2];
1501 char *x; 1549 char type[6];
1502 char xx[2]; 1550 char *addr;
1503 char type[6]; 1551 char *url;
1504 char *addr; 1552
1505 char *url; 1553 addr = malloc(MAX_IPV4_HOSTLENGTH + 1);
1506 1554 if (addr == NULL) {
1507 addr = malloc (MAX_IPV4_HOSTLENGTH + 1); 1555 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1508 if (addr == NULL) 1556 }
1509 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n")); 1557
1510 1558 memset(addr, 0, MAX_IPV4_HOSTLENGTH);
1511 memset(addr, 0, MAX_IPV4_HOSTLENGTH); 1559 url = malloc(strcspn(pos, "\r\n"));
1512 url = malloc (strcspn (pos, "\r\n")); 1560 if (url == NULL) {
1513 if (url == NULL) 1561 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1514 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 1562 }
1515 1563
1516 while (pos) { 1564 while (pos) {
1517 sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i); 1565 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) { 1566 if (i == 0) {
1519 pos += (size_t) strcspn (pos, "\r\n"); 1567 pos += (size_t)strcspn(pos, "\r\n");
1520 pos += (size_t) strspn (pos, "\r\n"); 1568 pos += (size_t)strspn(pos, "\r\n");
1521 if (strlen(pos) == 0) 1569 if (strlen(pos) == 0) {
1522 die (STATE_UNKNOWN, 1570 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1523 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"), 1571 status_line, (display_html ? "</A>" : ""));
1524 status_line, (display_html ? "</A>" : "")); 1572 }
1525 continue; 1573 continue;
1526 } 1574 }
1527 1575
1528 pos += i; 1576 pos += i;
1529 pos += strspn (pos, " \t"); 1577 pos += strspn(pos, " \t");
1530 1578
1531 /* 1579 /*
1532 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by 1580 * 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.'' 1581 * preceding each extra line with at least one SP or HT.''
1534 */ 1582 */
1535 for (; (i = strspn (pos, "\r\n")); pos += i) { 1583 for (; (i = strspn(pos, "\r\n")); pos += i) {
1536 pos += i; 1584 pos += i;
1537 if (!(i = strspn (pos, " \t"))) { 1585 if (!(i = strspn(pos, " \t"))) {
1538 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"), 1586 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1539 display_html ? "</A>" : ""); 1587 display_html ? "</A>" : "");
1540 } 1588 }
1541 } 1589 }
1542 1590
1543 url = realloc (url, strcspn (pos, "\r\n") + 1); 1591 url = realloc(url, strcspn(pos, "\r\n") + 1);
1544 if (url == NULL) 1592 if (url == NULL) {
1545 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 1593 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1546 1594 }
1547 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ 1595
1548 if (sscanf (pos, HD1, type, addr, &i, url) == 4) { 1596 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1549 url = prepend_slash (url); 1597 if (sscanf(pos, HD1, type, addr, &i, url) == 4) {
1550 use_ssl = server_type_check (type); 1598 url = prepend_slash(url);
1551 } 1599 use_ssl = server_type_check(type);
1552 1600 }
1553 /* URI_HTTP URI_HOST URI_PATH */ 1601
1554 else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 1602 /* URI_HTTP URI_HOST URI_PATH */
1555 url = prepend_slash (url); 1603 else if (sscanf(pos, HD2, type, addr, url) == 3) {
1556 use_ssl = server_type_check (type); 1604 url = prepend_slash(url);
1557 i = server_port_check (use_ssl); 1605 use_ssl = server_type_check(type);
1558 } 1606 i = server_port_check(use_ssl);
1559 1607 }
1560 /* URI_HTTP URI_HOST URI_PORT */ 1608
1561 else if (sscanf (pos, HD3, type, addr, &i) == 3) { 1609 /* URI_HTTP URI_HOST URI_PORT */
1562 strcpy (url, HTTP_URL); 1610 else if (sscanf(pos, HD3, type, addr, &i) == 3) {
1563 use_ssl = server_type_check (type); 1611 strcpy(url, HTTP_URL);
1564 } 1612 use_ssl = server_type_check(type);
1565 1613 }
1566 /* URI_HTTP URI_HOST */ 1614
1567 else if (sscanf (pos, HD4, type, addr) == 2) { 1615 /* URI_HTTP URI_HOST */
1568 strcpy (url, HTTP_URL); 1616 else if (sscanf(pos, HD4, type, addr) == 2) {
1569 use_ssl = server_type_check (type); 1617 strcpy(url, HTTP_URL);
1570 i = server_port_check (use_ssl); 1618 use_ssl = server_type_check(type);
1571 } 1619 i = server_port_check(use_ssl);
1572 /* URI_HTTP, URI_HOST, URI_PATH */ 1620 }
1573 else if (sscanf (pos, HD5, addr, url) == 2) { 1621 /* URI_HTTP, URI_HOST, URI_PATH */
1574 if(use_ssl){ 1622 else if (sscanf(pos, HD5, addr, url) == 2) {
1575 strcpy (type,"https"); 1623 if (use_ssl) {
1576 } 1624 strcpy(type, "https");
1577 else{ 1625 } else {
1578 strcpy (type, server_type); 1626 strcpy(type, server_type);
1579 } 1627 }
1580 xasprintf (&url, "/%s", url); 1628 xasprintf(&url, "/%s", url);
1581 use_ssl = server_type_check (type); 1629 use_ssl = server_type_check(type);
1582 i = server_port_check (use_ssl); 1630 i = server_port_check(use_ssl);
1583 } 1631 }
1584 1632
1585 /* URI_PATH */ 1633 /* URI_PATH */
1586 else if (sscanf (pos, HD6, url) == 1) { 1634 else if (sscanf(pos, HD6, url) == 1) {
1587 /* relative url */ 1635 /* relative url */
1588 if ((url[0] != '/')) { 1636 if ((url[0] != '/')) {
1589 if ((x = strrchr(server_url, '/'))) 1637 if ((x = strrchr(server_url, '/'))) {
1590 *x = '\0'; 1638 *x = '\0';
1591 xasprintf (&url, "%s/%s", server_url, url); 1639 }
1592 } 1640 xasprintf(&url, "%s/%s", server_url, url);
1593 i = server_port; 1641 }
1594 strcpy (type, server_type); 1642 i = server_port;
1595 strcpy (addr, host_name ? host_name : server_address); 1643 strcpy(type, server_type);
1596 } 1644 strcpy(addr, host_name ? host_name : server_address);
1597 1645 }
1598 else { 1646
1599 die (STATE_UNKNOWN, 1647 else {
1600 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), 1648 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), pos,
1601 pos, (display_html ? "</A>" : "")); 1649 (display_html ? "</A>" : ""));
1602 } 1650 }
1603 1651
1604 break; 1652 break;
1605 1653
1606 } /* end while (pos) */ 1654 } /* end while (pos) */
1607 1655
1608 if (++redir_depth > max_depth) 1656 if (++redir_depth > max_depth) {
1609 die (STATE_WARNING, 1657 die(STATE_WARNING,
1610 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"), 1658 _("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>" : "")); 1659 type, addr, i, url, (display_html ? "</A>" : ""));
1612 1660 }
1613 if (server_port==i && 1661
1614 !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) && 1662 if (server_port == i && !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) &&
1615 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && 1663 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, url)) {
1616 !strcmp(server_url, url)) 1664 die(STATE_CRITICAL,
1617 die (STATE_CRITICAL, 1665 _("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"), 1666 addr, i, url, (display_html ? "</A>" : ""));
1619 type, addr, i, url, (display_html ? "</A>" : "")); 1667 }
1620 1668
1621 strcpy (server_type, type); 1669 strcpy(server_type, type);
1622 1670
1623 free (host_name); 1671 free(host_name);
1624 host_name = strndup (addr, MAX_IPV4_HOSTLENGTH); 1672 host_name = strndup(addr, MAX_IPV4_HOSTLENGTH);
1625 1673
1626 if (!(followsticky & STICKY_HOST)) { 1674 if (!(followsticky & STICKY_HOST)) {
1627 free (server_address); 1675 free(server_address);
1628 server_address = strndup (addr, MAX_IPV4_HOSTLENGTH); 1676 server_address = strndup(addr, MAX_IPV4_HOSTLENGTH);
1629 } 1677 }
1630 if (!(followsticky & STICKY_PORT)) { 1678 if (!(followsticky & STICKY_PORT)) {
1631 server_port = i; 1679 server_port = i;
1632 } 1680 }
1633 1681
1634 free (server_url); 1682 free(server_url);
1635 server_url = url; 1683 server_url = url;
1636 1684
1637 if (server_port > MAX_PORT) 1685 if (server_port > MAX_PORT) {
1638 die (STATE_UNKNOWN, 1686 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"), 1687 MAX_PORT, server_type, server_address, server_port, server_url,
1640 MAX_PORT, server_type, server_address, server_port, server_url, 1688 display_html ? "</A>" : "");
1641 display_html ? "</A>" : ""); 1689 }
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 1690
1691 /* reset virtual port */
1692 virtual_port = server_port;
1654 1693
1655bool 1694 if (verbose) {
1656server_type_check (const char *type) 1695 printf(_("Redirection to %s://%s:%d%s\n"), server_type,
1657{ 1696 host_name ? host_name : server_address, server_port, server_url);
1658 if (strcmp (type, "https")) 1697 }
1659 return false; 1698
1660 else 1699 free(addr);
1661 return true; 1700 check_http();
1662} 1701}
1663 1702
1664int 1703bool server_type_check(const char *type) { return (!(bool)strcmp(type, "https")); }
1665server_port_check (int ssl_flag) 1704
1666{ 1705int server_port_check(int ssl_flag) {
1667 if (ssl_flag) 1706 if (ssl_flag) {
1668 return HTTPS_PORT; 1707 return HTTPS_PORT;
1669 else 1708 }
1670 return HTTP_PORT; 1709 return HTTP_PORT;
1671} 1710}
1672 1711
1673char *perfd_time (double elapsed_time) 1712char *perfd_time(double elapsed_time) {
1674{ 1713 return fperfdata("time", elapsed_time, "s", thlds->warning,
1675 return fperfdata ("time", elapsed_time, "s", 1714 thlds->warning ? thlds->warning->end : 0, thlds->critical,
1676 thlds->warning?true:false, thlds->warning?thlds->warning->end:0, 1715 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} 1716}
1680 1717
1681char *perfd_time_connect (double elapsed_time_connect) 1718char *perfd_time_connect(double elapsed_time_connect) {
1682{ 1719 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); 1720 socket_timeout);
1684} 1721}
1685 1722
1686char *perfd_time_ssl (double elapsed_time_ssl) 1723char *perfd_time_ssl(double elapsed_time_ssl) {
1687{ 1724 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); 1725 socket_timeout);
1689} 1726}
1690 1727
1691char *perfd_time_headers (double elapsed_time_headers) 1728char *perfd_time_headers(double elapsed_time_headers) {
1692{ 1729 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); 1730 socket_timeout);
1694} 1731}
1695 1732
1696char *perfd_time_firstbyte (double elapsed_time_firstbyte) 1733char *perfd_time_firstbyte(double elapsed_time_firstbyte) {
1697{ 1734 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); 1735 true, socket_timeout);
1699} 1736}
1700 1737
1701char *perfd_time_transfer (double elapsed_time_transfer) 1738char *perfd_time_transfer(double elapsed_time_transfer) {
1702{ 1739 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); 1740 true, socket_timeout);
1704} 1741}
1705 1742
1706char *perfd_size (int page_len) 1743char *perfd_size(int page_len) {
1707{ 1744 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", 1745 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} 1746}
1713 1747
1714void 1748void print_help(void) {
1715print_help (void) 1749 print_revision(progname, NP_VERSION);
1716{
1717 print_revision (progname, NP_VERSION);
1718 1750
1719 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 1751 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1720 printf (COPYRIGHT, copyright, email); 1752 printf(COPYRIGHT, copyright, email);
1721 1753
1722 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); 1754 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")); 1755 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")); 1756 printf("%s\n", _("strings and regular expressions, check connection times, and report on"));
1725 printf ("%s\n", _("certificate expiration times.")); 1757 printf("%s\n", _("certificate expiration times."));
1726 1758
1727 printf ("\n\n"); 1759 printf("\n");
1760 printf("%s\n", _("ATTENTION!"));
1761 printf("\n");
1762 printf("%s\n", _("THIS PLUGIN IS DEPRECATED. The functionality was reimplemented by the"));
1763 printf("%s\n", _("check_curl plugin, which can be used as a drop-in replacement. You should"));
1764 printf("%s\n", _("migrate your checks over to check_curl, because check_http is going to be"));
1765 printf("%s\n", _("removed sooner than later. Just replace check_http with check_curl in your"));
1766 printf("%s\n", _("check command definitions."));
1767 printf("%s\n",
1768 _("Report issues to: https://github.com/monitoring-plugins/monitoring-plugins/issues"));
1728 1769
1729 print_usage (); 1770 printf("\n\n");
1771
1772 print_usage();
1730 1773
1731#ifdef HAVE_SSL 1774#ifdef HAVE_SSL
1732 printf (_("In the first form, make an HTTP request.")); 1775 printf(_("In the first form, make an HTTP request."));
1733 printf (_("In the second form, connect to the server and check the TLS certificate.")); 1776 printf(_("In the second form, connect to the server and check the TLS certificate."));
1734#endif 1777#endif
1735 printf (_("NOTE: One or both of -H and -I must be specified")); 1778 printf(_("NOTE: One or both of -H and -I must be specified"));
1736 1779
1737 printf ("\n"); 1780 printf("\n");
1738 1781
1739 printf (UT_HELP_VRSN); 1782 printf(UT_HELP_VRSN);
1740 printf (UT_EXTRA_OPTS); 1783 printf(UT_EXTRA_OPTS);
1741 1784
1742 printf (" %s\n", "-H, --hostname=ADDRESS"); 1785 printf(" %s\n", "-H, --hostname=ADDRESS");
1743 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)")); 1786 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)")); 1787 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1745 printf (" %s\n", "-I, --IP-address=ADDRESS"); 1788 printf(" %s\n", "-I, --IP-address=ADDRESS");
1746 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); 1789 printf(" %s\n",
1747 printf (" %s\n", "-p, --port=INTEGER"); 1790 _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1748 printf (" %s", _("Port number (default: ")); 1791 printf(" %s\n", "-p, --port=INTEGER");
1749 printf ("%d)\n", HTTP_PORT); 1792 printf(" %s", _("Port number (default: "));
1793 printf("%d)\n", HTTP_PORT);
1750 1794
1751 printf (UT_IPv46); 1795 printf(UT_IPv46);
1752 1796
1753#ifdef HAVE_SSL 1797#ifdef HAVE_SSL
1754 printf (" %s\n", "-S, --ssl=VERSION[+]"); 1798 printf(" %s\n", "-S, --ssl=VERSION[+]");
1755 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); 1799 printf(" %s\n",
1756 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); 1800 _("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.")); 1801 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1758 printf (" %s\n", "--sni"); 1802 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)")); 1803 printf(" %s\n", "--sni");
1760 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); 1804 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")); 1805 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")); 1806 printf(" %s\n",
1763 printf (" %s\n", _(" --continue-after-certificate to override this behavior)")); 1807 _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1764 printf (" %s\n", "--continue-after-certificate"); 1808 printf(" %s\n",
1765 printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); 1809 _("(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.")); 1810 printf(" %s\n", _(" --continue-after-certificate to override this behavior)"));
1767 printf (" %s\n", "-J, --client-cert=FILE"); 1811 printf(" %s\n", "--continue-after-certificate");
1768 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)")); 1812 printf(" %s\n",
1769 printf (" %s\n", _("to be used in establishing the SSL session")); 1813 _("Allows the HTTP check to continue after performing the certificate check."));
1770 printf (" %s\n", "-K, --private-key=FILE"); 1814 printf(" %s\n", _("Does nothing unless -C is used."));
1771 printf (" %s\n", _("Name of file containing the private key (PEM format)")); 1815 printf(" %s\n", "-J, --client-cert=FILE");
1772 printf (" %s\n", _("matching the client certificate")); 1816 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1817 printf(" %s\n", _("to be used in establishing the SSL session"));
1818 printf(" %s\n", "-K, --private-key=FILE");
1819 printf(" %s\n", _("Name of file containing the private key (PEM format)"));
1820 printf(" %s\n", _("matching the client certificate"));
1773#endif 1821#endif
1774 1822
1775 printf (" %s\n", "-e, --expect=STRING"); 1823 printf(" %s\n", "-e, --expect=STRING");
1776 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); 1824 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: ")); 1825 printf(" %s", _("the first (status) line of the server response (default: "));
1778 printf ("%s)\n", HTTP_EXPECT); 1826 printf("%s)\n", HTTP_EXPECT);
1779 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); 1827 printf(" %s\n",
1780 printf (" %s\n", "-d, --header-string=STRING"); 1828 _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1781 printf (" %s\n", _("String to expect in the response headers")); 1829 printf(" %s\n", "-d, --header-string=STRING");
1782 printf (" %s\n", "-s, --string=STRING"); 1830 printf(" %s\n", _("String to expect in the response headers"));
1783 printf (" %s\n", _("String to expect in the content")); 1831 printf(" %s\n", "-s, --string=STRING");
1784 printf (" %s\n", "-u, --url=PATH"); 1832 printf(" %s\n", _("String to expect in the content"));
1785 printf (" %s\n", _("URL to GET or POST (default: /)")); 1833 printf(" %s\n", "-u, --url=PATH");
1786 printf (" %s\n", "-P, --post=STRING"); 1834 printf(" %s\n", _("URL to GET or POST (default: /)"));
1787 printf (" %s\n", _("URL decoded http POST data")); 1835 printf(" %s\n", "-P, --post=STRING");
1788 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT, CONNECT:POST)"); 1836 printf(" %s\n", _("URL decoded http POST data"));
1789 printf (" %s\n", _("Set HTTP method.")); 1837 printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, "
1790 printf (" %s\n", "-N, --no-body"); 1838 "CONNECT, CONNECT:POST)");
1791 printf (" %s\n", _("Don't wait for document body: stop reading after headers.")); 1839 printf(" %s\n", _("Set HTTP method."));
1792 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)")); 1840 printf(" %s\n", "-N, --no-body");
1793 printf (" %s\n", "-M, --max-age=SECONDS"); 1841 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")); 1842 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.")); 1843 printf(" %s\n", "-M, --max-age=SECONDS");
1796 printf (" %s\n", "-T, --content-type=STRING"); 1844 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")); 1845 printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1798 1846 printf(" %s\n", "-T, --content-type=STRING");
1799 printf (" %s\n", "-l, --linespan"); 1847 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)")); 1848
1801 printf (" %s\n", "-r, --regex, --ereg=STRING"); 1849 printf(" %s\n", "-l, --linespan");
1802 printf (" %s\n", _("Search page for regex STRING")); 1850 printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1803 printf (" %s\n", "-R, --eregi=STRING"); 1851 printf(" %s\n", "-r, --regex, --ereg=STRING");
1804 printf (" %s\n", _("Search page for case-insensitive regex STRING")); 1852 printf(" %s\n", _("Search page for regex STRING"));
1805 printf (" %s\n", "--invert-regex"); 1853 printf(" %s\n", "-R, --eregi=STRING");
1806 printf (" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); 1854 printf(" %s\n", _("Search page for case-insensitive regex STRING"));
1807 printf (" %s\n", _("can be changed with --state--regex)")); 1855 printf(" %s\n", "--invert-regex");
1808 printf (" %s\n", "--state-regex=STATE"); 1856 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")); 1857 printf(" %s\n", _("can be changed with --state--regex)"));
1810 1858 printf(" %s\n", "--state-regex=STATE");
1811 printf (" %s\n", "-a, --authorization=AUTH_PAIR"); 1859 printf(" %s\n", _("Return STATE if regex is found, OK if not\n"));
1812 printf (" %s\n", _("Username:password on sites with basic authentication")); 1860
1813 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); 1861 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1814 printf (" %s\n", _("Username:password on proxy-servers with basic authentication")); 1862 printf(" %s\n", _("Username:password on sites with basic authentication"));
1815 printf (" %s\n", "-A, --useragent=STRING"); 1863 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1816 printf (" %s\n", _("String to be sent in http header as \"User Agent\"")); 1864 printf(" %s\n", _("Username:password on proxy-servers with basic authentication"));
1817 printf (" %s\n", "-k, --header=STRING"); 1865 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")); 1866 printf(" %s\n", _("String to be sent in http header as \"User Agent\""));
1819 printf (" %s\n", "-E, --extended-perfdata"); 1867 printf(" %s\n", "-k, --header=STRING");
1820 printf (" %s\n", _("Print additional performance data")); 1868 printf(
1821 printf (" %s\n", "-B, --show-body"); 1869 " %s\n",
1822 printf (" %s\n", _("Print body content below status line")); 1870 _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1823 printf (" %s\n", "-L, --link"); 1871 printf(" %s\n", "-E, --extended-perfdata");
1824 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); 1872 printf(" %s\n", _("Print additional performance data"));
1825 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>"); 1873 printf(" %s\n", "-B, --show-body");
1826 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); 1874 printf(" %s\n", _("Print body content below status line"));
1827 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); 1875 printf(" %s\n", "-L, --link");
1828 printf (" %s\n", "--max-redirs=INTEGER"); 1876 printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1829 printf (" %s", _("Maximal number of redirects (default: ")); 1877 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1830 printf ("%d)\n", DEFAULT_MAX_REDIRS); 1878 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1831 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); 1879 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)")); 1880 printf(" %s\n", "--max-redirs=INTEGER");
1833 printf (UT_WARN_CRIT); 1881 printf(" %s", _("Maximal number of redirects (default: "));
1834 1882 printf("%d)\n", DEFAULT_MAX_REDIRS);
1835 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1883 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1836 1884 printf(" %s\n",
1837 printf (UT_VERBOSE); 1885 _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1838 1886 printf(UT_WARN_CRIT);
1839 printf ("\n"); 1887
1840 printf ("%s\n", _("Notes:")); 1888 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1841 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); 1889
1842 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); 1890 printf(UT_VERBOSE);
1843 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); 1891
1844 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); 1892 printf("\n");
1845 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); 1893 printf("%s\n", _("Notes:"));
1846 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); 1894 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1895 printf(" %s\n",
1896 _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1897 printf(" %s\n",
1898 _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1899 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1900 printf(" %s\n",
1901 _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1902 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1847 1903
1848#ifdef HAVE_SSL 1904#ifdef HAVE_SSL
1849 printf ("\n"); 1905 printf("\n");
1850 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to")); 1906 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 ")); 1907 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.")); 1908 printf(" %s\n", _("certificate is still valid for the specified number of days."));
1853 printf ("\n"); 1909 printf("\n");
1854 printf (" %s\n", _("Please note that this plugin does not check if the presented server")); 1910 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")); 1911 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.")); 1912 printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1857 printf ("\n"); 1913 printf("\n");
1858 printf ("%s\n", _("Examples:")); 1914 printf("%s\n", _("Examples:"));
1859 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com"); 1915 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,")); 1916 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")); 1917 printf(" %s\n",
1862 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1918 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1863 printf (" %s\n", _("a STATE_CRITICAL will be returned.")); 1919 printf(" %s\n",
1864 printf ("\n"); 1920 _("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"); 1921 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,")); 1922 printf("\n");
1867 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1923 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")); 1924 printf(" %s\n",
1869 printf (" %s\n\n", _("the certificate is expired.")); 1925 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1870 printf ("\n"); 1926 printf(" %s\n",
1871 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14"); 1927 _("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,")); 1928 printf(" %s\n",
1873 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1929 _("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.")); 1930 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")); 1931 printf("\n");
1876 1932 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: "); 1933 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 ")); 1934 _("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>")); 1935 printf(" %s\n",
1880 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1936 _("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,")); 1937 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")); 1938 printf(" %s\n",
1883 printf (" %s\n", _("inside the proxied connection: -j CONNECT:POST")); 1939 _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1940
1941 printf(" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1942 printf(" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j "
1943 "CONNECT -H www.verisign.com "));
1944 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
1945 "-S(sl) -j CONNECT -H <webserver>"));
1946 printf(" %s\n",
1947 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1948 printf(" %s\n",
1949 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1950 printf(" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can "
1951 "set the method used"));
1952 printf(" %s\n", _("inside the proxied connection: -j CONNECT:POST"));
1884 1953
1885#endif 1954#endif
1886 1955
1887 printf (UT_SUPPORT); 1956 printf(UT_SUPPORT);
1888
1889} 1957}
1890 1958
1891 1959void print_usage(void) {
1892 1960 printf("%s\n", _("Usage:"));
1893void 1961 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
1894print_usage (void) 1962 printf(" [-J <client certificate file>] [-K <private key>]\n");
1895{ 1963 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1896 printf ("%s\n", _("Usage:")); 1964 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); 1965 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
1898 printf (" [-J <client certificate file>] [-K <private key>]\n"); 1966 "regex>]\n");
1899 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1967 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"); 1968 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"); 1969 printf(" [-T <content-type>] [-j method]\n");
1902 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1970 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname);
1903 printf (" [-A string] [-k string] [-S <version>] [--sni]\n"); 1971 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} 1972}
diff --git a/plugins/check_ide_smart.c b/plugins/check_ide_smart.c
index 9640ef70..16fe3d01 100644
--- a/plugins/check_ide_smart.c
+++ b/plugins/check_ide_smart.c
@@ -56,7 +56,6 @@ void print_usage(void);
56# include <sys/device.h> 56# include <sys/device.h>
57# include <sys/param.h> 57# include <sys/param.h>
58# include <sys/sysctl.h> 58# include <sys/sysctl.h>
59# include <sys/videoio.h> /* for __u8 and friends */
60# include <sys/scsiio.h> 59# include <sys/scsiio.h>
61# include <sys/ataio.h> 60# include <sys/ataio.h>
62# include <dev/ata/atareg.h> 61# include <dev/ata/atareg.h>
@@ -79,48 +78,47 @@ void print_usage(void);
79#define UNKNOWN -1 78#define UNKNOWN -1
80 79
81typedef struct threshold_s { 80typedef struct threshold_s {
82 __u8 id; 81 uint8_t id;
83 __u8 threshold; 82 uint8_t threshold;
84 __u8 reserved[10]; 83 uint8_t reserved[10];
85} __attribute__((packed)) threshold_t; 84} __attribute__((packed)) threshold_t;
86 85
87typedef struct thresholds_s { 86typedef struct thresholds_s {
88 __u16 revision; 87 uint16_t revision;
89 threshold_t thresholds[NR_ATTRIBUTES]; 88 threshold_t thresholds[NR_ATTRIBUTES];
90 __u8 reserved[18]; 89 uint8_t reserved[18];
91 __u8 vendor[131]; 90 uint8_t vendor[131];
92 __u8 checksum; 91 uint8_t checksum;
93} __attribute__((packed)) thresholds_t; 92} __attribute__((packed)) thresholds_t;
94 93
95typedef struct value_s { 94typedef struct value_s {
96 __u8 id; 95 uint8_t id;
97 __u16 status; 96 uint16_t status;
98 __u8 value; 97 uint8_t value;
99 __u8 vendor[8]; 98 uint8_t vendor[8];
100} __attribute__((packed)) value_t; 99} __attribute__((packed)) value_t;
101 100
102typedef struct values_s { 101typedef struct values_s {
103 __u16 revision; 102 uint16_t revision;
104 value_t values[NR_ATTRIBUTES]; 103 value_t values[NR_ATTRIBUTES];
105 __u8 offline_status; 104 uint8_t offline_status;
106 __u8 vendor1; 105 uint8_t vendor1;
107 __u16 offline_timeout; 106 uint16_t offline_timeout;
108 __u8 vendor2; 107 uint8_t vendor2;
109 __u8 offline_capability; 108 uint8_t offline_capability;
110 __u16 smart_capability; 109 uint16_t smart_capability;
111 __u8 reserved[16]; 110 uint8_t reserved[16];
112 __u8 vendor[125]; 111 uint8_t vendor[125];
113 __u8 checksum; 112 uint8_t checksum;
114} __attribute__((packed)) values_t; 113} __attribute__((packed)) values_t;
115 114
116static struct { 115static struct {
117 __u8 value; 116 uint8_t value;
118 char *text; 117 char *text;
119} offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, 118} offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
120 {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
121 119
122static struct { 120static struct {
123 __u8 value; 121 uint8_t value;
124 char *text; 122 char *text;
125} smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"}, 123} smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"},
126 {SMART_DISABLE, "SMART_DISABLE"}, 124 {SMART_DISABLE, "SMART_DISABLE"},
@@ -140,7 +138,7 @@ static int smart_read_values(int, values_t *);
140static int nagios(values_t *, thresholds_t *); 138static int nagios(values_t *, thresholds_t *);
141static void print_value(value_t *, threshold_t *); 139static void print_value(value_t *, threshold_t *);
142static void print_values(values_t *, thresholds_t *); 140static void print_values(values_t *, thresholds_t *);
143static int smart_cmd_simple(int, enum SmartCommand, __u8, bool); 141static int smart_cmd_simple(int, enum SmartCommand, uint8_t, bool);
144static int smart_read_thresholds(int, thresholds_t *); 142static int smart_read_thresholds(int, thresholds_t *);
145static bool verbose = false; 143static bool verbose = false;
146 144
@@ -175,8 +173,9 @@ int main(int argc, char *argv[]) {
175 173
176 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex); 174 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex);
177 175
178 if (o == -1 || o == EOF || o == 1) 176 if (o == -1 || o == EOF || o == 1) {
179 break; 177 break;
178 }
180 179
181 switch (o) { 180 switch (o) {
182 case 'd': 181 case 'd':
@@ -234,8 +233,9 @@ int main(int argc, char *argv[]) {
234 smart_read_values(fd, &values); 233 smart_read_values(fd, &values);
235 smart_read_thresholds(fd, &thresholds); 234 smart_read_thresholds(fd, &thresholds);
236 retval = nagios(&values, &thresholds); 235 retval = nagios(&values, &thresholds);
237 if (verbose) 236 if (verbose) {
238 print_values(&values, &thresholds); 237 print_values(&values, &thresholds);
238 }
239 239
240 close(fd); 240 close(fd);
241 return retval; 241 return retval;
@@ -254,7 +254,7 @@ char *get_offline_text(int status) {
254int smart_read_values(int fd, values_t *values) { 254int smart_read_values(int fd, values_t *values) {
255#ifdef __linux__ 255#ifdef __linux__
256 int e; 256 int e;
257 __u8 args[4 + 512]; 257 uint8_t args[4 + 512];
258 args[0] = WIN_SMART; 258 args[0] = WIN_SMART;
259 args[1] = 0; 259 args[1] = 0;
260 args[2] = SMART_READ_VALUES; 260 args[2] = SMART_READ_VALUES;
@@ -282,8 +282,9 @@ int smart_read_values(int fd, values_t *values) {
282 req.cylinder = WDSMART_CYL; 282 req.cylinder = WDSMART_CYL;
283 283
284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
285 if (req.retsts != ATACMD_OK) 285 if (req.retsts != ATACMD_OK) {
286 errno = ENODEV; 286 errno = ENODEV;
287 }
287 } 288 }
288 289
289 if (errno != 0) { 290 if (errno != 0) {
@@ -370,22 +371,24 @@ void print_values(values_t *p, thresholds_t *t) {
370 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : ""); 371 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : "");
371} 372}
372 373
373int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_error) { 374int smart_cmd_simple(int fd, enum SmartCommand command, uint8_t val0, bool show_error) {
374 int e = STATE_UNKNOWN; 375 int e = STATE_UNKNOWN;
375#ifdef __linux__ 376#ifdef __linux__
376 __u8 args[4]; 377 uint8_t args[4];
377 args[0] = WIN_SMART; 378 args[0] = WIN_SMART;
378 args[1] = val0; 379 args[1] = val0;
379 args[2] = smart_command[command].value; 380 args[2] = smart_command[command].value;
380 args[3] = 0; 381 args[3] = 0;
381 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { 382 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
382 e = STATE_CRITICAL; 383 e = STATE_CRITICAL;
383 if (show_error) 384 if (show_error) {
384 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); 385 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
386 }
385 } else { 387 } else {
386 e = STATE_OK; 388 e = STATE_OK;
387 if (show_error) 389 if (show_error) {
388 printf(_("OK - Command sent (%s)\n"), smart_command[command].text); 390 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
391 }
389 } 392 }
390 393
391#endif /* __linux__ */ 394#endif /* __linux__ */
@@ -401,20 +404,24 @@ int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_err
401 req.sec_count = val0; 404 req.sec_count = val0;
402 405
403 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 406 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
404 if (req.retsts != ATACMD_OK) 407 if (req.retsts != ATACMD_OK) {
405 errno = ENODEV; 408 errno = ENODEV;
406 if (req.cylinder != WDSMART_CYL) 409 }
410 if (req.cylinder != WDSMART_CYL) {
407 errno = ENODEV; 411 errno = ENODEV;
412 }
408 } 413 }
409 414
410 if (errno != 0) { 415 if (errno != 0) {
411 e = STATE_CRITICAL; 416 e = STATE_CRITICAL;
412 if (show_error) 417 if (show_error) {
413 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); 418 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
419 }
414 } else { 420 } else {
415 e = STATE_OK; 421 e = STATE_OK;
416 if (show_error) 422 if (show_error) {
417 printf(_("OK - Command sent (%s)\n"), smart_command[command].text); 423 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
424 }
418 } 425 }
419 426
420#endif /* __NetBSD__ */ 427#endif /* __NetBSD__ */
@@ -424,7 +431,7 @@ int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_err
424int smart_read_thresholds(int fd, thresholds_t *thresholds) { 431int smart_read_thresholds(int fd, thresholds_t *thresholds) {
425#ifdef __linux__ 432#ifdef __linux__
426 int e; 433 int e;
427 __u8 args[4 + 512]; 434 uint8_t args[4 + 512];
428 args[0] = WIN_SMART; 435 args[0] = WIN_SMART;
429 args[1] = 0; 436 args[1] = 0;
430 args[2] = SMART_READ_THRESHOLDS; 437 args[2] = SMART_READ_THRESHOLDS;
@@ -452,8 +459,9 @@ int smart_read_thresholds(int fd, thresholds_t *thresholds) {
452 req.cylinder = WDSMART_CYL; 459 req.cylinder = WDSMART_CYL;
453 460
454 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 461 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
455 if (req.retsts != ATACMD_OK) 462 if (req.retsts != ATACMD_OK) {
456 errno = ENODEV; 463 errno = ENODEV;
464 }
457 } 465 }
458 466
459 if (errno != 0) { 467 if (errno != 0) {
diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c
index 87818da6..77a33304 100644
--- a/plugins/check_ldap.c
+++ b/plugins/check_ldap.c
@@ -1,30 +1,30 @@
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-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_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"; 30char *progname = "check_ldap";
@@ -34,209 +34,183 @@ const char *email = "devel@monitoring-plugins.org";
34#include "common.h" 34#include "common.h"
35#include "netutils.h" 35#include "netutils.h"
36#include "utils.h" 36#include "utils.h"
37#include "check_ldap.d/config.h"
37 38
39#include "states.h"
38#include <lber.h> 40#include <lber.h>
39#define LDAP_DEPRECATED 1 41#define LDAP_DEPRECATED 1
40#include <ldap.h> 42#include <ldap.h>
41 43
42enum { 44enum {
43 UNDEFINED = 0,
44#ifdef HAVE_LDAP_SET_OPTION
45 DEFAULT_PROTOCOL = 2,
46#endif
47 DEFAULT_PORT = 389 45 DEFAULT_PORT = 389
48}; 46};
49 47
50static int process_arguments (int, char **); 48typedef struct {
51static int validate_arguments (void); 49 int errorcode;
52static void print_help (void); 50 check_ldap_config config;
53void print_usage (void); 51} check_ldap_config_wrapper;
54 52static check_ldap_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
55static char ld_defattr[] = "(objectclass=*)"; 53static check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper /*config_wrapper*/);
56static char *ld_attr = ld_defattr;
57static char *ld_host = NULL;
58static char *ld_base = NULL;
59static char *ld_passwd = NULL;
60static char *ld_binddn = NULL;
61static int ld_port = -1;
62#ifdef HAVE_LDAP_SET_OPTION
63static int ld_protocol = DEFAULT_PROTOCOL;
64#endif
65#ifndef LDAP_OPT_SUCCESS
66# define LDAP_OPT_SUCCESS LDAP_SUCCESS
67#endif
68static double warn_time = UNDEFINED;
69static double crit_time = UNDEFINED;
70static thresholds *entries_thresholds = NULL;
71static struct timeval tv;
72static char* warn_entries = NULL;
73static char* crit_entries = NULL;
74static bool starttls = false;
75static bool ssl_on_connect = false;
76static bool verbose = false;
77
78/* for ldap tls */
79
80static char *SERVICE = "LDAP";
81
82int
83main (int argc, char *argv[])
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 54
97 int tls; 55static void print_help(void);
98 int version=3; 56void print_usage(void);
99 57
100 int status_entries = STATE_OK; 58#ifndef LDAP_OPT_SUCCESS
101 int num_entries = 0; 59# define LDAP_OPT_SUCCESS LDAP_SUCCESS
60#endif
61static int verbose = 0;
102 62
103 setlocale (LC_ALL, ""); 63int main(int argc, char *argv[]) {
104 bindtextdomain (PACKAGE, LOCALEDIR); 64 setlocale(LC_ALL, "");
105 textdomain (PACKAGE); 65 bindtextdomain(PACKAGE, LOCALEDIR);
66 textdomain(PACKAGE);
106 67
107 if (strstr(argv[0],"check_ldaps")) { 68 if (strstr(argv[0], "check_ldaps")) {
108 xasprintf (&progname, "check_ldaps"); 69 xasprintf(&progname, "check_ldaps");
109 } 70 }
110 71
111 /* Parse extra opts if any */ 72 /* Parse extra opts if any */
112 argv=np_extra_opts (&argc, argv, progname); 73 argv = np_extra_opts(&argc, argv, progname);
113 74
114 if (process_arguments (argc, argv) == ERROR) 75 check_ldap_config_wrapper tmp_config = process_arguments(argc, argv);
115 usage4 (_("Could not parse arguments")); 76 if (tmp_config.errorcode == ERROR) {
77 usage4(_("Could not parse arguments"));
78 }
116 79
117 if (strstr(argv[0],"check_ldaps") && ! starttls && ! ssl_on_connect) 80 const check_ldap_config config = tmp_config.config;
118 starttls = true;
119 81
120 /* initialize alarm signal handling */ 82 /* initialize alarm signal handling */
121 signal (SIGALRM, socket_timeout_alarm_handler); 83 signal(SIGALRM, socket_timeout_alarm_handler);
122 84
123 /* set socket timeout */ 85 /* set socket timeout */
124 alarm (socket_timeout); 86 alarm(socket_timeout);
125 87
126 /* get the start time */ 88 /* get the start time */
127 gettimeofday (&tv, NULL); 89 struct timeval start_time;
90 gettimeofday(&start_time, NULL);
128 91
92 LDAP *ldap_connection;
129 /* initialize ldap */ 93 /* initialize ldap */
130#ifdef HAVE_LDAP_INIT 94#ifdef HAVE_LDAP_INIT
131 if (!(ld = ldap_init (ld_host, ld_port))) { 95 if (!(ldap_connection = ldap_init(config.ld_host, config.ld_port))) {
132 printf ("Could not connect to the server at port %i\n", ld_port); 96 printf("Could not connect to the server at port %i\n", config.ld_port);
133 return STATE_CRITICAL; 97 return STATE_CRITICAL;
134 } 98 }
135#else 99#else
136 if (!(ld = ldap_open (ld_host, ld_port))) { 100 if (!(ld = ldap_open(config.ld_host, config.ld_port))) {
137 if (verbose) 101 if (verbose) {
138 ldap_perror(ld, "ldap_open"); 102 ldap_perror(ldap_connection, "ldap_open");
139 printf (_("Could not connect to the server at port %i\n"), ld_port); 103 }
104 printf(_("Could not connect to the server at port %i\n"), config.ld_port);
140 return STATE_CRITICAL; 105 return STATE_CRITICAL;
141 } 106 }
142#endif /* HAVE_LDAP_INIT */ 107#endif /* HAVE_LDAP_INIT */
143 108
144#ifdef HAVE_LDAP_SET_OPTION 109#ifdef HAVE_LDAP_SET_OPTION
145 /* set ldap options */ 110 /* set ldap options */
146 if (ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &ld_protocol) != 111 if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) !=
147 LDAP_OPT_SUCCESS ) { 112 LDAP_OPT_SUCCESS) {
148 printf(_("Could not set protocol version %d\n"), ld_protocol); 113 printf(_("Could not set protocol version %d\n"), config.ld_protocol);
149 return STATE_CRITICAL; 114 return STATE_CRITICAL;
150 } 115 }
151#endif 116#endif
152 117
153 if (ld_port == LDAPS_PORT || ssl_on_connect) { 118 int version = 3;
154 xasprintf (&SERVICE, "LDAPS"); 119 int tls;
120 if (config.ld_port == LDAPS_PORT || config.ssl_on_connect) {
155#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS) 121#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)
156 /* ldaps: set option tls */ 122 /* ldaps: set option tls */
157 tls = LDAP_OPT_X_TLS_HARD; 123 tls = LDAP_OPT_X_TLS_HARD;
158 124
159 if (ldap_set_option (ld, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) 125 if (ldap_set_option(ldap_connection, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) {
160 { 126 if (verbose) {
161 if (verbose) 127 ldap_perror(ldap_connection, "ldaps_option");
162 ldap_perror(ld, "ldaps_option"); 128 }
163 printf (_("Could not init TLS at port %i!\n"), ld_port); 129 printf(_("Could not init TLS at port %i!\n"), config.ld_port);
164 return STATE_CRITICAL; 130 return STATE_CRITICAL;
165 } 131 }
166#else 132#else
167 printf (_("TLS not supported by the libraries!\n")); 133 printf(_("TLS not supported by the libraries!\n"));
168 return STATE_CRITICAL; 134 return STATE_CRITICAL;
169#endif /* LDAP_OPT_X_TLS */ 135#endif /* LDAP_OPT_X_TLS */
170 } else if (starttls) { 136 } else if (config.starttls) {
171 xasprintf (&SERVICE, "LDAP-TLS");
172#if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S) 137#if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S)
173 /* ldap with startTLS: set option version */ 138 /* ldap with startTLS: set option version */
174 if (ldap_get_option(ld,LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS ) 139 if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) ==
175 { 140 LDAP_OPT_SUCCESS) {
176 if (version < LDAP_VERSION3) 141 if (version < LDAP_VERSION3) {
177 {
178 version = LDAP_VERSION3; 142 version = LDAP_VERSION3;
179 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version); 143 ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version);
180 } 144 }
181 } 145 }
182 /* call start_tls */ 146 /* call start_tls */
183 if (ldap_start_tls_s(ld, NULL, NULL) != LDAP_SUCCESS) 147 if (ldap_start_tls_s(ldap_connection, NULL, NULL) != LDAP_SUCCESS) {
184 { 148 if (verbose) {
185 if (verbose) 149 ldap_perror(ldap_connection, "ldap_start_tls");
186 ldap_perror(ld, "ldap_start_tls"); 150 }
187 printf (_("Could not init startTLS at port %i!\n"), ld_port); 151 printf(_("Could not init startTLS at port %i!\n"), config.ld_port);
188 return STATE_CRITICAL; 152 return STATE_CRITICAL;
189 } 153 }
190#else 154#else
191 printf (_("startTLS not supported by the library, needs LDAPv3!\n")); 155 printf(_("startTLS not supported by the library, needs LDAPv3!\n"));
192 return STATE_CRITICAL; 156 return STATE_CRITICAL;
193#endif /* HAVE_LDAP_START_TLS_S */ 157#endif /* HAVE_LDAP_START_TLS_S */
194 } 158 }
195 159
196 /* bind to the ldap server */ 160 /* bind to the ldap server */
197 if (ldap_bind_s (ld, ld_binddn, ld_passwd, LDAP_AUTH_SIMPLE) != 161 if (ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE) !=
198 LDAP_SUCCESS) { 162 LDAP_SUCCESS) {
199 if (verbose) 163 if (verbose) {
200 ldap_perror(ld, "ldap_bind"); 164 ldap_perror(ldap_connection, "ldap_bind");
201 printf (_("Could not bind to the LDAP server\n")); 165 }
166 printf(_("Could not bind to the LDAP server\n"));
202 return STATE_CRITICAL; 167 return STATE_CRITICAL;
203 } 168 }
204 169
170 LDAPMessage *result;
171 int num_entries = 0;
205 /* do a search of all objectclasses in the base dn */ 172 /* 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) 173 if (ldap_search_s(ldap_connection, config.ld_base,
207 != LDAP_SUCCESS) { 174 (config.crit_entries != NULL || config.warn_entries != NULL)
208 if (verbose) 175 ? LDAP_SCOPE_SUBTREE
209 ldap_perror(ld, "ldap_search"); 176 : LDAP_SCOPE_BASE,
210 printf (_("Could not search/find objectclasses in %s\n"), ld_base); 177 config.ld_attr, NULL, 0, &result) != LDAP_SUCCESS) {
178 if (verbose) {
179 ldap_perror(ldap_connection, "ldap_search");
180 }
181 printf(_("Could not search/find objectclasses in %s\n"), config.ld_base);
211 return STATE_CRITICAL; 182 return STATE_CRITICAL;
212 } else if (crit_entries!=NULL || warn_entries!=NULL) { 183 }
213 num_entries = ldap_count_entries(ld, result); 184
185 if (config.crit_entries != NULL || config.warn_entries != NULL) {
186 num_entries = ldap_count_entries(ldap_connection, result);
214 } 187 }
215 188
216 /* unbind from the ldap server */ 189 /* unbind from the ldap server */
217 ldap_unbind (ld); 190 ldap_unbind(ldap_connection);
218 191
219 /* reset the alarm handler */ 192 /* reset the alarm handler */
220 alarm (0); 193 alarm(0);
221 194
222 /* calculate the elapsed time and compare to thresholds */ 195 /* calculate the elapsed time and compare to thresholds */
223 196
224 microsec = deltime (tv); 197 long microsec = deltime(start_time);
225 elapsed_time = (double)microsec / 1.0e6; 198 double elapsed_time = (double)microsec / 1.0e6;
226 199 mp_state_enum status = STATE_UNKNOWN;
227 if (crit_time!=UNDEFINED && elapsed_time>crit_time) 200 if (config.crit_time_set && elapsed_time > config.crit_time) {
228 status = STATE_CRITICAL; 201 status = STATE_CRITICAL;
229 else if (warn_time!=UNDEFINED && elapsed_time>warn_time) 202 } else if (config.warn_time_set && elapsed_time > config.warn_time) {
230 status = STATE_WARNING; 203 status = STATE_WARNING;
231 else 204 } else {
232 status = STATE_OK; 205 status = STATE_OK;
206 }
233 207
234 if(entries_thresholds != NULL) { 208 if (config.entries_thresholds != NULL) {
235 if (verbose) { 209 if (verbose) {
236 printf ("entries found: %d\n", num_entries); 210 printf("entries found: %d\n", num_entries);
237 print_thresholds("entry thresholds", entries_thresholds); 211 print_thresholds("entry thresholds", config.entries_thresholds);
238 } 212 }
239 status_entries = get_status(num_entries, entries_thresholds); 213 mp_state_enum status_entries = get_status(num_entries, config.entries_thresholds);
240 if (status_entries == STATE_CRITICAL) { 214 if (status_entries == STATE_CRITICAL) {
241 status = STATE_CRITICAL; 215 status = STATE_CRITICAL;
242 } else if (status != STATE_CRITICAL) { 216 } else if (status != STATE_CRITICAL) {
@@ -245,273 +219,281 @@ main (int argc, char *argv[])
245 } 219 }
246 220
247 /* print out the result */ 221 /* print out the result */
248 if (crit_entries!=NULL || warn_entries!=NULL) { 222 if (config.crit_entries != NULL || config.warn_entries != NULL) {
249 printf (_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), 223 printf(_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), state_text(status),
250 state_text (status), 224 num_entries, elapsed_time,
251 num_entries, 225 fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time,
252 elapsed_time, 226 config.crit_time_set, config.crit_time, true, 0, false, 0),
253 fperfdata ("time", elapsed_time, "s", 227 sperfdata("entries", (double)num_entries, "", config.warn_entries,
254 (int)warn_time, warn_time, 228 config.crit_entries, true, 0.0, false, 0.0));
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 { 229 } else {
262 printf (_("LDAP %s - %.3f seconds response time|%s\n"), 230 printf(_("LDAP %s - %.3f seconds response time|%s\n"), state_text(status), elapsed_time,
263 state_text (status), 231 fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time,
264 elapsed_time, 232 config.crit_time_set, config.crit_time, true, 0, false, 0));
265 fperfdata ("time", elapsed_time, "s",
266 (int)warn_time, warn_time,
267 (int)crit_time, crit_time,
268 true, 0, false, 0));
269 } 233 }
270 234
271 return status; 235 exit(status);
272} 236}
273 237
274/* process command-line arguments */ 238/* process command-line arguments */
275int 239check_ldap_config_wrapper process_arguments(int argc, char **argv) {
276process_arguments (int argc, char **argv)
277{
278 int c;
279
280 int option = 0;
281 /* initialize the long option struct */ 240 /* initialize the long option struct */
282 static struct option longopts[] = { 241 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
283 {"help", no_argument, 0, 'h'}, 242 {"version", no_argument, 0, 'V'},
284 {"version", no_argument, 0, 'V'}, 243 {"timeout", required_argument, 0, 't'},
285 {"timeout", required_argument, 0, 't'}, 244 {"hostname", required_argument, 0, 'H'},
286 {"hostname", required_argument, 0, 'H'}, 245 {"base", required_argument, 0, 'b'},
287 {"base", required_argument, 0, 'b'}, 246 {"attr", required_argument, 0, 'a'},
288 {"attr", required_argument, 0, 'a'}, 247 {"bind", required_argument, 0, 'D'},
289 {"bind", required_argument, 0, 'D'}, 248 {"pass", required_argument, 0, 'P'},
290 {"pass", required_argument, 0, 'P'},
291#ifdef HAVE_LDAP_SET_OPTION 249#ifdef HAVE_LDAP_SET_OPTION
292 {"ver2", no_argument, 0, '2'}, 250 {"ver2", no_argument, 0, '2'},
293 {"ver3", no_argument, 0, '3'}, 251 {"ver3", no_argument, 0, '3'},
294#endif 252#endif
295 {"starttls", no_argument, 0, 'T'}, 253 {"starttls", no_argument, 0, 'T'},
296 {"ssl", no_argument, 0, 'S'}, 254 {"ssl", no_argument, 0, 'S'},
297 {"use-ipv4", no_argument, 0, '4'}, 255 {"use-ipv4", no_argument, 0, '4'},
298 {"use-ipv6", no_argument, 0, '6'}, 256 {"use-ipv6", no_argument, 0, '6'},
299 {"port", required_argument, 0, 'p'}, 257 {"port", required_argument, 0, 'p'},
300 {"warn", required_argument, 0, 'w'}, 258 {"warn", required_argument, 0, 'w'},
301 {"crit", required_argument, 0, 'c'}, 259 {"crit", required_argument, 0, 'c'},
302 {"warn-entries", required_argument, 0, 'W'}, 260 {"warn-entries", required_argument, 0, 'W'},
303 {"crit-entries", required_argument, 0, 'C'}, 261 {"crit-entries", required_argument, 0, 'C'},
304 {"verbose", no_argument, 0, 'v'}, 262 {"verbose", no_argument, 0, 'v'},
305 {0, 0, 0, 0} 263 {0, 0, 0, 0}};
264
265 check_ldap_config_wrapper result = {
266 .errorcode = OK,
267 .config = check_ldap_config_init(),
306 }; 268 };
307 269
308 if (argc < 2) 270 if (argc < 2) {
309 return ERROR; 271 result.errorcode = ERROR;
272 return result;
273 }
310 274
311 for (c = 1; c < argc; c++) { 275 for (int index = 1; index < argc; index++) {
312 if (strcmp ("-to", argv[c]) == 0) 276 if (strcmp("-to", argv[index]) == 0) {
313 strcpy (argv[c], "-t"); 277 strcpy(argv[index], "-t");
278 }
314 } 279 }
315 280
281 int option = 0;
316 while (true) { 282 while (true) {
317 c = getopt_long (argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option); 283 int option_index =
284 getopt_long(argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option);
318 285
319 if (c == -1 || c == EOF) 286 if (option_index == -1 || option_index == EOF) {
320 break; 287 break;
288 }
321 289
322 switch (c) { 290 switch (option_index) {
323 case 'h': /* help */ 291 case 'h': /* help */
324 print_help (); 292 print_help();
325 exit (STATE_UNKNOWN); 293 exit(STATE_UNKNOWN);
326 case 'V': /* version */ 294 case 'V': /* version */
327 print_revision (progname, NP_VERSION); 295 print_revision(progname, NP_VERSION);
328 exit (STATE_UNKNOWN); 296 exit(STATE_UNKNOWN);
329 case 't': /* timeout period */ 297 case 't': /* timeout period */
330 if (!is_intnonneg (optarg)) 298 if (!is_intnonneg(optarg)) {
331 usage2 (_("Timeout interval must be a positive integer"), optarg); 299 usage2(_("Timeout interval must be a positive integer"), optarg);
332 else 300 } else {
333 socket_timeout = atoi (optarg); 301 socket_timeout = atoi(optarg);
302 }
334 break; 303 break;
335 case 'H': 304 case 'H':
336 ld_host = optarg; 305 result.config.ld_host = optarg;
337 break; 306 break;
338 case 'b': 307 case 'b':
339 ld_base = optarg; 308 result.config.ld_base = optarg;
340 break; 309 break;
341 case 'p': 310 case 'p':
342 ld_port = atoi (optarg); 311 result.config.ld_port = atoi(optarg);
343 break; 312 break;
344 case 'a': 313 case 'a':
345 ld_attr = optarg; 314 result.config.ld_attr = optarg;
346 break; 315 break;
347 case 'D': 316 case 'D':
348 ld_binddn = optarg; 317 result.config.ld_binddn = optarg;
349 break; 318 break;
350 case 'P': 319 case 'P':
351 ld_passwd = optarg; 320 result.config.ld_passwd = optarg;
352 break; 321 break;
353 case 'w': 322 case 'w':
354 warn_time = strtod (optarg, NULL); 323 result.config.warn_time_set = true;
324 result.config.warn_time = strtod(optarg, NULL);
355 break; 325 break;
356 case 'c': 326 case 'c':
357 crit_time = strtod (optarg, NULL); 327 result.config.crit_time_set = true;
328 result.config.crit_time = strtod(optarg, NULL);
358 break; 329 break;
359 case 'W': 330 case 'W':
360 warn_entries = optarg; 331 result.config.warn_entries = optarg;
361 break; 332 break;
362 case 'C': 333 case 'C':
363 crit_entries = optarg; 334 result.config.crit_entries = optarg;
364 break; 335 break;
365#ifdef HAVE_LDAP_SET_OPTION 336#ifdef HAVE_LDAP_SET_OPTION
366 case '2': 337 case '2':
367 ld_protocol = 2; 338 result.config.ld_protocol = 2;
368 break; 339 break;
369 case '3': 340 case '3':
370 ld_protocol = 3; 341 result.config.ld_protocol = 3;
371 break; 342 break;
372#endif 343#endif // HAVE_LDAP_SET_OPTION
373 case '4': 344 case '4':
374 address_family = AF_INET; 345 address_family = AF_INET;
375 break; 346 break;
376 case 'v': 347 case 'v':
377 verbose = true; 348 verbose++;
378 break; 349 break;
379 case 'T': 350 case 'T':
380 if (! ssl_on_connect) 351 if (!result.config.ssl_on_connect) {
381 starttls = true; 352 result.config.starttls = true;
382 else 353 } else {
383 usage_va(_("%s cannot be combined with %s"), "-T/--starttls", "-S/--ssl"); 354 usage_va(_("%s cannot be combined with %s"), "-T/--starttls", "-S/--ssl");
355 }
384 break; 356 break;
385 case 'S': 357 case 'S':
386 if (! starttls) { 358 if (!result.config.starttls) {
387 ssl_on_connect = true; 359 result.config.ssl_on_connect = true;
388 if (ld_port == -1) 360 if (result.config.ld_port == -1) {
389 ld_port = LDAPS_PORT; 361 result.config.ld_port = LDAPS_PORT;
390 } else 362 }
363 } else {
391 usage_va(_("%s cannot be combined with %s"), "-S/--ssl", "-T/--starttls"); 364 usage_va(_("%s cannot be combined with %s"), "-S/--ssl", "-T/--starttls");
365 }
392 break; 366 break;
393 case '6': 367 case '6':
394#ifdef USE_IPV6 368#ifdef USE_IPV6
395 address_family = AF_INET6; 369 address_family = AF_INET6;
396#else 370#else
397 usage (_("IPv6 support not available\n")); 371 usage(_("IPv6 support not available\n"));
398#endif 372#endif
399 break; 373 break;
400 default: 374 default:
401 usage5 (); 375 usage5();
402 } 376 }
403 } 377 }
404 378
405 c = optind; 379 int index = optind;
406 if (ld_host == NULL && is_host(argv[c])) 380 if ((result.config.ld_host == NULL) && is_host(argv[index])) {
407 ld_host = strdup (argv[c++]); 381 result.config.ld_host = strdup(argv[index++]);
382 }
408 383
409 if (ld_base == NULL && argv[c]) 384 if ((result.config.ld_base == NULL) && argv[index]) {
410 ld_base = strdup (argv[c++]); 385 result.config.ld_base = strdup(argv[index++]);
386 }
411 387
412 if (ld_port == -1) 388 if (result.config.ld_port == -1) {
413 ld_port = DEFAULT_PORT; 389 result.config.ld_port = DEFAULT_PORT;
390 }
414 391
415 return validate_arguments (); 392 if (strstr(argv[0], "check_ldaps") && !result.config.starttls &&
393 !result.config.ssl_on_connect) {
394 result.config.starttls = true;
395 }
396
397 return validate_arguments(result);
416} 398}
417 399
400check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper config_wrapper) {
401 if (config_wrapper.config.ld_host == NULL || strlen(config_wrapper.config.ld_host) == 0) {
402 usage4(_("Please specify the host name\n"));
403 }
418 404
419int 405 if (config_wrapper.config.ld_base == NULL) {
420validate_arguments () 406 usage4(_("Please specify the LDAP base\n"));
421{ 407 }
422 if (ld_host==NULL || strlen(ld_host)==0)
423 usage4 (_("Please specify the host name\n"));
424 408
425 if (ld_base==NULL) 409 if (config_wrapper.config.crit_entries != NULL || config_wrapper.config.warn_entries != NULL) {
426 usage4 (_("Please specify the LDAP base\n")); 410 set_thresholds(&config_wrapper.config.entries_thresholds,
411 config_wrapper.config.warn_entries, config_wrapper.config.crit_entries);
412 }
427 413
428 if (crit_entries!=NULL || warn_entries!=NULL) { 414 if (config_wrapper.config.ld_passwd == NULL) {
429 set_thresholds(&entries_thresholds, 415 config_wrapper.config.ld_passwd = getenv("LDAP_PASSWORD");
430 warn_entries, crit_entries);
431 } 416 }
432 if (ld_passwd==NULL)
433 ld_passwd = getenv("LDAP_PASSWORD");
434 417
435 return OK; 418 return config_wrapper;
436} 419}
437 420
438 421void print_help(void) {
439void
440print_help (void)
441{
442 char *myport; 422 char *myport;
443 xasprintf (&myport, "%d", DEFAULT_PORT); 423 xasprintf(&myport, "%d", DEFAULT_PORT);
444 424
445 print_revision (progname, NP_VERSION); 425 print_revision(progname, NP_VERSION);
446 426
447 printf ("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n"); 427 printf("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n");
448 printf (COPYRIGHT, copyright, email); 428 printf(COPYRIGHT, copyright, email);
449 429
450 printf ("\n\n"); 430 printf("\n\n");
451 431
452 print_usage (); 432 print_usage();
453 433
454 printf (UT_HELP_VRSN); 434 printf(UT_HELP_VRSN);
455 printf (UT_EXTRA_OPTS); 435 printf(UT_EXTRA_OPTS);
456 436
457 printf (UT_HOST_PORT, 'p', myport); 437 printf(UT_HOST_PORT, 'p', myport);
458 438
459 printf (UT_IPv46); 439 printf(UT_IPv46);
460 440
461 printf (" %s\n", "-a [--attr]"); 441 printf(" %s\n", "-a [--attr]");
462 printf (" %s\n", _("ldap attribute to search (default: \"(objectclass=*)\"")); 442 printf(" %s\n", _("ldap attribute to search (default: \"(objectclass=*)\""));
463 printf (" %s\n", "-b [--base]"); 443 printf(" %s\n", "-b [--base]");
464 printf (" %s\n", _("ldap base (eg. ou=my unit, o=my org, c=at")); 444 printf(" %s\n", _("ldap base (eg. ou=my unit, o=my org, c=at"));
465 printf (" %s\n", "-D [--bind]"); 445 printf(" %s\n", "-D [--bind]");
466 printf (" %s\n", _("ldap bind DN (if required)")); 446 printf(" %s\n", _("ldap bind DN (if required)"));
467 printf (" %s\n", "-P [--pass]"); 447 printf(" %s\n", "-P [--pass]");
468 printf (" %s\n", _("ldap password (if required, or set the password through environment variable 'LDAP_PASSWORD')")); 448 printf(" %s\n", _("ldap password (if required, or set the password through environment "
469 printf (" %s\n", "-T [--starttls]"); 449 "variable 'LDAP_PASSWORD')"));
470 printf (" %s\n", _("use starttls mechanism introduced in protocol version 3")); 450 printf(" %s\n", "-T [--starttls]");
471 printf (" %s\n", "-S [--ssl]"); 451 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); 452 printf(" %s\n", "-S [--ssl]");
453 printf(" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"),
454 LDAPS_PORT);
473 455
474#ifdef HAVE_LDAP_SET_OPTION 456#ifdef HAVE_LDAP_SET_OPTION
475 printf (" %s\n", "-2 [--ver2]"); 457 printf(" %s\n", "-2 [--ver2]");
476 printf (" %s\n", _("use ldap protocol version 2")); 458 printf(" %s\n", _("use ldap protocol version 2"));
477 printf (" %s\n", "-3 [--ver3]"); 459 printf(" %s\n", "-3 [--ver3]");
478 printf (" %s\n", _("use ldap protocol version 3")); 460 printf(" %s\n", _("use ldap protocol version 3"));
479 printf (" (%s %d)\n", _("default protocol version:"), DEFAULT_PROTOCOL); 461 printf(" (%s %d)\n", _("default protocol version:"), DEFAULT_PROTOCOL);
480#endif 462#endif
481 463
482 printf (UT_WARN_CRIT); 464 printf(UT_WARN_CRIT);
483 465
484 printf (" %s\n", "-W [--warn-entries]"); 466 printf(" %s\n", "-W [--warn-entries]");
485 printf (" %s\n", _("Number of found entries to result in warning status")); 467 printf(" %s\n", _("Number of found entries to result in warning status"));
486 printf (" %s\n", "-C [--crit-entries]"); 468 printf(" %s\n", "-C [--crit-entries]");
487 printf (" %s\n", _("Number of found entries to result in critical status")); 469 printf(" %s\n", _("Number of found entries to result in critical status"));
488 470
489 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 471 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
490 472
491 printf (UT_VERBOSE); 473 printf(UT_VERBOSE);
492 474
493 printf ("\n"); 475 printf("\n");
494 printf ("%s\n", _("Notes:")); 476 printf("%s\n", _("Notes:"));
495 printf (" %s\n", _("If this plugin is called via 'check_ldaps', method 'STARTTLS' will be")); 477 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); 478 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.")); 479 DEFAULT_PORT);
498 printf (" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' or '--ssl' flags")); 480 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.")); 481 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.")); 482 "or '--ssl' flags"));
483 printf(" %s\n", _("to define the behaviour explicitly instead."));
484 printf(" %s\n", _("The parameters --warn-entries and --crit-entries are optional."));
501 485
502 printf (UT_SUPPORT); 486 printf(UT_SUPPORT);
503} 487}
504 488
505void 489void print_usage(void) {
506print_usage (void) 490 printf("%s\n", _("Usage:"));
507{ 491 printf(" %s -H <host> -b <base_dn> [-p <port>] [-a <attr>] [-D <binddn>]", progname);
508 printf ("%s\n", _("Usage:")); 492 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 493#ifdef HAVE_LDAP_SET_OPTION
512 "\n [-2|-3] [-4|-6]" 494 "\n [-2|-3] [-4|-6]"
513#else 495#else
514 "" 496 ""
515#endif 497#endif
516 ); 498 );
517} 499}
diff --git a/plugins/check_ldap.d/config.h b/plugins/check_ldap.d/config.h
new file mode 100644
index 00000000..c8a40610
--- /dev/null
+++ b/plugins/check_ldap.d/config.h
@@ -0,0 +1,60 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7static char ld_defattr[] = "(objectclass=*)";
8
9enum {
10#ifdef HAVE_LDAP_SET_OPTION
11 DEFAULT_PROTOCOL = 2,
12#endif
13};
14
15typedef struct {
16 char *ld_host;
17 char *ld_base;
18 char *ld_passwd;
19 char *ld_binddn;
20 char *ld_attr;
21 int ld_port;
22 bool starttls;
23 bool ssl_on_connect;
24#ifdef HAVE_LDAP_SET_OPTION
25 int ld_protocol;
26#endif
27
28 char *warn_entries;
29 char *crit_entries;
30 thresholds *entries_thresholds;
31 bool warn_time_set;
32 double warn_time;
33 bool crit_time_set;
34 double crit_time;
35} check_ldap_config;
36
37check_ldap_config check_ldap_config_init() {
38 check_ldap_config tmp = {
39 .ld_host = NULL,
40 .ld_base = NULL,
41 .ld_passwd = NULL,
42 .ld_binddn = NULL,
43 .ld_attr = ld_defattr,
44 .ld_port = -1,
45 .starttls = false,
46 .ssl_on_connect = false,
47#ifdef HAVE_LDAP_SET_OPTION
48 .ld_protocol = DEFAULT_PROTOCOL,
49#endif
50
51 .warn_entries = NULL,
52 .crit_entries = NULL,
53 .entries_thresholds = NULL,
54 .warn_time_set = false,
55 .warn_time = 0,
56 .crit_time_set = false,
57 .crit_time = 0,
58 };
59 return tmp;
60}
diff --git a/plugins/check_load.c b/plugins/check_load.c
index 1431d130..644cd604 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 (option_index == -1 || option_index == EOF) {
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 632e66fb..4a17049a 100644
--- a/plugins/check_mrtg.c
+++ b/plugins/check_mrtg.c
@@ -35,21 +35,18 @@ const 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 "check_mrtg.d/config.h"
39
40typedef struct {
41 int errorcode;
42 check_mrtg_config config;
43} check_mrtg_config_wrapper;
44static check_mrtg_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
45static check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper /*config_wrapper*/);
38 46
39static int process_arguments(int /*argc*/, char ** /*argv*/);
40static int validate_arguments(void);
41static void print_help(void); 47static void print_help(void);
42void print_usage(void); 48void print_usage(void);
43 49
44static char *log_file = NULL;
45static int expire_minutes = 0;
46static bool use_average = true;
47static int variable_number = -1;
48static unsigned long value_warning_threshold = 0L;
49static unsigned long value_critical_threshold = 0L;
50static char *label;
51static char *units;
52
53int main(int argc, char **argv) { 50int main(int argc, char **argv) {
54 setlocale(LC_ALL, ""); 51 setlocale(LC_ALL, "");
55 bindtextdomain(PACKAGE, LOCALEDIR); 52 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -58,32 +55,37 @@ int main(int argc, char **argv) {
58 /* Parse extra opts if any */ 55 /* Parse extra opts if any */
59 argv = np_extra_opts(&argc, argv, progname); 56 argv = np_extra_opts(&argc, argv, progname);
60 57
61 if (process_arguments(argc, argv) == ERROR) 58 check_mrtg_config_wrapper tmp_config = process_arguments(argc, argv);
59 if (tmp_config.errorcode == ERROR) {
62 usage4(_("Could not parse arguments\n")); 60 usage4(_("Could not parse arguments\n"));
61 }
62
63 const check_mrtg_config config = tmp_config.config;
63 64
64 /* open the MRTG log file for reading */ 65 /* open the MRTG log file for reading */
65 FILE *mtrg_log_file = fopen(log_file, "r"); 66 FILE *mtrg_log_file = fopen(config.log_file, "r");
66 if (mtrg_log_file == NULL) { 67 if (mtrg_log_file == NULL) {
67 printf(_("Unable to open MRTG log file\n")); 68 printf(_("Unable to open MRTG log file\n"));
68 return STATE_UNKNOWN; 69 return STATE_UNKNOWN;
69 } 70 }
70 71
71 time_t timestamp = 0L; 72 time_t timestamp = 0;
72 unsigned long average_value_rate = 0L; 73 unsigned long average_value_rate = 0;
73 unsigned long maximum_value_rate = 0L; 74 unsigned long maximum_value_rate = 0;
74 char input_buffer[MAX_INPUT_BUFFER]; 75 char input_buffer[MAX_INPUT_BUFFER];
75 int line = 0; 76 int line = 0;
76 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mtrg_log_file)) { 77 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mtrg_log_file)) {
77
78 line++; 78 line++;
79 79
80 /* skip the first line of the log file */ 80 /* skip the first line of the log file */
81 if (line == 1) 81 if (line == 1) {
82 continue; 82 continue;
83 }
83 84
84 /* break out of read loop if we've passed the number of entries we want to read */ 85 /* break out of read loop if we've passed the number of entries we want to read */
85 if (line > 2) 86 if (line > 2) {
86 break; 87 break;
88 }
87 89
88 /* grab the timestamp */ 90 /* grab the timestamp */
89 char *temp_buffer = strtok(input_buffer, " "); 91 char *temp_buffer = strtok(input_buffer, " ");
@@ -91,23 +93,27 @@ int main(int argc, char **argv) {
91 93
92 /* grab the average value 1 rate */ 94 /* grab the average value 1 rate */
93 temp_buffer = strtok(NULL, " "); 95 temp_buffer = strtok(NULL, " ");
94 if (variable_number == 1) 96 if (config.variable_number == 1) {
95 average_value_rate = strtoul(temp_buffer, NULL, 10); 97 average_value_rate = strtoul(temp_buffer, NULL, 10);
98 }
96 99
97 /* grab the average value 2 rate */ 100 /* grab the average value 2 rate */
98 temp_buffer = strtok(NULL, " "); 101 temp_buffer = strtok(NULL, " ");
99 if (variable_number == 2) 102 if (config.variable_number == 2) {
100 average_value_rate = strtoul(temp_buffer, NULL, 10); 103 average_value_rate = strtoul(temp_buffer, NULL, 10);
104 }
101 105
102 /* grab the maximum value 1 rate */ 106 /* grab the maximum value 1 rate */
103 temp_buffer = strtok(NULL, " "); 107 temp_buffer = strtok(NULL, " ");
104 if (variable_number == 1) 108 if (config.variable_number == 1) {
105 maximum_value_rate = strtoul(temp_buffer, NULL, 10); 109 maximum_value_rate = strtoul(temp_buffer, NULL, 10);
110 }
106 111
107 /* grab the maximum value 2 rate */ 112 /* grab the maximum value 2 rate */
108 temp_buffer = strtok(NULL, " "); 113 temp_buffer = strtok(NULL, " ");
109 if (variable_number == 2) 114 if (config.variable_number == 2) {
110 maximum_value_rate = strtoul(temp_buffer, NULL, 10); 115 maximum_value_rate = strtoul(temp_buffer, NULL, 10);
116 }
111 } 117 }
112 118
113 /* close the log file */ 119 /* close the log file */
@@ -122,49 +128,69 @@ int main(int argc, char **argv) {
122 /* make sure the MRTG data isn't too old */ 128 /* make sure the MRTG data isn't too old */
123 time_t current_time; 129 time_t current_time;
124 time(&current_time); 130 time(&current_time);
125 if (expire_minutes > 0 && (current_time - timestamp) > (expire_minutes * 60)) { 131 if (config.expire_minutes > 0 && (current_time - timestamp) > (config.expire_minutes * 60)) {
126 printf(_("MRTG data has expired (%d minutes old)\n"), (int)((current_time - timestamp) / 60)); 132 printf(_("MRTG data has expired (%d minutes old)\n"),
133 (int)((current_time - timestamp) / 60));
127 return STATE_WARNING; 134 return STATE_WARNING;
128 } 135 }
129 136
130 unsigned long rate = 0L; 137 unsigned long rate = 0L;
131 /* else check the incoming/outgoing rates */ 138 /* else check the incoming/outgoing rates */
132 if (use_average) 139 if (config.use_average) {
133 rate = average_value_rate; 140 rate = average_value_rate;
134 else 141 } else {
135 rate = maximum_value_rate; 142 rate = maximum_value_rate;
143 }
136 144
137 int result = STATE_OK; 145 int result = STATE_OK;
138 if (rate > value_critical_threshold) 146 if (config.value_critical_threshold_set && rate > config.value_critical_threshold) {
139 result = STATE_CRITICAL; 147 result = STATE_CRITICAL;
140 else if (rate > value_warning_threshold) 148 } else if (config.value_warning_threshold_set && rate > config.value_warning_threshold) {
141 result = STATE_WARNING; 149 result = STATE_WARNING;
150 }
142 151
143 printf("%s. %s = %lu %s|%s\n", (use_average) ? _("Avg") : _("Max"), label, rate, units, 152 printf("%s. %s = %lu %s|%s\n", (config.use_average) ? _("Avg") : _("Max"), config.label, rate,
144 perfdata(label, (long)rate, units, (int)value_warning_threshold, (long)value_warning_threshold, (int)value_critical_threshold, 153 config.units,
145 (long)value_critical_threshold, 0, 0, 0, 0)); 154 perfdata(config.label, (long)rate, config.units, config.value_warning_threshold_set,
155 (long)config.value_warning_threshold, config.value_critical_threshold_set,
156 (long)config.value_critical_threshold, 0, 0, 0, 0));
146 157
147 return result; 158 return result;
148} 159}
149 160
150/* process command-line arguments */ 161/* process command-line arguments */
151int process_arguments(int argc, char **argv) { 162check_mrtg_config_wrapper process_arguments(int argc, char **argv) {
152 static struct option longopts[] = { 163 static struct option longopts[] = {{"logfile", required_argument, 0, 'F'},
153 {"logfile", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'}, {"aggregation", required_argument, 0, 'a'}, 164 {"expires", required_argument, 0, 'e'},
154 {"variable", required_argument, 0, 'v'}, {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, 165 {"aggregation", required_argument, 0, 'a'},
155 {"label", required_argument, 0, 'l'}, {"units", required_argument, 0, 'u'}, {"variable", required_argument, 0, 'v'}, 166 {"variable", required_argument, 0, 'v'},
156 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; 167 {"critical", required_argument, 0, 'c'},
157 168 {"warning", required_argument, 0, 'w'},
158 if (argc < 2) 169 {"label", required_argument, 0, 'l'},
159 return ERROR; 170 {"units", required_argument, 0, 'u'},
171 {"variable", required_argument, 0, 'v'},
172 {"version", no_argument, 0, 'V'},
173 {"help", no_argument, 0, 'h'},
174 {0, 0, 0, 0}};
175
176 check_mrtg_config_wrapper result = {
177 .errorcode = OK,
178 .config = check_mrtg_config_init(),
179 };
180
181 if (argc < 2) {
182 result.errorcode = ERROR;
183 return result;
184 }
160 185
161 for (int i = 1; i < argc; i++) { 186 for (int i = 1; i < argc; i++) {
162 if (strcmp("-to", argv[i]) == 0) 187 if (strcmp("-to", argv[i]) == 0) {
163 strcpy(argv[i], "-t"); 188 strcpy(argv[i], "-t");
164 else if (strcmp("-wt", argv[i]) == 0) 189 } else if (strcmp("-wt", argv[i]) == 0) {
165 strcpy(argv[i], "-w"); 190 strcpy(argv[i], "-w");
166 else if (strcmp("-ct", argv[i]) == 0) 191 } else if (strcmp("-ct", argv[i]) == 0) {
167 strcpy(argv[i], "-c"); 192 strcpy(argv[i], "-c");
193 }
168 } 194 }
169 195
170 int option_char; 196 int option_char;
@@ -172,38 +198,39 @@ int process_arguments(int argc, char **argv) {
172 while (1) { 198 while (1) {
173 option_char = getopt_long(argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, &option); 199 option_char = getopt_long(argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, &option);
174 200
175 if (option_char == -1 || option_char == EOF) 201 if (option_char == -1 || option_char == EOF) {
176 break; 202 break;
203 }
177 204
178 switch (option_char) { 205 switch (option_char) {
179 case 'F': /* input file */ 206 case 'F': /* input file */
180 log_file = optarg; 207 result.config.log_file = optarg;
181 break; 208 break;
182 case 'e': /* ups name */ 209 case 'e': /* ups name */
183 expire_minutes = atoi(optarg); 210 result.config.expire_minutes = atoi(optarg);
184 break; 211 break;
185 case 'a': /* port */ 212 case 'a': /* port */
186 if (!strcmp(optarg, "MAX")) 213 result.config.use_average = (bool)(strcmp(optarg, "MAX"));
187 use_average = false;
188 else
189 use_average = true;
190 break; 214 break;
191 case 'v': 215 case 'v':
192 variable_number = atoi(optarg); 216 result.config.variable_number = atoi(optarg);
193 if (variable_number < 1 || variable_number > 2) 217 if (result.config.variable_number < 1 || result.config.variable_number > 2) {
194 usage4(_("Invalid variable number")); 218 usage4(_("Invalid variable number"));
219 }
195 break; 220 break;
196 case 'w': /* critical time threshold */ 221 case 'w': /* critical time threshold */
197 value_warning_threshold = strtoul(optarg, NULL, 10); 222 result.config.value_warning_threshold_set = true;
223 result.config.value_warning_threshold = strtoul(optarg, NULL, 10);
198 break; 224 break;
199 case 'c': /* warning time threshold */ 225 case 'c': /* warning time threshold */
200 value_critical_threshold = strtoul(optarg, NULL, 10); 226 result.config.value_critical_threshold_set = true;
227 result.config.value_critical_threshold = strtoul(optarg, NULL, 10);
201 break; 228 break;
202 case 'l': /* label */ 229 case 'l': /* label */
203 label = optarg; 230 result.config.label = optarg;
204 break; 231 break;
205 case 'u': /* timeout */ 232 case 'u': /* timeout */
206 units = optarg; 233 result.config.units = optarg;
207 break; 234 break;
208 case 'V': /* version */ 235 case 'V': /* version */
209 print_revision(progname, NP_VERSION); 236 print_revision(progname, NP_VERSION);
@@ -217,63 +244,71 @@ int process_arguments(int argc, char **argv) {
217 } 244 }
218 245
219 option_char = optind; 246 option_char = optind;
220 if (log_file == NULL && argc > option_char) { 247 if (result.config.log_file == NULL && argc > option_char) {
221 log_file = argv[option_char++]; 248 result.config.log_file = argv[option_char++];
222 } 249 }
223 250
224 if (expire_minutes <= 0 && argc > option_char) { 251 if (result.config.expire_minutes <= 0 && argc > option_char) {
225 if (is_intpos(argv[option_char])) 252 if (is_intpos(argv[option_char])) {
226 expire_minutes = atoi(argv[option_char++]); 253 result.config.expire_minutes = atoi(argv[option_char++]);
227 else 254 } else {
228 die(STATE_UNKNOWN, _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"), argv[option_char], progname); 255 die(STATE_UNKNOWN,
256 _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"),
257 argv[option_char], progname);
258 }
229 } 259 }
230 260
231 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) { 261 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) {
232 use_average = false; 262 result.config.use_average = false;
233 option_char++; 263 option_char++;
234 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) { 264 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) {
235 use_average = true; 265 result.config.use_average = true;
236 option_char++; 266 option_char++;
237 } 267 }
238 268
239 if (argc > option_char && variable_number == -1) { 269 if (argc > option_char && result.config.variable_number == -1) {
240 variable_number = atoi(argv[option_char++]); 270 result.config.variable_number = atoi(argv[option_char++]);
241 if (variable_number < 1 || variable_number > 2) { 271 if (result.config.variable_number < 1 || result.config.variable_number > 2) {
242 printf("%s :", argv[option_char]); 272 printf("%s :", argv[option_char]);
243 usage(_("Invalid variable number\n")); 273 usage(_("Invalid variable number\n"));
244 } 274 }
245 } 275 }
246 276
247 if (argc > option_char && value_warning_threshold == 0) { 277 if (argc > option_char && !result.config.value_warning_threshold_set) {
248 value_warning_threshold = strtoul(argv[option_char++], NULL, 10); 278 result.config.value_warning_threshold_set = true;
279 result.config.value_warning_threshold = strtoul(argv[option_char++], NULL, 10);
249 } 280 }
250 281
251 if (argc > option_char && value_critical_threshold == 0) { 282 if (argc > option_char && !result.config.value_critical_threshold_set) {
252 value_critical_threshold = strtoul(argv[option_char++], NULL, 10); 283 result.config.value_critical_threshold_set = true;
284 result.config.value_critical_threshold = strtoul(argv[option_char++], NULL, 10);
253 } 285 }
254 286
255 if (argc > option_char && strlen(label) == 0) { 287 if (argc > option_char && strlen(result.config.label) == 0) {
256 label = argv[option_char++]; 288 result.config.label = argv[option_char++];
257 } 289 }
258 290
259 if (argc > option_char && strlen(units) == 0) { 291 if (argc > option_char && strlen(result.config.units) == 0) {
260 units = argv[option_char++]; 292 result.config.units = argv[option_char++];
261 } 293 }
262 294
263 return validate_arguments(); 295 return validate_arguments(result);
264} 296}
265 297
266int validate_arguments(void) { 298check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper config_wrapper) {
267 if (variable_number == -1) 299 if (config_wrapper.config.variable_number == -1) {
268 usage4(_("You must supply the variable number")); 300 usage4(_("You must supply the variable number"));
301 }
269 302
270 if (label == NULL) 303 if (config_wrapper.config.label == NULL) {
271 label = strdup("value"); 304 config_wrapper.config.label = strdup("value");
305 }
272 306
273 if (units == NULL) 307 if (config_wrapper.config.units == NULL) {
274 units = strdup(""); 308 config_wrapper.config.units = strdup("");
309 }
275 310
276 return OK; 311 return config_wrapper;
277} 312}
278 313
279void print_help(void) { 314void print_help(void) {
@@ -311,25 +346,32 @@ void print_help(void) {
311 printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")")); 346 printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")"));
312 347
313 printf("\n"); 348 printf("\n");
314 printf(" %s\n", _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If")); 349 printf(" %s\n",
350 _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If"));
315 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"));
316 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"));
317 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."));
318 354
319 printf("\n"); 355 printf("\n");
320 printf(" %s\n", _("This plugin is useful for monitoring MRTG data that does not correspond to")); 356 printf(" %s\n",
321 printf(" %s\n", _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth).")); 357 _("This plugin is useful for monitoring MRTG data that does not correspond to"));
322 printf(" %s\n", _("It can be used to monitor any kind of data that MRTG is monitoring - errors,")); 358 printf(" %s\n",
323 printf(" %s\n", _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows")); 359 _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth)."));
360 printf(" %s\n",
361 _("It can be used to monitor any kind of data that MRTG is monitoring - errors,"));
362 printf(" %s\n",
363 _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows"));
324 printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and")); 364 printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and"));
325 printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well.")); 365 printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well."));
326 366
327 printf("%s\n", _("Notes:")); 367 printf("%s\n", _("Notes:"));
328 printf(" %s\n", _("- This plugin only monitors one of the two variables stored in the MRTG log")); 368 printf(" %s\n",
369 _("- This plugin only monitors one of the two variables stored in the MRTG log"));
329 printf(" %s\n", _("file. If you want to monitor both values you will have to define two")); 370 printf(" %s\n", _("file. If you want to monitor both values you will have to define two"));
330 printf(" %s\n", _("commands with different values for the <variable> argument. Of course,")); 371 printf(" %s\n", _("commands with different values for the <variable> argument. Of course,"));
331 printf(" %s\n", _("you can always hack the code to make this plugin work for you...")); 372 printf(" %s\n", _("you can always hack the code to make this plugin work for you..."));
332 printf(" %s\n", _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from")); 373 printf(" %s\n",
374 _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from"));
333 printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"); 375 printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html");
334 376
335 printf(UT_SUPPORT); 377 printf(UT_SUPPORT);
diff --git a/plugins/check_mrtg.d/config.h b/plugins/check_mrtg.d/config.h
new file mode 100644
index 00000000..96b849a2
--- /dev/null
+++ b/plugins/check_mrtg.d/config.h
@@ -0,0 +1,36 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7typedef struct {
8 bool use_average;
9 int variable_number;
10 int expire_minutes;
11 char *label;
12 char *units;
13 char *log_file;
14
15 bool value_warning_threshold_set;
16 unsigned long value_warning_threshold;
17 bool value_critical_threshold_set;
18 unsigned long value_critical_threshold;
19} check_mrtg_config;
20
21check_mrtg_config check_mrtg_config_init() {
22 check_mrtg_config tmp = {
23 .use_average = true,
24 .variable_number = -1,
25 .expire_minutes = 0,
26 .label = NULL,
27 .units = NULL,
28 .log_file = NULL,
29
30 .value_warning_threshold_set = false,
31 .value_warning_threshold = 0,
32 .value_critical_threshold_set = false,
33 .value_critical_threshold = 0,
34 };
35 return tmp;
36}
diff --git a/plugins/check_mrtgtraf.c b/plugins/check_mrtgtraf.c
index e5a2e2ad..10ce936f 100644
--- a/plugins/check_mrtgtraf.c
+++ b/plugins/check_mrtgtraf.c
@@ -29,25 +29,23 @@
29 * 29 *
30 *****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "common.h"
33#include "utils.h"
34
35const char *progname = "check_mrtgtraf"; 32const char *progname = "check_mrtgtraf";
36const char *copyright = "1999-2024"; 33const char *copyright = "1999-2024";
37const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
38 35
39static int process_arguments(int /*argc*/, char ** /*argv*/); 36#include "check_mrtgtraf.d/config.h"
37#include "common.h"
38#include "utils.h"
39
40typedef struct {
41 int errorcode;
42 check_mrtgtraf_config config;
43} check_mrtgtraf_config_wrapper;
44
45static check_mrtgtraf_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
40static void print_help(void); 46static void print_help(void);
41void print_usage(void); 47void print_usage(void);
42 48
43static char *log_file = NULL;
44static int expire_minutes = -1;
45static bool use_average = true;
46static unsigned long incoming_warning_threshold = 0L;
47static unsigned long incoming_critical_threshold = 0L;
48static unsigned long outgoing_warning_threshold = 0L;
49static unsigned long outgoing_critical_threshold = 0L;
50
51int main(int argc, char **argv) { 49int main(int argc, char **argv) {
52 setlocale(LC_ALL, ""); 50 setlocale(LC_ALL, "");
53 bindtextdomain(PACKAGE, LOCALEDIR); 51 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -56,13 +54,18 @@ int main(int argc, char **argv) {
56 /* Parse extra opts if any */ 54 /* Parse extra opts if any */
57 argv = np_extra_opts(&argc, argv, progname); 55 argv = np_extra_opts(&argc, argv, progname);
58 56
59 if (process_arguments(argc, argv) == ERROR) 57 check_mrtgtraf_config_wrapper tmp_config = process_arguments(argc, argv);
58 if (tmp_config.errorcode == ERROR) {
60 usage4(_("Could not parse arguments")); 59 usage4(_("Could not parse arguments"));
60 }
61
62 const check_mrtgtraf_config config = tmp_config.config;
61 63
62 /* open the MRTG log file for reading */ 64 /* open the MRTG log file for reading */
63 FILE *mrtg_log_file_ptr = fopen(log_file, "r"); 65 FILE *mrtg_log_file_ptr = fopen(config.log_file, "r");
64 if (mrtg_log_file_ptr == NULL) 66 if (mrtg_log_file_ptr == NULL) {
65 usage4(_("Unable to open MRTG log file")); 67 usage4(_("Unable to open MRTG log file"));
68 }
66 69
67 time_t timestamp = 0L; 70 time_t timestamp = 0L;
68 char input_buffer[MAX_INPUT_BUFFER]; 71 char input_buffer[MAX_INPUT_BUFFER];
@@ -76,13 +79,15 @@ int main(int argc, char **argv) {
76 line++; 79 line++;
77 80
78 /* skip the first line of the log file */ 81 /* skip the first line of the log file */
79 if (line == 1) 82 if (line == 1) {
80 continue; 83 continue;
84 }
81 85
82 /* break out of read loop */ 86 /* break out of read loop */
83 /* if we've passed the number of entries we want to read */ 87 /* if we've passed the number of entries we want to read */
84 if (line > 2) 88 if (line > 2) {
85 break; 89 break;
90 }
86 91
87 /* grab the timestamp */ 92 /* grab the timestamp */
88 char *temp_buffer = strtok(input_buffer, " "); 93 char *temp_buffer = strtok(input_buffer, " ");
@@ -109,19 +114,22 @@ int main(int argc, char **argv) {
109 fclose(mrtg_log_file_ptr); 114 fclose(mrtg_log_file_ptr);
110 115
111 /* if we couldn't read enough data, return an unknown error */ 116 /* if we couldn't read enough data, return an unknown error */
112 if (line <= 2) 117 if (line <= 2) {
113 usage4(_("Unable to process MRTG log file")); 118 usage4(_("Unable to process MRTG log file"));
119 }
114 120
115 /* make sure the MRTG data isn't too old */ 121 /* make sure the MRTG data isn't too old */
116 time_t current_time; 122 time_t current_time;
117 time(&current_time); 123 time(&current_time);
118 if ((expire_minutes > 0) && (current_time - timestamp) > (expire_minutes * 60)) 124 if ((config.expire_minutes > 0) && (current_time - timestamp) > (config.expire_minutes * 60)) {
119 die(STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"), (int)((current_time - timestamp) / 60)); 125 die(STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"),
126 (int)((current_time - timestamp) / 60));
127 }
120 128
121 unsigned long incoming_rate = 0L; 129 unsigned long incoming_rate = 0L;
122 unsigned long outgoing_rate = 0L; 130 unsigned long outgoing_rate = 0L;
123 /* else check the incoming/outgoing rates */ 131 /* else check the incoming/outgoing rates */
124 if (use_average) { 132 if (config.use_average) {
125 incoming_rate = average_incoming_rate; 133 incoming_rate = average_incoming_rate;
126 outgoing_rate = average_outgoing_rate; 134 outgoing_rate = average_outgoing_rate;
127 } else { 135 } else {
@@ -166,24 +174,31 @@ int main(int argc, char **argv) {
166 /* report outgoing traffic in MBytes/sec */ 174 /* report outgoing traffic in MBytes/sec */
167 else { 175 else {
168 strcpy(outgoing_speed_rating, "MB"); 176 strcpy(outgoing_speed_rating, "MB");
169 adjusted_outgoing_rate = (double)(outgoing_rate / 1024.0 / 1024.0); 177 adjusted_outgoing_rate = (outgoing_rate / 1024.0 / 1024.0);
170 } 178 }
171 179
172 int result = STATE_OK; 180 int result = STATE_OK;
173 if (incoming_rate > incoming_critical_threshold || outgoing_rate > outgoing_critical_threshold) { 181 if (incoming_rate > config.incoming_critical_threshold ||
182 outgoing_rate > config.outgoing_critical_threshold) {
174 result = STATE_CRITICAL; 183 result = STATE_CRITICAL;
175 } else if (incoming_rate > incoming_warning_threshold || outgoing_rate > outgoing_warning_threshold) { 184 } else if (incoming_rate > config.incoming_warning_threshold ||
185 outgoing_rate > config.outgoing_warning_threshold) {
176 result = STATE_WARNING; 186 result = STATE_WARNING;
177 } 187 }
178 188
179 char *error_message; 189 char *error_message;
180 xasprintf(&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"), (use_average) ? _("Avg") : _("Max"), 190 xasprintf(&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"),
181 adjusted_incoming_rate, incoming_speed_rating, (use_average) ? _("Avg") : _("Max"), adjusted_outgoing_rate, 191 (config.use_average) ? _("Avg") : _("Max"), adjusted_incoming_rate,
182 outgoing_speed_rating, 192 incoming_speed_rating, (config.use_average) ? _("Avg") : _("Max"),
183 fperfdata("in", adjusted_incoming_rate, incoming_speed_rating, (int)incoming_warning_threshold, incoming_warning_threshold, 193 adjusted_outgoing_rate, outgoing_speed_rating,
184 (int)incoming_critical_threshold, incoming_critical_threshold, true, 0, false, 0), 194 fperfdata("in", adjusted_incoming_rate, incoming_speed_rating,
185 fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating, (int)outgoing_warning_threshold, outgoing_warning_threshold, 195 (int)config.incoming_warning_threshold, config.incoming_warning_threshold,
186 (int)outgoing_critical_threshold, outgoing_critical_threshold, true, 0, false, 0)); 196 (int)config.incoming_critical_threshold, config.incoming_critical_threshold,
197 true, 0, false, 0),
198 fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating,
199 (int)config.outgoing_warning_threshold, config.outgoing_warning_threshold,
200 (int)config.outgoing_critical_threshold, config.outgoing_critical_threshold,
201 true, 0, false, 0));
187 202
188 printf(_("Traffic %s - %s\n"), state_text(result), error_message); 203 printf(_("Traffic %s - %s\n"), state_text(result), error_message);
189 204
@@ -191,7 +206,7 @@ int main(int argc, char **argv) {
191} 206}
192 207
193/* process command-line arguments */ 208/* process command-line arguments */
194int process_arguments(int argc, char **argv) { 209check_mrtgtraf_config_wrapper process_arguments(int argc, char **argv) {
195 static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, 210 static struct option longopts[] = {{"filename", required_argument, 0, 'F'},
196 {"expires", required_argument, 0, 'e'}, 211 {"expires", required_argument, 0, 'e'},
197 {"aggregation", required_argument, 0, 'a'}, 212 {"aggregation", required_argument, 0, 'a'},
@@ -201,44 +216,51 @@ int process_arguments(int argc, char **argv) {
201 {"help", no_argument, 0, 'h'}, 216 {"help", no_argument, 0, 'h'},
202 {0, 0, 0, 0}}; 217 {0, 0, 0, 0}};
203 218
204 if (argc < 2) 219 check_mrtgtraf_config_wrapper result = {
205 return ERROR; 220 .errorcode = OK,
221 .config = check_mrtgtraf_config_init(),
222 };
223 if (argc < 2) {
224 result.errorcode = ERROR;
225 return result;
226 }
206 227
207 for (int i = 1; i < argc; i++) { 228 for (int i = 1; i < argc; i++) {
208 if (strcmp("-to", argv[i]) == 0) 229 if (strcmp("-to", argv[i]) == 0) {
209 strcpy(argv[i], "-t"); 230 strcpy(argv[i], "-t");
210 else if (strcmp("-wt", argv[i]) == 0) 231 } else if (strcmp("-wt", argv[i]) == 0) {
211 strcpy(argv[i], "-w"); 232 strcpy(argv[i], "-w");
212 else if (strcmp("-ct", argv[i]) == 0) 233 } else if (strcmp("-ct", argv[i]) == 0) {
213 strcpy(argv[i], "-c"); 234 strcpy(argv[i], "-c");
235 }
214 } 236 }
215 237
216 int option_char; 238 int option_char;
217 int option = 0; 239 int option = 0;
218 while (1) { 240 while (true) {
219 option_char = getopt_long(argc, argv, "hVF:e:a:c:w:", longopts, &option); 241 option_char = getopt_long(argc, argv, "hVF:e:a:c:w:", longopts, &option);
220 242
221 if (option_char == -1 || option_char == EOF) 243 if (option_char == -1 || option_char == EOF) {
222 break; 244 break;
245 }
223 246
224 switch (option_char) { 247 switch (option_char) {
225 case 'F': /* input file */ 248 case 'F': /* input file */
226 log_file = optarg; 249 result.config.log_file = optarg;
227 break; 250 break;
228 case 'e': /* expiration time */ 251 case 'e': /* expiration time */
229 expire_minutes = atoi(optarg); 252 result.config.expire_minutes = atoi(optarg);
230 break; 253 break;
231 case 'a': /* aggregation (AVE or MAX) */ 254 case 'a': /* aggregation (AVE or MAX) */
232 if (!strcmp(optarg, "MAX")) 255 result.config.use_average = (bool)(strcmp(optarg, "MAX"));
233 use_average = false;
234 else
235 use_average = true;
236 break; 256 break;
237 case 'c': /* warning threshold */ 257 case 'c': /* warning threshold */
238 sscanf(optarg, "%lu,%lu", &incoming_critical_threshold, &outgoing_critical_threshold); 258 sscanf(optarg, "%lu,%lu", &result.config.incoming_critical_threshold,
259 &result.config.outgoing_critical_threshold);
239 break; 260 break;
240 case 'w': /* critical threshold */ 261 case 'w': /* critical threshold */
241 sscanf(optarg, "%lu,%lu", &incoming_warning_threshold, &outgoing_warning_threshold); 262 sscanf(optarg, "%lu,%lu", &result.config.incoming_warning_threshold,
263 &result.config.outgoing_warning_threshold);
242 break; 264 break;
243 case 'V': /* version */ 265 case 'V': /* version */
244 print_revision(progname, NP_VERSION); 266 print_revision(progname, NP_VERSION);
@@ -252,39 +274,39 @@ int process_arguments(int argc, char **argv) {
252 } 274 }
253 275
254 option_char = optind; 276 option_char = optind;
255 if (argc > option_char && log_file == NULL) { 277 if (argc > option_char && result.config.log_file == NULL) {
256 log_file = argv[option_char++]; 278 result.config.log_file = argv[option_char++];
257 } 279 }
258 280
259 if (argc > option_char && expire_minutes == -1) { 281 if (argc > option_char && result.config.expire_minutes == -1) {
260 expire_minutes = atoi(argv[option_char++]); 282 result.config.expire_minutes = atoi(argv[option_char++]);
261 } 283 }
262 284
263 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) { 285 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) {
264 use_average = false; 286 result.config.use_average = false;
265 option_char++; 287 option_char++;
266 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) { 288 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) {
267 use_average = true; 289 result.config.use_average = true;
268 option_char++; 290 option_char++;
269 } 291 }
270 292
271 if (argc > option_char && incoming_warning_threshold == 0) { 293 if (argc > option_char && result.config.incoming_warning_threshold == 0) {
272 incoming_warning_threshold = strtoul(argv[option_char++], NULL, 10); 294 result.config.incoming_warning_threshold = strtoul(argv[option_char++], NULL, 10);
273 } 295 }
274 296
275 if (argc > option_char && incoming_critical_threshold == 0) { 297 if (argc > option_char && result.config.incoming_critical_threshold == 0) {
276 incoming_critical_threshold = strtoul(argv[option_char++], NULL, 10); 298 result.config.incoming_critical_threshold = strtoul(argv[option_char++], NULL, 10);
277 } 299 }
278 300
279 if (argc > option_char && outgoing_warning_threshold == 0) { 301 if (argc > option_char && result.config.outgoing_warning_threshold == 0) {
280 outgoing_warning_threshold = strtoul(argv[option_char++], NULL, 10); 302 result.config.outgoing_warning_threshold = strtoul(argv[option_char++], NULL, 10);
281 } 303 }
282 304
283 if (argc > option_char && outgoing_critical_threshold == 0) { 305 if (argc > option_char && result.config.outgoing_critical_threshold == 0) {
284 outgoing_critical_threshold = strtoul(argv[option_char++], NULL, 10); 306 result.config.outgoing_critical_threshold = strtoul(argv[option_char++], NULL, 10);
285 } 307 }
286 308
287 return OK; 309 return result;
288} 310}
289 311
290void print_help(void) { 312void print_help(void) {
diff --git a/plugins/check_mrtgtraf.d/config.h b/plugins/check_mrtgtraf.d/config.h
new file mode 100644
index 00000000..94929ff7
--- /dev/null
+++ b/plugins/check_mrtgtraf.d/config.h
@@ -0,0 +1,30 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7typedef struct {
8 char *log_file;
9 int expire_minutes;
10 bool use_average;
11 unsigned long incoming_warning_threshold;
12 unsigned long incoming_critical_threshold;
13 unsigned long outgoing_warning_threshold;
14 unsigned long outgoing_critical_threshold;
15
16} check_mrtgtraf_config;
17
18check_mrtgtraf_config check_mrtgtraf_config_init() {
19 check_mrtgtraf_config tmp = {
20 .log_file = NULL,
21 .expire_minutes = -1,
22 .use_average = true,
23
24 .incoming_warning_threshold = 0,
25 .incoming_critical_threshold = 0,
26 .outgoing_warning_threshold = 0,
27 .outgoing_critical_threshold = 0,
28 };
29 return tmp;
30}
diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c
index 2b6cfeaf..3d7ec4cd 100644
--- a/plugins/check_mysql.c
+++ b/plugins/check_mysql.c
@@ -40,66 +40,44 @@ const char *email = "devel@monitoring-plugins.org";
40#include "utils.h" 40#include "utils.h"
41#include "utils_base.h" 41#include "utils_base.h"
42#include "netutils.h" 42#include "netutils.h"
43#include "check_mysql.d/config.h"
43 44
44#include <mysql.h> 45#include <mysql.h>
45#include <mysqld_error.h> 46#include <mysqld_error.h>
46#include <errmsg.h> 47#include <errmsg.h>
47 48
48static char *db_user = NULL;
49static char *db_host = NULL;
50static char *db_socket = NULL;
51static char *db_pass = NULL;
52static char *db = NULL;
53static char *ca_cert = NULL;
54static char *ca_dir = NULL;
55static char *cert = NULL;
56static char *key = NULL;
57static char *ciphers = NULL;
58static bool ssl = false;
59static char *opt_file = NULL;
60static char *opt_group = NULL;
61static unsigned int db_port = MYSQL_PORT;
62static bool check_replica = false;
63static bool ignore_auth = false;
64static int verbose = 0; 49static int verbose = 0;
65 50
66static double warning_time = 0;
67static double critical_time = 0;
68
69#define LENGTH_METRIC_UNIT 6 51#define LENGTH_METRIC_UNIT 6
70static const char *metric_unit[LENGTH_METRIC_UNIT] = { 52static const char *metric_unit[LENGTH_METRIC_UNIT] = {
71 "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache", "Threads_connected", "Threads_running"}; 53 "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache",
54 "Threads_connected", "Threads_running"};
72 55
73#define LENGTH_METRIC_COUNTER 9 56#define LENGTH_METRIC_COUNTER 9
74static const char *metric_counter[LENGTH_METRIC_COUNTER] = { 57static const char *metric_counter[LENGTH_METRIC_COUNTER] = {"Connections",
75 "Connections", "Qcache_hits", "Qcache_inserts", "Qcache_lowmem_prunes", "Qcache_not_cached", "Queries", 58 "Qcache_hits",
76 "Questions", "Table_locks_waited", "Uptime"}; 59 "Qcache_inserts",
77 60 "Qcache_lowmem_prunes",
78#define MYSQLDUMP_THREADS_QUERY \ 61 "Qcache_not_cached",
79 "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE 'SELECT /*!40001 SQL_NO_CACHE */%'" 62 "Queries",
80 63 "Questions",
81static thresholds *my_threshold = NULL; 64 "Table_locks_waited",
82 65 "Uptime"};
83static int process_arguments(int, char **); 66
84static int validate_arguments(void); 67#define MYSQLDUMP_THREADS_QUERY \
68 "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE " \
69 "'SELECT /*!40001 SQL_NO_CACHE */%'"
70
71typedef struct {
72 int errorcode;
73 check_mysql_config config;
74} check_mysql_config_wrapper;
75static check_mysql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
76static check_mysql_config_wrapper validate_arguments(check_mysql_config_wrapper /*config_wrapper*/);
85static void print_help(void); 77static void print_help(void);
86void print_usage(void); 78void print_usage(void);
87 79
88int main(int argc, char **argv) { 80int main(int argc, char **argv) {
89
90 MYSQL mysql;
91 MYSQL_RES *res;
92 MYSQL_ROW row;
93
94 /* should be status */
95
96 char *result = NULL;
97 char *error = NULL;
98 char replica_result[REPLICA_RESULTSIZE] = {0};
99 char *perf;
100
101 perf = strdup("");
102
103 setlocale(LC_ALL, ""); 81 setlocale(LC_ALL, "");
104 bindtextdomain(PACKAGE, LOCALEDIR); 82 bindtextdomain(PACKAGE, LOCALEDIR);
105 textdomain(PACKAGE); 83 textdomain(PACKAGE);
@@ -107,36 +85,46 @@ int main(int argc, char **argv) {
107 /* Parse extra opts if any */ 85 /* Parse extra opts if any */
108 argv = np_extra_opts(&argc, argv, progname); 86 argv = np_extra_opts(&argc, argv, progname);
109 87
110 if (process_arguments(argc, argv) == ERROR) { 88 check_mysql_config_wrapper tmp_config = process_arguments(argc, argv);
89 if (tmp_config.errorcode == ERROR) {
111 usage4(_("Could not parse arguments")); 90 usage4(_("Could not parse arguments"));
112 } 91 }
113 92
93 const check_mysql_config config = tmp_config.config;
94
95 MYSQL mysql;
114 /* initialize mysql */ 96 /* initialize mysql */
115 mysql_init(&mysql); 97 mysql_init(&mysql);
116 98
117 if (opt_file != NULL) { 99 if (config.opt_file != NULL) {
118 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, opt_file); 100 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file);
119 } 101 }
120 102
121 if (opt_group != NULL) { 103 if (config.opt_group != NULL) {
122 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, opt_group); 104 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group);
123 } else { 105 } else {
124 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); 106 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client");
125 } 107 }
126 108
127 if (ssl) { 109 if (config.ssl) {
128 mysql_ssl_set(&mysql, key, cert, ca_cert, ca_dir, ciphers); 110 mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir,
111 config.ciphers);
129 } 112 }
130 /* establish a connection to the server and error checking */ 113 /* establish a connection to the server and error checking */
131 if (!mysql_real_connect(&mysql, db_host, db_user, db_pass, db, db_port, db_socket, 0)) { 114 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db,
115 config.db_port, config.db_socket, 0)) {
132 /* Depending on internally-selected auth plugin MySQL might return */ 116 /* Depending on internally-selected auth plugin MySQL might return */
133 /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */ 117 /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */
134 /* Semantically these errors are the same. */ 118 /* Semantically these errors are the same. */
135 if (ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR || mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) { 119 if (config.ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR ||
136 printf("MySQL OK - Version: %s (protocol %d)\n", mysql_get_server_info(&mysql), mysql_get_proto_info(&mysql)); 120 mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) {
121 printf("MySQL OK - Version: %s (protocol %d)\n", mysql_get_server_info(&mysql),
122 mysql_get_proto_info(&mysql));
137 mysql_close(&mysql); 123 mysql_close(&mysql);
138 return STATE_OK; 124 return STATE_OK;
139 } else if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { 125 }
126
127 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
140 die(STATE_WARNING, "%s\n", mysql_error(&mysql)); 128 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
141 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) { 129 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
142 die(STATE_WARNING, "%s\n", mysql_error(&mysql)); 130 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
@@ -152,7 +140,7 @@ int main(int argc, char **argv) {
152 } 140 }
153 141
154 /* get the server stats */ 142 /* get the server stats */
155 result = strdup(mysql_stat(&mysql)); 143 char *result = strdup(mysql_stat(&mysql));
156 144
157 /* error checking once more */ 145 /* error checking once more */
158 if (mysql_error(&mysql)) { 146 if (mysql_error(&mysql)) {
@@ -165,6 +153,10 @@ int main(int argc, char **argv) {
165 } 153 }
166 } 154 }
167 155
156 char *perf = strdup("");
157 char *error = NULL;
158 MYSQL_RES *res;
159 MYSQL_ROW row;
168 /* try to fetch some perf data */ 160 /* try to fetch some perf data */
169 if (mysql_query(&mysql, "show global status") == 0) { 161 if (mysql_query(&mysql, "show global status") == 0) {
170 if ((res = mysql_store_result(&mysql)) == NULL) { 162 if ((res = mysql_store_result(&mysql)) == NULL) {
@@ -174,17 +166,19 @@ int main(int argc, char **argv) {
174 } 166 }
175 167
176 while ((row = mysql_fetch_row(res)) != NULL) { 168 while ((row = mysql_fetch_row(res)) != NULL) {
177 int i; 169 for (int i = 0; i < LENGTH_METRIC_UNIT; i++) {
178
179 for (i = 0; i < LENGTH_METRIC_UNIT; i++) {
180 if (strcmp(row[0], metric_unit[i]) == 0) { 170 if (strcmp(row[0], metric_unit[i]) == 0) {
181 xasprintf(&perf, "%s%s ", perf, perfdata(metric_unit[i], atol(row[1]), "", false, 0, false, 0, false, 0, false, 0)); 171 xasprintf(&perf, "%s%s ", perf,
172 perfdata(metric_unit[i], atol(row[1]), "", false, 0, false, 0, false,
173 0, false, 0));
182 continue; 174 continue;
183 } 175 }
184 } 176 }
185 for (i = 0; i < LENGTH_METRIC_COUNTER; i++) { 177 for (int i = 0; i < LENGTH_METRIC_COUNTER; i++) {
186 if (strcmp(row[0], metric_counter[i]) == 0) { 178 if (strcmp(row[0], metric_counter[i]) == 0) {
187 xasprintf(&perf, "%s%s ", perf, perfdata(metric_counter[i], atol(row[1]), "c", false, 0, false, 0, false, 0, false, 0)); 179 xasprintf(&perf, "%s%s ", perf,
180 perfdata(metric_counter[i], atol(row[1]), "c", false, 0, false, 0,
181 false, 0, false, 0));
188 continue; 182 continue;
189 } 183 }
190 } 184 }
@@ -195,8 +189,8 @@ int main(int argc, char **argv) {
195 } 189 }
196 } 190 }
197 191
198 if (check_replica) { 192 char replica_result[REPLICA_RESULTSIZE] = {0};
199 193 if (config.check_replica) {
200 // Detect which version we are, on older version 194 // Detect which version we are, on older version
201 // "show slave status" should work, on newer ones 195 // "show slave status" should work, on newer ones
202 // "show replica status" 196 // "show replica status"
@@ -210,8 +204,8 @@ int main(int argc, char **argv) {
210 unsigned long minor_version = (server_verion_int % 10000) / 100; 204 unsigned long minor_version = (server_verion_int % 10000) / 100;
211 unsigned long patch_version = (server_verion_int % 100); 205 unsigned long patch_version = (server_verion_int % 100);
212 if (verbose) { 206 if (verbose) {
213 printf("Found MariaDB: %s, main version: %lu, minor version: %lu, patch version: %lu\n", server_version, major_version, 207 printf("Found MariaDB: %s, main version: %lu, minor version: %lu, patch version: %lu\n",
214 minor_version, patch_version); 208 server_version, major_version, minor_version, patch_version);
215 } 209 }
216 210
217 if (strstr(server_version, "MariaDB") != NULL) { 211 if (strstr(server_version, "MariaDB") != NULL) {
@@ -283,12 +277,14 @@ int main(int argc, char **argv) {
283 277
284 } else { 278 } else {
285 /* mysql 4.x.x and mysql 5.x.x */ 279 /* mysql 4.x.x and mysql 5.x.x */
286 int replica_io_field = -1, replica_sql_field = -1, seconds_behind_field = -1, i, num_fields; 280 int replica_io_field = -1;
281 int replica_sql_field = -1;
282 int seconds_behind_field = -1;
283 int num_fields;
287 MYSQL_FIELD *fields; 284 MYSQL_FIELD *fields;
288
289 num_fields = mysql_num_fields(res); 285 num_fields = mysql_num_fields(res);
290 fields = mysql_fetch_fields(res); 286 fields = mysql_fetch_fields(res);
291 for (i = 0; i < num_fields; i++) { 287 for (int i = 0; i < num_fields; i++) {
292 if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { 288 if (strcmp(fields[i].name, "Slave_IO_Running") == 0) {
293 replica_io_field = i; 289 replica_io_field = i;
294 continue; 290 continue;
@@ -311,11 +307,15 @@ int main(int argc, char **argv) {
311 } 307 }
312 308
313 /* Save replica status in replica_result */ 309 /* Save replica status in replica_result */
314 snprintf(replica_result, REPLICA_RESULTSIZE, "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s", row[replica_io_field], 310 snprintf(replica_result, REPLICA_RESULTSIZE,
315 row[replica_sql_field], seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown"); 311 "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s",
316 312 row[replica_io_field], row[replica_sql_field],
317 /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no mysqldump threads running */ 313 seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown");
318 if (strcmp(row[replica_io_field], "Yes") != 0 || strcmp(row[replica_sql_field], "Yes") != 0) { 314
315 /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no
316 * mysqldump threads running */
317 if (strcmp(row[replica_io_field], "Yes") != 0 ||
318 strcmp(row[replica_sql_field], "Yes") != 0) {
319 MYSQL_RES *res_mysqldump; 319 MYSQL_RES *res_mysqldump;
320 MYSQL_ROW row_mysqldump; 320 MYSQL_ROW row_mysqldump;
321 unsigned int mysqldump_threads = 0; 321 unsigned int mysqldump_threads = 0;
@@ -344,20 +344,23 @@ int main(int argc, char **argv) {
344 if (seconds_behind_field == -1) { 344 if (seconds_behind_field == -1) {
345 printf("seconds_behind_field not found\n"); 345 printf("seconds_behind_field not found\n");
346 } else { 346 } else {
347 printf("seconds_behind_field(index %d)=%s\n", seconds_behind_field, row[seconds_behind_field]); 347 printf("seconds_behind_field(index %d)=%s\n", seconds_behind_field,
348 row[seconds_behind_field]);
348 } 349 }
349 } 350 }
350 351
351 /* Check Seconds Behind against threshold */ 352 /* Check Seconds Behind against threshold */
352 if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && strcmp(row[seconds_behind_field], "NULL") != 0)) { 353 if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL &&
354 strcmp(row[seconds_behind_field], "NULL") != 0)) {
353 double value = atof(row[seconds_behind_field]); 355 double value = atof(row[seconds_behind_field]);
354 int status; 356 int status;
355 357
356 status = get_status(value, my_threshold); 358 status = get_status(value, config.my_threshold);
357 359
358 xasprintf(&perf, "%s %s", perf, 360 xasprintf(&perf, "%s %s", perf,
359 fperfdata("seconds behind master", value, "s", true, (double)warning_time, true, (double)critical_time, false, 0, 361 fperfdata("seconds behind master", value, "s", true,
360 false, 0)); 362 (double)config.warning_time, true, (double)config.critical_time,
363 false, 0, false, 0));
361 364
362 if (status == STATE_WARNING) { 365 if (status == STATE_WARNING) {
363 printf("SLOW_REPLICA %s: %s|%s\n", _("WARNING"), replica_result, perf); 366 printf("SLOW_REPLICA %s: %s|%s\n", _("WARNING"), replica_result, perf);
@@ -377,7 +380,7 @@ int main(int argc, char **argv) {
377 mysql_close(&mysql); 380 mysql_close(&mysql);
378 381
379 /* print out the result of stats */ 382 /* print out the result of stats */
380 if (check_replica) { 383 if (config.check_replica) {
381 printf("%s %s|%s\n", result, replica_result, perf); 384 printf("%s %s|%s\n", result, replica_result, perf);
382 } else { 385 } else {
383 printf("%s|%s\n", result, perf); 386 printf("%s|%s\n", result, perf);
@@ -389,12 +392,7 @@ int main(int argc, char **argv) {
389#define CHECK_REPLICA_OPT CHAR_MAX + 1 392#define CHECK_REPLICA_OPT CHAR_MAX + 1
390 393
391/* process command-line arguments */ 394/* process command-line arguments */
392int process_arguments(int argc, char **argv) { 395check_mysql_config_wrapper process_arguments(int argc, char **argv) {
393 int c;
394 char *warning = NULL;
395 char *critical = NULL;
396
397 int option = 0;
398 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 396 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
399 {"socket", required_argument, 0, 's'}, 397 {"socket", required_argument, 0, 's'},
400 {"database", required_argument, 0, 'd'}, 398 {"database", required_argument, 0, 'd'},
@@ -419,56 +417,67 @@ int process_arguments(int argc, char **argv) {
419 {"ciphers", required_argument, 0, 'L'}, 417 {"ciphers", required_argument, 0, 'L'},
420 {0, 0, 0, 0}}; 418 {0, 0, 0, 0}};
421 419
420 check_mysql_config_wrapper result = {
421 .errorcode = OK,
422 .config = check_mysql_config_init(),
423 };
424
422 if (argc < 1) { 425 if (argc < 1) {
423 return ERROR; 426 result.errorcode = ERROR;
427 return result;
424 } 428 }
425 429
426 while (1) { 430 char *warning = NULL;
427 c = getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option); 431 char *critical = NULL;
432
433 int option = 0;
434 while (true) {
435 int option_index =
436 getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option);
428 437
429 if (c == -1 || c == EOF) { 438 if (option_index == -1 || option_index == EOF) {
430 break; 439 break;
431 } 440 }
432 441
433 switch (c) { 442 switch (option_index) {
434 case 'H': /* hostname */ 443 case 'H': /* hostname */
435 if (is_host(optarg)) { 444 if (is_host(optarg)) {
436 db_host = optarg; 445 result.config.db_host = optarg;
437 } else if (*optarg == '/') { 446 } else if (*optarg == '/') {
438 db_socket = optarg; 447 result.config.db_socket = optarg;
439 } else { 448 } else {
440 usage2(_("Invalid hostname/address"), optarg); 449 usage2(_("Invalid hostname/address"), optarg);
441 } 450 }
442 break; 451 break;
443 case 's': /* socket */ 452 case 's': /* socket */
444 db_socket = optarg; 453 result.config.db_socket = optarg;
445 break; 454 break;
446 case 'd': /* database */ 455 case 'd': /* database */
447 db = optarg; 456 result.config.db = optarg;
448 break; 457 break;
449 case 'l': 458 case 'l':
450 ssl = true; 459 result.config.ssl = true;
451 break; 460 break;
452 case 'C': 461 case 'C':
453 ca_cert = optarg; 462 result.config.ca_cert = optarg;
454 break; 463 break;
455 case 'a': 464 case 'a':
456 cert = optarg; 465 result.config.cert = optarg;
457 break; 466 break;
458 case 'k': 467 case 'k':
459 key = optarg; 468 result.config.key = optarg;
460 break; 469 break;
461 case 'D': 470 case 'D':
462 ca_dir = optarg; 471 result.config.ca_dir = optarg;
463 break; 472 break;
464 case 'L': 473 case 'L':
465 ciphers = optarg; 474 result.config.ciphers = optarg;
466 break; 475 break;
467 case 'u': /* username */ 476 case 'u': /* username */
468 db_user = optarg; 477 result.config.db_user = optarg;
469 break; 478 break;
470 case 'p': /* authentication information: password */ 479 case 'p': /* authentication information: password */
471 db_pass = strdup(optarg); 480 result.config.db_pass = strdup(optarg);
472 481
473 /* Delete the password from process list */ 482 /* Delete the password from process list */
474 while (*optarg != '\0') { 483 while (*optarg != '\0') {
@@ -477,28 +486,28 @@ int process_arguments(int argc, char **argv) {
477 } 486 }
478 break; 487 break;
479 case 'f': /* client options file */ 488 case 'f': /* client options file */
480 opt_file = optarg; 489 result.config.opt_file = optarg;
481 break; 490 break;
482 case 'g': /* client options group */ 491 case 'g': /* client options group */
483 opt_group = optarg; 492 result.config.opt_group = optarg;
484 break; 493 break;
485 case 'P': /* critical time threshold */ 494 case 'P': /* critical time threshold */
486 db_port = atoi(optarg); 495 result.config.db_port = atoi(optarg);
487 break; 496 break;
488 case 'S': 497 case 'S':
489 case CHECK_REPLICA_OPT: 498 case CHECK_REPLICA_OPT:
490 check_replica = true; /* check-slave */ 499 result.config.check_replica = true; /* check-slave */
491 break; 500 break;
492 case 'n': 501 case 'n':
493 ignore_auth = true; /* ignore-auth */ 502 result.config.ignore_auth = true; /* ignore-auth */
494 break; 503 break;
495 case 'w': 504 case 'w':
496 warning = optarg; 505 warning = optarg;
497 warning_time = strtod(warning, NULL); 506 result.config.warning_time = strtod(warning, NULL);
498 break; 507 break;
499 case 'c': 508 case 'c':
500 critical = optarg; 509 critical = optarg;
501 critical_time = strtod(critical, NULL); 510 result.config.critical_time = strtod(critical, NULL);
502 break; 511 break;
503 case 'V': /* version */ 512 case 'V': /* version */
504 print_revision(progname, NP_VERSION); 513 print_revision(progname, NP_VERSION);
@@ -514,48 +523,47 @@ int process_arguments(int argc, char **argv) {
514 } 523 }
515 } 524 }
516 525
517 c = optind; 526 int index = optind;
518 527
519 set_thresholds(&my_threshold, warning, critical); 528 set_thresholds(&result.config.my_threshold, warning, critical);
520 529
521 while (argc > c) { 530 while (argc > index) {
522 531 if (result.config.db_host == NULL) {
523 if (db_host == NULL) { 532 if (is_host(argv[index])) {
524 if (is_host(argv[c])) { 533 result.config.db_host = argv[index++];
525 db_host = argv[c++];
526 } else { 534 } else {
527 usage2(_("Invalid hostname/address"), argv[c]); 535 usage2(_("Invalid hostname/address"), argv[index]);
528 } 536 }
529 } else if (db_user == NULL) { 537 } else if (result.config.db_user == NULL) {
530 db_user = argv[c++]; 538 result.config.db_user = argv[index++];
531 } else if (db_pass == NULL) { 539 } else if (result.config.db_pass == NULL) {
532 db_pass = argv[c++]; 540 result.config.db_pass = argv[index++];
533 } else if (db == NULL) { 541 } else if (result.config.db == NULL) {
534 db = argv[c++]; 542 result.config.db = argv[index++];
535 } else if (is_intnonneg(argv[c])) { 543 } else if (is_intnonneg(argv[index])) {
536 db_port = atoi(argv[c++]); 544 result.config.db_port = atoi(argv[index++]);
537 } else { 545 } else {
538 break; 546 break;
539 } 547 }
540 } 548 }
541 549
542 return validate_arguments(); 550 return validate_arguments(result);
543} 551}
544 552
545int validate_arguments(void) { 553check_mysql_config_wrapper validate_arguments(check_mysql_config_wrapper config_wrapper) {
546 if (db_user == NULL) { 554 if (config_wrapper.config.db_user == NULL) {
547 db_user = strdup(""); 555 config_wrapper.config.db_user = strdup("");
548 } 556 }
549 557
550 if (db_host == NULL) { 558 if (config_wrapper.config.db_host == NULL) {
551 db_host = strdup(""); 559 config_wrapper.config.db_host = strdup("");
552 } 560 }
553 561
554 if (db == NULL) { 562 if (config_wrapper.config.db == NULL) {
555 db = strdup(""); 563 config_wrapper.config.db = strdup("");
556 } 564 }
557 565
558 return OK; 566 return config_wrapper;
559} 567}
560 568
561void print_help(void) { 569void print_help(void) {
@@ -595,15 +603,17 @@ void print_help(void) {
595 printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); 603 printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!"));
596 printf(" %s\n", _("Your clear-text password could be visible as a process table entry")); 604 printf(" %s\n", _("Your clear-text password could be visible as a process table entry"));
597 printf(" %s\n", "-S, --check-slave"); 605 printf(" %s\n", "-S, --check-slave");
598 printf(" %s\n", 606 printf(" %s\n", _("Check if the slave thread is running properly. This option is deprecated "
599 _("Check if the slave thread is running properly. This option is deprecated in favour of check-replica, which does the same")); 607 "in favour of check-replica, which does the same"));
600 printf(" %s\n", "--check-replica"); 608 printf(" %s\n", "--check-replica");
601 printf(" %s\n", _("Check if the replica thread is running properly.")); 609 printf(" %s\n", _("Check if the replica thread is running properly."));
602 printf(" %s\n", "-w, --warning"); 610 printf(" %s\n", "-w, --warning");
603 printf(" %s\n", _("Exit with WARNING status if replica server is more than INTEGER seconds")); 611 printf(" %s\n",
612 _("Exit with WARNING status if replica server is more than INTEGER seconds"));
604 printf(" %s\n", _("behind master")); 613 printf(" %s\n", _("behind master"));
605 printf(" %s\n", "-c, --critical"); 614 printf(" %s\n", "-c, --critical");
606 printf(" %s\n", _("Exit with CRITICAL status if replica server is more then INTEGER seconds")); 615 printf(" %s\n",
616 _("Exit with CRITICAL status if replica server is more then INTEGER seconds"));
607 printf(" %s\n", _("behind master")); 617 printf(" %s\n", _("behind master"));
608 printf(" %s\n", "-l, --ssl"); 618 printf(" %s\n", "-l, --ssl");
609 printf(" %s\n", _("Use ssl encryption")); 619 printf(" %s\n", _("Use ssl encryption"));
@@ -619,7 +629,8 @@ void print_help(void) {
619 printf(" %s\n", _("List of valid SSL ciphers")); 629 printf(" %s\n", _("List of valid SSL ciphers"));
620 630
621 printf("\n"); 631 printf("\n");
622 printf(" %s\n", _("There are no required arguments. By default, the local database is checked")); 632 printf(" %s\n",
633 _("There are no required arguments. By default, the local database is checked"));
623 printf(" %s\n", _("using the default unix socket. You can force TCP on localhost by using an")); 634 printf(" %s\n", _("using the default unix socket. You can force TCP on localhost by using an"));
624 printf(" %s\n", _("IP address or FQDN ('localhost' will use the socket as well).")); 635 printf(" %s\n", _("IP address or FQDN ('localhost' will use the socket as well)."));
625 636
diff --git a/plugins/check_mysql.d/config.h b/plugins/check_mysql.d/config.h
new file mode 100644
index 00000000..71ddbe8d
--- /dev/null
+++ b/plugins/check_mysql.d/config.h
@@ -0,0 +1,58 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6#include <mysql.h>
7
8typedef struct {
9 char *db_host;
10 unsigned int db_port;
11 char *db_user;
12 char *db_socket;
13 char *db_pass;
14 char *db;
15 char *ca_cert;
16 char *ca_dir;
17 char *cert;
18 char *key;
19 char *ciphers;
20 bool ssl;
21 char *opt_file;
22 char *opt_group;
23
24 bool check_replica;
25 bool ignore_auth;
26
27 double warning_time;
28 double critical_time;
29 thresholds *my_threshold;
30
31} check_mysql_config;
32
33check_mysql_config check_mysql_config_init() {
34 check_mysql_config tmp = {
35 .db_host = NULL,
36 .db_port = MYSQL_PORT,
37 .db = NULL,
38 .db_pass = NULL,
39 .db_socket = NULL,
40 .db_user = NULL,
41 .ca_cert = NULL,
42 .ca_dir = NULL,
43 .cert = NULL,
44 .key = NULL,
45 .ciphers = NULL,
46 .ssl = false,
47 .opt_file = NULL,
48 .opt_group = NULL,
49
50 .check_replica = false,
51 .ignore_auth = false,
52
53 .warning_time = 0,
54 .critical_time = 0,
55 .my_threshold = NULL,
56 };
57 return tmp;
58}
diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c
index 79b6e2f4..c7e84deb 100644
--- a/plugins/check_mysql_query.c
+++ b/plugins/check_mysql_query.c
@@ -37,27 +37,22 @@ const char *email = "devel@monitoring-plugins.org";
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
44static char *db_user = NULL; 45typedef struct {
45static char *db_host = NULL; 46 int errorcode;
46static char *db_socket = NULL; 47 check_mysql_query_config config;
47static char *db_pass = NULL; 48} check_mysql_query_config_wrapper;
48static char *db = NULL; 49static check_mysql_query_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
49static char *opt_file = NULL; 50static check_mysql_query_config_wrapper
50static char *opt_group = NULL; 51 validate_arguments(check_mysql_query_config_wrapper /*config_wrapper*/);
51static unsigned int db_port = MYSQL_PORT;
52
53static int process_arguments(int /*argc*/, char ** /*argv*/);
54static int validate_arguments(void);
55static void print_help(void); 52static void print_help(void);
56void print_usage(void); 53void print_usage(void);
57 54
58static char *sql_query = NULL;
59static int verbose = 0; 55static int verbose = 0;
60static thresholds *my_thresholds = NULL;
61 56
62int main(int argc, char **argv) { 57int main(int argc, char **argv) {
63 setlocale(LC_ALL, ""); 58 setlocale(LC_ALL, "");
@@ -67,39 +62,47 @@ int main(int argc, char **argv) {
67 /* Parse extra opts if any */ 62 /* Parse extra opts if any */
68 argv = np_extra_opts(&argc, argv, progname); 63 argv = np_extra_opts(&argc, argv, progname);
69 64
70 if (process_arguments(argc, argv) == ERROR) 65 check_mysql_query_config_wrapper tmp_config = process_arguments(argc, argv);
66 if (tmp_config.errorcode == ERROR) {
71 usage4(_("Could not parse arguments")); 67 usage4(_("Could not parse arguments"));
68 }
69
70 const check_mysql_query_config config = tmp_config.config;
72 71
73 MYSQL mysql; 72 MYSQL mysql;
74 /* initialize mysql */ 73 /* initialize mysql */
75 mysql_init(&mysql); 74 mysql_init(&mysql);
76 75
77 if (opt_file != NULL) 76 if (config.opt_file != NULL) {
78 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, opt_file); 77 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file);
78 }
79 79
80 if (opt_group != NULL) 80 if (config.opt_group != NULL) {
81 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, opt_group); 81 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group);
82 else 82 } else {
83 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); 83 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client");
84 }
84 85
85 /* establish a connection to the server and error checking */ 86 /* establish a connection to the server and error checking */
86 if (!mysql_real_connect(&mysql, db_host, db_user, db_pass, db, db_port, db_socket, 0)) { 87 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db,
87 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) 88 config.db_port, config.db_socket, 0)) {
89 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
88 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 90 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
89 else if (mysql_errno(&mysql) == CR_VERSION_ERROR) 91 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
90 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 92 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
91 else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) 93 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
92 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 94 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
93 else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) 95 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
94 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 96 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
95 else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) 97 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
96 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 98 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
97 else 99 } else {
98 die(STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error(&mysql)); 100 die(STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error(&mysql));
101 }
99 } 102 }
100 103
101 char *error = NULL; 104 char *error = NULL;
102 if (mysql_query(&mysql, sql_query) != 0) { 105 if (mysql_query(&mysql, config.sql_query) != 0) {
103 error = strdup(mysql_error(&mysql)); 106 error = strdup(mysql_error(&mysql));
104 mysql_close(&mysql); 107 mysql_close(&mysql);
105 die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error); 108 die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error);
@@ -140,10 +143,11 @@ int main(int argc, char **argv) {
140 /* close the connection */ 143 /* close the connection */
141 mysql_close(&mysql); 144 mysql_close(&mysql);
142 145
143 if (verbose >= 3) 146 if (verbose >= 3) {
144 printf("mysql result: %f\n", value); 147 printf("mysql result: %f\n", value);
148 }
145 149
146 int status = get_status(value, my_thresholds); 150 int status = get_status(value, config.my_thresholds);
147 151
148 if (status == STATE_OK) { 152 if (status == STATE_OK) {
149 printf("QUERY %s: ", _("OK")); 153 printf("QUERY %s: ", _("OK"));
@@ -152,26 +156,44 @@ int main(int argc, char **argv) {
152 } else if (status == STATE_CRITICAL) { 156 } else if (status == STATE_CRITICAL) {
153 printf("QUERY %s: ", _("CRITICAL")); 157 printf("QUERY %s: ", _("CRITICAL"));
154 } 158 }
155 printf(_("'%s' returned %f | %s"), sql_query, value, 159 printf(_("'%s' returned %f | %s"), config.sql_query, value,
156 fperfdata("result", value, "", my_thresholds->warning ? true : false, my_thresholds->warning ? my_thresholds->warning->end : 0, 160 fperfdata("result", value, "", config.my_thresholds->warning,
157 my_thresholds->critical ? true : false, my_thresholds->critical ? my_thresholds->critical->end : 0, false, 0, false, 161 config.my_thresholds->warning ? config.my_thresholds->warning->end : 0,
158 0)); 162 config.my_thresholds->critical,
163 config.my_thresholds->critical ? config.my_thresholds->critical->end : 0,
164 false, 0, false, 0));
159 printf("\n"); 165 printf("\n");
160 166
161 return status; 167 return status;
162} 168}
163 169
164/* process command-line arguments */ 170/* process command-line arguments */
165int process_arguments(int argc, char **argv) { 171check_mysql_query_config_wrapper process_arguments(int argc, char **argv) {
166 static struct option longopts[] = { 172 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
167 {"hostname", required_argument, 0, 'H'}, {"socket", required_argument, 0, 's'}, {"database", required_argument, 0, 'd'}, 173 {"socket", required_argument, 0, 's'},
168 {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'}, {"file", required_argument, 0, 'f'}, 174 {"database", required_argument, 0, 'd'},
169 {"group", required_argument, 0, 'g'}, {"port", required_argument, 0, 'P'}, {"verbose", no_argument, 0, 'v'}, 175 {"username", required_argument, 0, 'u'},
170 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"query", required_argument, 0, 'q'}, 176 {"password", required_argument, 0, 'p'},
171 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {0, 0, 0, 0}}; 177 {"file", required_argument, 0, 'f'},
172 178 {"group", required_argument, 0, 'g'},
173 if (argc < 1) 179 {"port", required_argument, 0, 'P'},
174 return ERROR; 180 {"verbose", no_argument, 0, 'v'},
181 {"version", no_argument, 0, 'V'},
182 {"help", no_argument, 0, 'h'},
183 {"query", required_argument, 0, 'q'},
184 {"warning", required_argument, 0, 'w'},
185 {"critical", required_argument, 0, 'c'},
186 {0, 0, 0, 0}};
187
188 check_mysql_query_config_wrapper result = {
189 .errorcode = OK,
190 .config = check_mysql_query_config_init(),
191 };
192
193 if (argc < 1) {
194 result.errorcode = ERROR;
195 return result;
196 }
175 197
176 char *warning = NULL; 198 char *warning = NULL;
177 char *critical = NULL; 199 char *critical = NULL;
@@ -180,28 +202,29 @@ int process_arguments(int argc, char **argv) {
180 int option = 0; 202 int option = 0;
181 int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option); 203 int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option);
182 204
183 if (option_char == -1 || option_char == EOF) 205 if (option_char == -1 || option_char == EOF) {
184 break; 206 break;
207 }
185 208
186 switch (option_char) { 209 switch (option_char) {
187 case 'H': /* hostname */ 210 case 'H': /* hostname */
188 if (is_host(optarg)) { 211 if (is_host(optarg)) {
189 db_host = optarg; 212 result.config.db_host = optarg;
190 } else { 213 } else {
191 usage2(_("Invalid hostname/address"), optarg); 214 usage2(_("Invalid hostname/address"), optarg);
192 } 215 }
193 break; 216 break;
194 case 's': /* socket */ 217 case 's': /* socket */
195 db_socket = optarg; 218 result.config.db_socket = optarg;
196 break; 219 break;
197 case 'd': /* database */ 220 case 'd': /* database */
198 db = optarg; 221 result.config.db = optarg;
199 break; 222 break;
200 case 'u': /* username */ 223 case 'u': /* username */
201 db_user = optarg; 224 result.config.db_user = optarg;
202 break; 225 break;
203 case 'p': /* authentication information: password */ 226 case 'p': /* authentication information: password */
204 db_pass = strdup(optarg); 227 result.config.db_pass = strdup(optarg);
205 228
206 /* Delete the password from process list */ 229 /* Delete the password from process list */
207 while (*optarg != '\0') { 230 while (*optarg != '\0') {
@@ -210,13 +233,13 @@ int process_arguments(int argc, char **argv) {
210 } 233 }
211 break; 234 break;
212 case 'f': /* client options file */ 235 case 'f': /* client options file */
213 opt_file = optarg; 236 result.config.opt_file = optarg;
214 break; 237 break;
215 case 'g': /* client options group */ 238 case 'g': /* client options group */
216 opt_group = optarg; 239 result.config.opt_group = optarg;
217 break; 240 break;
218 case 'P': /* critical time threshold */ 241 case 'P': /* critical time threshold */
219 db_port = atoi(optarg); 242 result.config.db_port = atoi(optarg);
220 break; 243 break;
221 case 'v': 244 case 'v':
222 verbose++; 245 verbose++;
@@ -228,7 +251,7 @@ int process_arguments(int argc, char **argv) {
228 print_help(); 251 print_help();
229 exit(STATE_UNKNOWN); 252 exit(STATE_UNKNOWN);
230 case 'q': 253 case 'q':
231 xasprintf(&sql_query, "%s", optarg); 254 xasprintf(&result.config.sql_query, "%s", optarg);
232 break; 255 break;
233 case 'w': 256 case 'w':
234 warning = optarg; 257 warning = optarg;
@@ -241,25 +264,30 @@ int process_arguments(int argc, char **argv) {
241 } 264 }
242 } 265 }
243 266
244 set_thresholds(&my_thresholds, warning, critical); 267 set_thresholds(&result.config.my_thresholds, warning, critical);
245 268
246 return validate_arguments(); 269 return validate_arguments(result);
247} 270}
248 271
249int validate_arguments(void) { 272check_mysql_query_config_wrapper
250 if (sql_query == NULL) 273validate_arguments(check_mysql_query_config_wrapper config_wrapper) {
274 if (config_wrapper.config.sql_query == NULL) {
251 usage("Must specify a SQL query to run"); 275 usage("Must specify a SQL query to run");
276 }
252 277
253 if (db_user == NULL) 278 if (config_wrapper.config.db_user == NULL) {
254 db_user = strdup(""); 279 config_wrapper.config.db_user = strdup("");
280 }
255 281
256 if (db_host == NULL) 282 if (config_wrapper.config.db_host == NULL) {
257 db_host = strdup(""); 283 config_wrapper.config.db_host = strdup("");
284 }
258 285
259 if (db == NULL) 286 if (config_wrapper.config.db == NULL) {
260 db = strdup(""); 287 config_wrapper.config.db = strdup("");
288 }
261 289
262 return OK; 290 return config_wrapper;
263} 291}
264 292
265void print_help(void) { 293void print_help(void) {
diff --git a/plugins/check_mysql_query.d/config.h b/plugins/check_mysql_query.d/config.h
new file mode 100644
index 00000000..be019160
--- /dev/null
+++ b/plugins/check_mysql_query.d/config.h
@@ -0,0 +1,36 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <mysql.h>
6
7typedef struct {
8 char *db_host;
9 char *db_socket;
10 char *db;
11 char *db_user;
12 char *db_pass;
13 char *opt_file;
14 char *opt_group;
15 unsigned int db_port;
16
17 char *sql_query;
18 thresholds *my_thresholds;
19} check_mysql_query_config;
20
21check_mysql_query_config check_mysql_query_config_init() {
22 check_mysql_query_config tmp = {
23 .db_host = NULL,
24 .db_socket = NULL,
25 .db = NULL,
26 .db_user = NULL,
27 .db_pass = NULL,
28 .opt_file = NULL,
29 .opt_group = NULL,
30 .db_port = MYSQL_PORT,
31
32 .sql_query = NULL,
33 .my_thresholds = NULL,
34 };
35 return tmp;
36}
diff --git a/plugins/check_nagios.c b/plugins/check_nagios.c
index 48629be3..a46dc1ed 100644
--- a/plugins/check_nagios.c
+++ b/plugins/check_nagios.c
@@ -39,47 +39,20 @@ const char *email = "devel@monitoring-plugins.org";
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 42#include "states.h"
43static int process_arguments(int /*argc*/, char ** /*argv*/); 43#include "check_nagios.d/config.h"
44
45typedef struct {
46 int errorcode;
47 check_nagios_config config;
48} check_nagios_config_wrapper;
49static check_nagios_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
44static void print_help(void); 50static void print_help(void);
45void print_usage(void); 51void print_usage(void);
46 52
47static char *status_log = NULL;
48static char *process_string = NULL;
49static int expire_minutes = 0;
50
51static int verbose = 0; 53static int verbose = 0;
52 54
53int main(int argc, char **argv) { 55int main(int argc, char **argv) {
54 int result = STATE_UNKNOWN;
55 char input_buffer[MAX_INPUT_BUFFER];
56 unsigned long latest_entry_time = 0L;
57 unsigned long temp_entry_time = 0L;
58 int proc_entries = 0;
59 time_t current_time;
60 char *temp_ptr;
61 FILE *fp;
62 int procuid = 0;
63 int procpid = 0;
64 int procppid = 0;
65 int procvsz = 0;
66 int procrss = 0;
67 float procpcpu = 0;
68 char procstat[8];
69#ifdef PS_USES_PROCETIME
70 char procetime[MAX_INPUT_BUFFER];
71#endif /* PS_USES_PROCETIME */
72 char procprog[MAX_INPUT_BUFFER];
73 char *procargs;
74 int pos;
75 int cols;
76 int expected_cols = PS_COLS - 1;
77 const char *zombie = "Z";
78 char *temp_string;
79 output chld_out;
80 output chld_err;
81 size_t i;
82
83 setlocale(LC_ALL, ""); 56 setlocale(LC_ALL, "");
84 bindtextdomain(PACKAGE, LOCALEDIR); 57 bindtextdomain(PACKAGE, LOCALEDIR);
85 textdomain(PACKAGE); 58 textdomain(PACKAGE);
@@ -87,8 +60,13 @@ int main(int argc, char **argv) {
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);
89 62
90 if (process_arguments(argc, argv) == ERROR) 63 check_nagios_config_wrapper tmp_config = process_arguments(argc, argv);
64
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) {
@@ -99,13 +77,17 @@ int main(int argc, char **argv) {
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;
@@ -113,22 +95,44 @@ int main(int argc, char **argv) {
113 } 95 }
114 if ((temp_ptr = strtok(input_buffer, "]")) != NULL) { 96 if ((temp_ptr = strtok(input_buffer, "]")) != NULL) {
115 temp_entry_time = strtoul(temp_ptr + 1, NULL, 10); 97 temp_entry_time = strtoul(temp_ptr + 1, NULL, 10);
116 if (temp_entry_time > latest_entry_time) 98 if (temp_entry_time > latest_entry_time) {
117 latest_entry_time = temp_entry_time; 99 latest_entry_time = temp_entry_time;
100 }
118 } 101 }
119 } 102 }
120 fclose(fp); 103 fclose(log_file);
121 104
122 if (verbose >= 2) 105 if (verbose >= 2) {
123 printf("command: %s\n", PS_COMMAND); 106 printf("command: %s\n", PS_COMMAND);
107 }
124 108
125 /* run the command to check for the Nagios process.. */ 109 /* run the command to check for the Nagios process.. */
126 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) {
127 result = STATE_WARNING; 114 result = STATE_WARNING;
115 }
128 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";
129 /* count the number of matching Nagios processes... */ 133 /* count the number of matching Nagios processes... */
130 for (i = 0; i < chld_out.lines; i++) { 134 for (size_t i = 0; i < chld_out.lines; i++) {
131 cols = sscanf(chld_out.line[i], PS_FORMAT, PS_VARLIST); 135 int cols = sscanf(chld_out.line[i], PS_FORMAT, PS_VARLIST);
132 /* Zombie processes do not give a procprog command */ 136 /* Zombie processes do not give a procprog command */
133 if (cols == (expected_cols - 1) && strstr(procstat, zombie)) { 137 if (cols == (expected_cols - 1) && strstr(procstat, zombie)) {
134 cols = expected_cols; 138 cols = expected_cols;
@@ -142,14 +146,14 @@ int main(int argc, char **argv) {
142 strip(procargs); 146 strip(procargs);
143 147
144 /* Some ps return full pathname for command. This removes path */ 148 /* Some ps return full pathname for command. This removes path */
145 temp_string = strtok((char *)procprog, "/"); 149 char *temp_string = strtok((char *)procprog, "/");
146 while (temp_string) { 150 while (temp_string) {
147 strcpy(procprog, temp_string); 151 strcpy(procprog, temp_string);
148 temp_string = strtok(NULL, "/"); 152 temp_string = strtok(NULL, "/");
149 } 153 }
150 154
151 /* May get empty procargs */ 155 /* May get empty procargs */
152 if (!strstr(procargs, argv[0]) && strstr(procargs, process_string) && strcmp(procargs, "")) { 156 if (!strstr(procargs, argv[0]) && strstr(procargs, config.process_string) && strcmp(procargs, "")) {
153 proc_entries++; 157 proc_entries++;
154 if (verbose >= 2) { 158 if (verbose >= 2) {
155 printf(_("Found process: %s %s\n"), procprog, procargs); 159 printf(_("Found process: %s %s\n"), procprog, procargs);
@@ -159,8 +163,9 @@ int main(int argc, char **argv) {
159 } 163 }
160 164
161 /* If we get anything on stderr, at least set warning */ 165 /* If we get anything on stderr, at least set warning */
162 if (chld_err.buflen) 166 if (chld_err.buflen) {
163 result = max_state(result, STATE_WARNING); 167 result = max_state(result, STATE_WARNING);
168 }
164 169
165 /* reset the alarm handler */ 170 /* reset the alarm handler */
166 alarm(0); 171 alarm(0);
@@ -173,8 +178,9 @@ int main(int argc, char **argv) {
173 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"));
174 } 179 }
175 180
181 time_t current_time;
176 time(&current_time); 182 time(&current_time);
177 if ((int)(current_time - latest_entry_time) > (expire_minutes * 60)) { 183 if ((int)(current_time - latest_entry_time) > (config.expire_minutes * 60)) {
178 result = STATE_WARNING; 184 result = STATE_WARNING;
179 } else { 185 } else {
180 result = STATE_OK; 186 result = STATE_OK;
@@ -187,39 +193,45 @@ int main(int argc, char **argv) {
187 (int)(current_time - latest_entry_time)); 193 (int)(current_time - latest_entry_time));
188 printf("\n"); 194 printf("\n");
189 195
190 return result; 196 exit(result);
191} 197}
192 198
193/* process command-line arguments */ 199/* process command-line arguments */
194int process_arguments(int argc, char **argv) { 200check_nagios_config_wrapper process_arguments(int argc, char **argv) {
195 int c;
196
197 int option = 0;
198 static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'}, 201 static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'},
199 {"command", required_argument, 0, 'C'}, {"timeout", optional_argument, 0, 't'}, 202 {"command", required_argument, 0, 'C'}, {"timeout", optional_argument, 0, 't'},
200 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, 203 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
201 {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}}; 204 {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}};
202 205
203 if (argc < 2) 206 check_nagios_config_wrapper result = {
204 return ERROR; 207 .errorcode = OK,
208 .config = check_nagios_config_init(),
209 };
210 if (argc < 2) {
211 result.errorcode = ERROR;
212 return result;
213 }
205 214
206 if (!is_option(argv[1])) { 215 if (!is_option(argv[1])) {
207 status_log = argv[1]; 216 result.config.status_log = argv[1];
208 if (is_intnonneg(argv[2])) 217 if (is_intnonneg(argv[2])) {
209 expire_minutes = atoi(argv[2]); 218 result.config.expire_minutes = atoi(argv[2]);
210 else 219 } else {
211 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n")); 220 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n"));
212 process_string = argv[3]; 221 }
213 return OK; 222 result.config.process_string = argv[3];
223 return result;
214 } 224 }
215 225
216 while (1) { 226 int option = 0;
217 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);
218 229
219 if (c == -1 || c == EOF || c == 1) 230 if (option_index == -1 || option_index == EOF || option_index == 1) {
220 break; 231 break;
232 }
221 233
222 switch (c) { 234 switch (option_index) {
223 case 'h': /* help */ 235 case 'h': /* help */
224 print_help(); 236 print_help();
225 exit(STATE_UNKNOWN); 237 exit(STATE_UNKNOWN);
@@ -227,22 +239,24 @@ int process_arguments(int argc, char **argv) {
227 print_revision(progname, NP_VERSION); 239 print_revision(progname, NP_VERSION);
228 exit(STATE_UNKNOWN); 240 exit(STATE_UNKNOWN);
229 case 'F': /* status log */ 241 case 'F': /* status log */
230 status_log = optarg; 242 result.config.status_log = optarg;
231 break; 243 break;
232 case 'C': /* command */ 244 case 'C': /* command */
233 process_string = optarg; 245 result.config.process_string = optarg;
234 break; 246 break;
235 case 'e': /* expiry time */ 247 case 'e': /* expiry time */
236 if (is_intnonneg(optarg)) 248 if (is_intnonneg(optarg)) {
237 expire_minutes = atoi(optarg); 249 result.config.expire_minutes = atoi(optarg);
238 else 250 } else {
239 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n")); 251 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n"));
252 }
240 break; 253 break;
241 case 't': /* timeout */ 254 case 't': /* timeout */
242 if (is_intnonneg(optarg)) 255 if (is_intnonneg(optarg)) {
243 timeout_interval = atoi(optarg); 256 timeout_interval = atoi(optarg);
244 else 257 } else {
245 die(STATE_UNKNOWN, _("Timeout must be an integer (seconds)\n")); 258 die(STATE_UNKNOWN, _("Timeout must be an integer (seconds)\n"));
259 }
246 break; 260 break;
247 case 'v': 261 case 'v':
248 verbose++; 262 verbose++;
@@ -252,13 +266,15 @@ int process_arguments(int argc, char **argv) {
252 } 266 }
253 } 267 }
254 268
255 if (status_log == NULL) 269 if (result.config.status_log == NULL) {
256 die(STATE_UNKNOWN, _("You must provide the status_log\n")); 270 die(STATE_UNKNOWN, _("You must provide the status_log\n"));
271 }
257 272
258 if (process_string == NULL) 273 if (result.config.process_string == NULL) {
259 die(STATE_UNKNOWN, _("You must provide a process string\n")); 274 die(STATE_UNKNOWN, _("You must provide a process string\n"));
275 }
260 276
261 return OK; 277 return result;
262} 278}
263 279
264void print_help(void) { 280void print_help(void) {
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
index dec0b668..35ca92cd 100644
--- a/plugins/check_nt.c
+++ b/plugins/check_nt.c
@@ -13,7 +13,7 @@
13 * This plugin collects data from the NSClient service running on a 13 * This plugin collects data from the NSClient service running on a
14 * Windows NT/2000/XP/2003 server. 14 * Windows NT/2000/XP/2003 server.
15 * This plugin requires NSClient software to run on NT 15 * This plugin requires NSClient software to run on NT
16 * (http://nsclient.ready2run.nl/) 16 * (https://nsclient.org/)
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
@@ -39,82 +39,28 @@ const char *email = "devel@monitoring-plugins.org";
39#include "common.h" 39#include "common.h"
40#include "netutils.h" 40#include "netutils.h"
41#include "utils.h" 41#include "utils.h"
42 42#include "check_nt.d/config.h"
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 43
57enum { 44enum {
58 MAX_VALUE_LIST = 30, 45 MAX_VALUE_LIST = 30,
59 PORT = 1248
60}; 46};
61 47
62static char *server_address = NULL;
63static int server_port = PORT;
64static char *value_list = NULL;
65static char *req_password = NULL;
66static unsigned long lvalue_list[MAX_VALUE_LIST];
67static unsigned long warning_value = 0L;
68static unsigned long critical_value = 0L;
69static bool check_warning_value = false;
70static bool check_critical_value = false;
71static enum checkvars vars_to_check = CHECK_NONE;
72static bool show_all = false;
73
74static char recv_buffer[MAX_INPUT_BUFFER]; 48static char recv_buffer[MAX_INPUT_BUFFER];
75 49
76static void fetch_data(const char *address, int port, const char *sendb); 50static void fetch_data(const char *address, int port, const char *sendb);
77static int process_arguments(int /*argc*/, char ** /*argv*/); 51
52typedef struct {
53 int errorcode;
54 check_nt_config config;
55} check_nt_config_wrapper;
56static check_nt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57
78static void preparelist(char *string); 58static void preparelist(char *string);
79static bool strtoularray(unsigned long *array, char *string, const char *delim); 59static bool strtoularray(unsigned long *array, char *string, const char *delim);
80static void print_help(void); 60static void print_help(void);
81void print_usage(void); 61void print_usage(void);
82 62
83int main(int argc, char **argv) { 63int main(int argc, char **argv) {
84
85 /* should be int result = STATE_UNKNOWN; */
86
87 int return_code = STATE_UNKNOWN;
88 char *send_buffer = NULL;
89 char *output_message = NULL;
90 char *perfdata = NULL;
91 char *temp_string = NULL;
92 char *temp_string_perf = NULL;
93 char *description = NULL, *counter_unit = NULL;
94 char *minval = NULL, *maxval = NULL, *errcvt = NULL;
95 char *fds = NULL, *tds = NULL;
96 char *numstr;
97
98 double total_disk_space = 0;
99 double free_disk_space = 0;
100 double percent_used_space = 0;
101 double warning_used_space = 0;
102 double critical_used_space = 0;
103 double mem_commitLimit = 0;
104 double mem_commitByte = 0;
105 double fminval = 0, fmaxval = 0;
106 unsigned long utilization;
107 unsigned long uptime;
108 unsigned long age_in_minutes;
109 double counter_value = 0.0;
110 int offset = 0;
111 int updays = 0;
112 int uphours = 0;
113 int upminutes = 0;
114
115 bool isPercent = false;
116 bool allRight = false;
117
118 setlocale(LC_ALL, ""); 64 setlocale(LC_ALL, "");
119 bindtextdomain(PACKAGE, LOCALEDIR); 65 bindtextdomain(PACKAGE, LOCALEDIR);
120 textdomain(PACKAGE); 66 textdomain(PACKAGE);
@@ -122,8 +68,12 @@ int main(int argc, char **argv) {
122 /* Parse extra opts if any */ 68 /* Parse extra opts if any */
123 argv = np_extra_opts(&argc, argv, progname); 69 argv = np_extra_opts(&argc, argv, progname);
124 70
125 if (process_arguments(argc, argv) == ERROR) 71 check_nt_config_wrapper tmp_config = process_arguments(argc, argv);
72 if (tmp_config.errorcode == ERROR) {
126 usage4(_("Could not parse arguments")); 73 usage4(_("Could not parse arguments"));
74 }
75
76 const check_nt_config config = tmp_config.config;
127 77
128 /* initialize alarm signal handling */ 78 /* initialize alarm signal handling */
129 signal(SIGALRM, socket_timeout_alarm_handler); 79 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -131,54 +81,68 @@ int main(int argc, char **argv) {
131 /* set socket timeout */ 81 /* set socket timeout */
132 alarm(socket_timeout); 82 alarm(socket_timeout);
133 83
134 switch (vars_to_check) { 84 int return_code = STATE_UNKNOWN;
135 85 char *send_buffer = NULL;
86 char *output_message = NULL;
87 char *perfdata = NULL;
88 char *temp_string = NULL;
89 char *temp_string_perf = NULL;
90 char *description = NULL;
91 char *counter_unit = NULL;
92 char *errcvt = NULL;
93 unsigned long lvalue_list[MAX_VALUE_LIST];
94 switch (config.vars_to_check) {
136 case CHECK_CLIENTVERSION: 95 case CHECK_CLIENTVERSION:
137 96 xasprintf(&send_buffer, "%s&1", config.req_password);
138 xasprintf(&send_buffer, "%s&1", req_password); 97 fetch_data(config.server_address, config.server_port, send_buffer);
139 fetch_data(server_address, server_port, send_buffer); 98 if (config.value_list != NULL && strcmp(recv_buffer, config.value_list) != 0) {
140 if (value_list != NULL && strcmp(recv_buffer, value_list) != 0) { 99 xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"),
141 xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"), recv_buffer, value_list); 100 recv_buffer, config.value_list);
142 return_code = STATE_WARNING; 101 return_code = STATE_WARNING;
143 } else { 102 } else {
144 xasprintf(&output_message, "%s", recv_buffer); 103 xasprintf(&output_message, "%s", recv_buffer);
145 return_code = STATE_OK; 104 return_code = STATE_OK;
146 } 105 }
147 break; 106 break;
148
149 case CHECK_CPULOAD: 107 case CHECK_CPULOAD:
150 108 if (config.value_list == NULL) {
151 if (value_list == NULL)
152 output_message = strdup(_("missing -l parameters")); 109 output_message = strdup(_("missing -l parameters"));
153 else if (!strtoularray(lvalue_list, value_list, ",")) 110 } else if (!strtoularray(lvalue_list, config.value_list, ",")) {
154 output_message = strdup(_("wrong -l parameter.")); 111 output_message = strdup(_("wrong -l parameter."));
155 else { 112 } else {
156 /* -l parameters is present with only integers */ 113 /* -l parameters is present with only integers */
157 return_code = STATE_OK; 114 return_code = STATE_OK;
158 temp_string = strdup(_("CPU Load")); 115 temp_string = strdup(_("CPU Load"));
159 temp_string_perf = strdup(" "); 116 temp_string_perf = strdup(" ");
160 117
161 /* loop until one of the parameters is wrong or not present */ 118 /* loop until one of the parameters is wrong or not present */
162 while (lvalue_list[0 + offset] > (unsigned long)0 && lvalue_list[0 + offset] <= (unsigned long)17280 && 119 int offset = 0;
163 lvalue_list[1 + offset] > (unsigned long)0 && lvalue_list[1 + offset] <= (unsigned long)100 && 120 while (lvalue_list[0 + offset] > (unsigned long)0 &&
164 lvalue_list[2 + offset] > (unsigned long)0 && lvalue_list[2 + offset] <= (unsigned long)100) { 121 lvalue_list[0 + offset] <= (unsigned long)17280 &&
122 lvalue_list[1 + offset] > (unsigned long)0 &&
123 lvalue_list[1 + offset] <= (unsigned long)100 &&
124 lvalue_list[2 + offset] > (unsigned long)0 &&
125 lvalue_list[2 + offset] <= (unsigned long)100) {
165 126
166 /* Send request and retrieve data */ 127 /* Send request and retrieve data */
167 xasprintf(&send_buffer, "%s&2&%lu", req_password, lvalue_list[0 + offset]); 128 xasprintf(&send_buffer, "%s&2&%lu", config.req_password, lvalue_list[0 + offset]);
168 fetch_data(server_address, server_port, send_buffer); 129 fetch_data(config.server_address, config.server_port, send_buffer);
169 130
170 utilization = strtoul(recv_buffer, NULL, 10); 131 unsigned long utilization = strtoul(recv_buffer, NULL, 10);
171 132
172 /* Check if any of the request is in a warning or critical state */ 133 /* Check if any of the request is in a warning or critical state */
173 if (utilization >= lvalue_list[2 + offset]) 134 if (utilization >= lvalue_list[2 + offset]) {
174 return_code = STATE_CRITICAL; 135 return_code = STATE_CRITICAL;
175 else if (utilization >= lvalue_list[1 + offset] && return_code < STATE_WARNING) 136 } else if (utilization >= lvalue_list[1 + offset] && return_code < STATE_WARNING) {
176 return_code = STATE_WARNING; 137 return_code = STATE_WARNING;
138 }
177 139
178 xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization, lvalue_list[0 + offset]); 140 xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization,
141 lvalue_list[0 + offset]);
179 xasprintf(&temp_string, "%s%s", temp_string, output_message); 142 xasprintf(&temp_string, "%s%s", temp_string, output_message);
180 xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"), lvalue_list[0 + offset], utilization, 143 xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"),
181 lvalue_list[1 + offset], lvalue_list[2 + offset]); 144 lvalue_list[0 + offset], utilization, lvalue_list[1 + offset],
145 lvalue_list[2 + offset]);
182 xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata); 146 xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata);
183 offset += 3; /* move across the array */ 147 offset += 3; /* move across the array */
184 } 148 }
@@ -186,82 +150,97 @@ int main(int argc, char **argv) {
186 if (strlen(temp_string) > 10) { /* we had at least one loop */ 150 if (strlen(temp_string) > 10) { /* we had at least one loop */
187 output_message = strdup(temp_string); 151 output_message = strdup(temp_string);
188 perfdata = temp_string_perf; 152 perfdata = temp_string_perf;
189 } else 153 } else {
190 output_message = strdup(_("not enough values for -l parameters")); 154 output_message = strdup(_("not enough values for -l parameters"));
155 }
191 } 156 }
192 break; 157 break;
193 158 case CHECK_UPTIME: {
194 case CHECK_UPTIME: 159 char *tmp_value_list = config.value_list;
195 160 if (config.value_list == NULL) {
196 if (value_list == NULL) { 161 tmp_value_list = "minutes";
197 value_list = "minutes";
198 } 162 }
199 if (strncmp(value_list, "seconds", strlen("seconds") + 1) && strncmp(value_list, "minutes", strlen("minutes") + 1) && 163 if (strncmp(tmp_value_list, "seconds", strlen("seconds") + 1) &&
200 strncmp(value_list, "hours", strlen("hours") + 1) && strncmp(value_list, "days", strlen("days") + 1)) { 164 strncmp(tmp_value_list, "minutes", strlen("minutes") + 1) &&
165 strncmp(config.value_list, "hours", strlen("hours") + 1) &&
166 strncmp(tmp_value_list, "days", strlen("days") + 1)) {
201 167
202 output_message = strdup(_("wrong -l argument")); 168 output_message = strdup(_("wrong -l argument"));
203 } else { 169 } else {
204 xasprintf(&send_buffer, "%s&3", req_password); 170 xasprintf(&send_buffer, "%s&3", config.req_password);
205 fetch_data(server_address, server_port, send_buffer); 171 fetch_data(config.server_address, config.server_port, send_buffer);
206 uptime = strtoul(recv_buffer, NULL, 10); 172 unsigned long uptime = strtoul(recv_buffer, NULL, 10);
207 updays = uptime / 86400; 173 int updays = uptime / 86400;
208 uphours = (uptime % 86400) / 3600; 174 int uphours = (uptime % 86400) / 3600;
209 upminutes = ((uptime % 86400) % 3600) / 60; 175 int upminutes = ((uptime % 86400) % 3600) / 60;
210 176
211 if (!strncmp(value_list, "minutes", strlen("minutes"))) 177 if (!strncmp(tmp_value_list, "minutes", strlen("minutes"))) {
212 uptime = uptime / 60; 178 uptime = uptime / 60;
213 else if (!strncmp(value_list, "hours", strlen("hours"))) 179 } else if (!strncmp(tmp_value_list, "hours", strlen("hours"))) {
214 uptime = uptime / 3600; 180 uptime = uptime / 3600;
215 else if (!strncmp(value_list, "days", strlen("days"))) 181 } else if (!strncmp(tmp_value_list, "days", strlen("days"))) {
216 uptime = uptime / 86400; 182 uptime = uptime / 86400;
183 }
217 /* else uptime in seconds, nothing to do */ 184 /* else uptime in seconds, nothing to do */
218 185
219 xasprintf(&output_message, _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays, uphours, upminutes, 186 xasprintf(&output_message,
220 uptime); 187 _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays,
188 uphours, upminutes, uptime);
221 189
222 if (check_critical_value && uptime <= critical_value) 190 if (config.check_critical_value && uptime <= config.critical_value) {
223 return_code = STATE_CRITICAL; 191 return_code = STATE_CRITICAL;
224 else if (check_warning_value && uptime <= warning_value) 192 } else if (config.check_warning_value && uptime <= config.warning_value) {
225 return_code = STATE_WARNING; 193 return_code = STATE_WARNING;
226 else 194 } else {
227 return_code = STATE_OK; 195 return_code = STATE_OK;
196 }
228 } 197 }
229 break; 198 } break;
230
231 case CHECK_USEDDISKSPACE: 199 case CHECK_USEDDISKSPACE:
232 200 if (config.value_list == NULL) {
233 if (value_list == NULL)
234 output_message = strdup(_("missing -l parameters")); 201 output_message = strdup(_("missing -l parameters"));
235 else if (strlen(value_list) != 1) 202 } else if (strlen(config.value_list) != 1) {
236 output_message = strdup(_("wrong -l argument")); 203 output_message = strdup(_("wrong -l argument"));
237 else { 204 } else {
238 xasprintf(&send_buffer, "%s&4&%s", req_password, value_list); 205 xasprintf(&send_buffer, "%s&4&%s", config.req_password, config.value_list);
239 fetch_data(server_address, server_port, send_buffer); 206 fetch_data(config.server_address, config.server_port, send_buffer);
240 fds = strtok(recv_buffer, "&"); 207 char *fds = strtok(recv_buffer, "&");
241 tds = strtok(NULL, "&"); 208 char *tds = strtok(NULL, "&");
242 if (fds != NULL) 209 double total_disk_space = 0;
210 double free_disk_space = 0;
211 if (fds != NULL) {
243 free_disk_space = atof(fds); 212 free_disk_space = atof(fds);
244 if (tds != NULL) 213 }
214 if (tds != NULL) {
245 total_disk_space = atof(tds); 215 total_disk_space = atof(tds);
216 }
246 217
247 if (total_disk_space > 0 && free_disk_space >= 0) { 218 if (total_disk_space > 0 && free_disk_space >= 0) {
248 percent_used_space = ((total_disk_space - free_disk_space) / total_disk_space) * 100; 219 double percent_used_space =
249 warning_used_space = ((float)warning_value / 100) * total_disk_space; 220 ((total_disk_space - free_disk_space) / total_disk_space) * 100;
250 critical_used_space = ((float)critical_value / 100) * total_disk_space; 221 double warning_used_space = ((float)config.warning_value / 100) * total_disk_space;
251 222 double critical_used_space =
252 xasprintf(&temp_string, _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"), value_list, 223 ((float)config.critical_value / 100) * total_disk_space;
253 total_disk_space / 1073741824, (total_disk_space - free_disk_space) / 1073741824, percent_used_space, 224
254 free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100); 225 xasprintf(
255 xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"), value_list, 226 &temp_string,
256 (total_disk_space - free_disk_space) / 1073741824, warning_used_space / 1073741824, 227 _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"),
257 critical_used_space / 1073741824, total_disk_space / 1073741824); 228 config.value_list, total_disk_space / 1073741824,
258 229 (total_disk_space - free_disk_space) / 1073741824, percent_used_space,
259 if (check_critical_value && percent_used_space >= critical_value) 230 free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100);
231 xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"),
232 config.value_list, (total_disk_space - free_disk_space) / 1073741824,
233 warning_used_space / 1073741824, critical_used_space / 1073741824,
234 total_disk_space / 1073741824);
235
236 if (config.check_critical_value && percent_used_space >= config.critical_value) {
260 return_code = STATE_CRITICAL; 237 return_code = STATE_CRITICAL;
261 else if (check_warning_value && percent_used_space >= warning_value) 238 } else if (config.check_warning_value &&
239 percent_used_space >= config.warning_value) {
262 return_code = STATE_WARNING; 240 return_code = STATE_WARNING;
263 else 241 } else {
264 return_code = STATE_OK; 242 return_code = STATE_OK;
243 }
265 244
266 output_message = strdup(temp_string); 245 output_message = strdup(temp_string);
267 perfdata = temp_string_perf; 246 perfdata = temp_string_perf;
@@ -271,60 +250,64 @@ int main(int argc, char **argv) {
271 } 250 }
272 } 251 }
273 break; 252 break;
274
275 case CHECK_SERVICESTATE: 253 case CHECK_SERVICESTATE:
276 case CHECK_PROCSTATE: 254 case CHECK_PROCSTATE:
277 255 if (config.value_list == NULL) {
278 if (value_list == NULL)
279 output_message = strdup(_("No service/process specified")); 256 output_message = strdup(_("No service/process specified"));
280 else { 257 } else {
281 preparelist(value_list); /* replace , between services with & to send the request */ 258 preparelist(
282 xasprintf(&send_buffer, "%s&%u&%s&%s", req_password, (vars_to_check == CHECK_SERVICESTATE) ? 5 : 6, 259 config.value_list); /* replace , between services with & to send the request */
283 (show_all) ? "ShowAll" : "ShowFail", value_list); 260 xasprintf(&send_buffer, "%s&%u&%s&%s", config.req_password,
284 fetch_data(server_address, server_port, send_buffer); 261 (config.vars_to_check == CHECK_SERVICESTATE) ? 5 : 6,
285 numstr = strtok(recv_buffer, "&"); 262 (config.show_all) ? "ShowAll" : "ShowFail", config.value_list);
286 if (numstr == NULL) 263 fetch_data(config.server_address, config.server_port, send_buffer);
264 char *numstr = strtok(recv_buffer, "&");
265 if (numstr == NULL) {
287 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 266 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
267 }
288 return_code = atoi(numstr); 268 return_code = atoi(numstr);
289 temp_string = strtok(NULL, "&"); 269 temp_string = strtok(NULL, "&");
290 output_message = strdup(temp_string); 270 output_message = strdup(temp_string);
291 } 271 }
292 break; 272 break;
293
294 case CHECK_MEMUSE: 273 case CHECK_MEMUSE:
295 274 xasprintf(&send_buffer, "%s&7", config.req_password);
296 xasprintf(&send_buffer, "%s&7", req_password); 275 fetch_data(config.server_address, config.server_port, send_buffer);
297 fetch_data(server_address, server_port, send_buffer); 276 char *numstr = strtok(recv_buffer, "&");
298 numstr = strtok(recv_buffer, "&"); 277 if (numstr == NULL) {
299 if (numstr == NULL)
300 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 278 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
301 mem_commitLimit = atof(numstr); 279 }
280 double mem_commitLimit = atof(numstr);
302 numstr = strtok(NULL, "&"); 281 numstr = strtok(NULL, "&");
303 if (numstr == NULL) 282 if (numstr == NULL) {
304 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 283 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
305 mem_commitByte = atof(numstr); 284 }
306 percent_used_space = (mem_commitByte / mem_commitLimit) * 100; 285 double mem_commitByte = atof(numstr);
307 warning_used_space = ((float)warning_value / 100) * mem_commitLimit; 286 double percent_used_space = (mem_commitByte / mem_commitLimit) * 100;
308 critical_used_space = ((float)critical_value / 100) * mem_commitLimit; 287 double warning_used_space = ((float)config.warning_value / 100) * mem_commitLimit;
288 double critical_used_space = ((float)config.critical_value / 100) * mem_commitLimit;
309 289
310 /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here, 290 /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here,
311 which equals RAM + Pagefiles. */ 291 which equals RAM + Pagefiles. */
312 xasprintf(&output_message, _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"), 292 xasprintf(
313 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space, (mem_commitLimit - mem_commitByte) / 1048567, 293 &output_message,
314 (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100); 294 _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"),
315 xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"), mem_commitByte / 1048567, warning_used_space / 1048567, 295 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space,
296 (mem_commitLimit - mem_commitByte) / 1048567,
297 (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100);
298 xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"),
299 mem_commitByte / 1048567, warning_used_space / 1048567,
316 critical_used_space / 1048567, mem_commitLimit / 1048567); 300 critical_used_space / 1048567, mem_commitLimit / 1048567);
317 301
318 return_code = STATE_OK; 302 return_code = STATE_OK;
319 if (check_critical_value && percent_used_space >= critical_value) 303 if (config.check_critical_value && percent_used_space >= config.critical_value) {
320 return_code = STATE_CRITICAL; 304 return_code = STATE_CRITICAL;
321 else if (check_warning_value && percent_used_space >= warning_value) 305 } else if (config.check_warning_value && percent_used_space >= config.warning_value) {
322 return_code = STATE_WARNING; 306 return_code = STATE_WARNING;
307 }
323 308
324 break; 309 break;
325 310 case CHECK_COUNTER: {
326 case CHECK_COUNTER:
327
328 /* 311 /*
329 CHECK_COUNTER has been modified to provide extensive perfdata information. 312 CHECK_COUNTER has been modified to provide extensive perfdata information.
330 In order to do this, some modifications have been done to the code 313 In order to do this, some modifications have been done to the code
@@ -341,31 +324,38 @@ int main(int argc, char **argv) {
341 the counter unit - that is, the dimensions of the counter you're getting. Examples: 324 the counter unit - that is, the dimensions of the counter you're getting. Examples:
342 pages/s, packets transferred, etc. 325 pages/s, packets transferred, etc.
343 326
344 4) If you want, you may provide the minimum and maximum values to expect. They aren't mandatory, 327 4) If you want, you may provide the minimum and maximum values to expect. They aren't
345 but once specified they MUST have the same order of magnitude and units of -w and -c; otherwise. 328 mandatory, but once specified they MUST have the same order of magnitude and units of -w and
346 strange things will happen when you make graphs of your data. 329 -c; otherwise. strange things will happen when you make graphs of your data.
347 */ 330 */
348 331
349 if (value_list == NULL) 332 double counter_value = 0.0;
333 if (config.value_list == NULL) {
350 output_message = strdup(_("No counter specified")); 334 output_message = strdup(_("No counter specified"));
351 else { 335 } else {
352 preparelist(value_list); /* replace , between services with & to send the request */ 336 preparelist(
353 isPercent = (strchr(value_list, '%') != NULL); 337 config.value_list); /* replace , between services with & to send the request */
338 bool isPercent = (strchr(config.value_list, '%') != NULL);
354 339
355 strtok(value_list, "&"); /* burn the first parameters */ 340 strtok(config.value_list, "&"); /* burn the first parameters */
356 description = strtok(NULL, "&"); 341 description = strtok(NULL, "&");
357 counter_unit = strtok(NULL, "&"); 342 counter_unit = strtok(NULL, "&");
358 xasprintf(&send_buffer, "%s&8&%s", req_password, value_list); 343 xasprintf(&send_buffer, "%s&8&%s", config.req_password, config.value_list);
359 fetch_data(server_address, server_port, send_buffer); 344 fetch_data(config.server_address, config.server_port, send_buffer);
360 counter_value = atof(recv_buffer); 345 counter_value = atof(recv_buffer);
361 346
362 if (description == NULL) 347 bool allRight = false;
348 if (description == NULL) {
363 xasprintf(&output_message, "%.f", counter_value); 349 xasprintf(&output_message, "%.f", counter_value);
364 else if (isPercent) { 350 } else if (isPercent) {
365 counter_unit = strdup("%"); 351 counter_unit = strdup("%");
366 allRight = true; 352 allRight = true;
367 } 353 }
368 354
355 char *minval = NULL;
356 char *maxval = NULL;
357 double fminval = 0;
358 double fmaxval = 0;
369 if ((counter_unit != NULL) && (!allRight)) { 359 if ((counter_unit != NULL) && (!allRight)) {
370 minval = strtok(NULL, "&"); 360 minval = strtok(NULL, "&");
371 maxval = strtok(NULL, "&"); 361 maxval = strtok(NULL, "&");
@@ -375,84 +365,92 @@ int main(int argc, char **argv) {
375 fminval = (minval != NULL) ? strtod(minval, &errcvt) : -1; 365 fminval = (minval != NULL) ? strtod(minval, &errcvt) : -1;
376 fmaxval = (minval != NULL) ? strtod(maxval, &errcvt) : -1; 366 fmaxval = (minval != NULL) ? strtod(maxval, &errcvt) : -1;
377 367
378 if ((fminval == 0) && (minval == errcvt)) 368 if ((fminval == 0) && (minval == errcvt)) {
379 output_message = strdup(_("Minimum value contains non-numbers")); 369 output_message = strdup(_("Minimum value contains non-numbers"));
380 else { 370 } else {
381 if ((fmaxval == 0) && (maxval == errcvt)) 371 if ((fmaxval == 0) && (maxval == errcvt)) {
382 output_message = strdup(_("Maximum value contains non-numbers")); 372 output_message = strdup(_("Maximum value contains non-numbers"));
383 else 373 } else {
384 allRight = true; /* Everything is OK. */ 374 allRight = true; /* Everything is OK. */
375 }
385 } 376 }
386 } else if ((counter_unit == NULL) && (description != NULL)) 377 } else if ((counter_unit == NULL) && (description != NULL)) {
387 output_message = strdup(_("No unit counter specified")); 378 output_message = strdup(_("No unit counter specified"));
379 }
388 380
389 if (allRight) { 381 if (allRight) {
390 /* Let's format the output string, finally... */ 382 /* Let's format the output string, finally... */
391 if (strstr(description, "%") == NULL) { 383 if (strstr(description, "%") == NULL) {
392 xasprintf(&output_message, "%s = %.2f %s", description, counter_value, counter_unit); 384 xasprintf(&output_message, "%s = %.2f %s", description, counter_value,
385 counter_unit);
393 } else { 386 } else {
394 /* has formatting, will segv if wrong */ 387 /* has formatting, will segv if wrong */
395 xasprintf(&output_message, description, counter_value); 388 xasprintf(&output_message, description, counter_value);
396 } 389 }
397 xasprintf(&output_message, "%s |", output_message); 390 xasprintf(&output_message, "%s |", output_message);
398 xasprintf(&output_message, "%s %s", output_message, 391 xasprintf(&output_message, "%s %s", output_message,
399 fperfdata(description, counter_value, counter_unit, 1, warning_value, 1, critical_value, 392 fperfdata(description, counter_value, counter_unit, 1,
400 (!(isPercent) && (minval != NULL)), fminval, (!(isPercent) && (minval != NULL)), fmaxval)); 393 config.warning_value, 1, config.critical_value,
394 (!(isPercent) && (minval != NULL)), fminval,
395 (!(isPercent) && (minval != NULL)), fmaxval));
401 } 396 }
402 } 397 }
403 398
404 if (critical_value > warning_value) { /* Normal thresholds */ 399 if (config.critical_value > config.warning_value) { /* Normal thresholds */
405 if (check_critical_value && counter_value >= critical_value) 400 if (config.check_critical_value && counter_value >= config.critical_value) {
406 return_code = STATE_CRITICAL; 401 return_code = STATE_CRITICAL;
407 else if (check_warning_value && counter_value >= warning_value) 402 } else if (config.check_warning_value && counter_value >= config.warning_value) {
408 return_code = STATE_WARNING; 403 return_code = STATE_WARNING;
409 else 404 } else {
410 return_code = STATE_OK; 405 return_code = STATE_OK;
406 }
411 } else { /* inverse thresholds */ 407 } else { /* inverse thresholds */
412 return_code = STATE_OK; 408 return_code = STATE_OK;
413 if (check_critical_value && counter_value <= critical_value) 409 if (config.check_critical_value && counter_value <= config.critical_value) {
414 return_code = STATE_CRITICAL; 410 return_code = STATE_CRITICAL;
415 else if (check_warning_value && counter_value <= warning_value) 411 } else if (config.check_warning_value && counter_value <= config.warning_value) {
416 return_code = STATE_WARNING; 412 return_code = STATE_WARNING;
413 }
417 } 414 }
418 break; 415 } break;
419
420 case CHECK_FILEAGE: 416 case CHECK_FILEAGE:
421 417 if (config.value_list == NULL) {
422 if (value_list == NULL)
423 output_message = strdup(_("No counter specified")); 418 output_message = strdup(_("No counter specified"));
424 else { 419 } else {
425 preparelist(value_list); /* replace , between services with & to send the request */ 420 preparelist(
426 xasprintf(&send_buffer, "%s&9&%s", req_password, value_list); 421 config.value_list); /* replace , between services with & to send the request */
427 fetch_data(server_address, server_port, send_buffer); 422 xasprintf(&send_buffer, "%s&9&%s", config.req_password, config.value_list);
428 age_in_minutes = atoi(strtok(recv_buffer, "&")); 423 fetch_data(config.server_address, config.server_port, send_buffer);
424 unsigned long age_in_minutes = atoi(strtok(recv_buffer, "&"));
429 description = strtok(NULL, "&"); 425 description = strtok(NULL, "&");
430 output_message = strdup(description); 426 output_message = strdup(description);
431 427
432 if (critical_value > warning_value) { /* Normal thresholds */ 428 if (config.critical_value > config.warning_value) { /* Normal thresholds */
433 if (check_critical_value && age_in_minutes >= critical_value) 429 if (config.check_critical_value && age_in_minutes >= config.critical_value) {
434 return_code = STATE_CRITICAL; 430 return_code = STATE_CRITICAL;
435 else if (check_warning_value && age_in_minutes >= warning_value) 431 } else if (config.check_warning_value && age_in_minutes >= config.warning_value) {
436 return_code = STATE_WARNING; 432 return_code = STATE_WARNING;
437 else 433 } else {
438 return_code = STATE_OK; 434 return_code = STATE_OK;
435 }
439 } else { /* inverse thresholds */ 436 } else { /* inverse thresholds */
440 if (check_critical_value && age_in_minutes <= critical_value) 437 if (config.check_critical_value && age_in_minutes <= config.critical_value) {
441 return_code = STATE_CRITICAL; 438 return_code = STATE_CRITICAL;
442 else if (check_warning_value && age_in_minutes <= warning_value) 439 } else if (config.check_warning_value && age_in_minutes <= config.warning_value) {
443 return_code = STATE_WARNING; 440 return_code = STATE_WARNING;
444 else 441 } else {
445 return_code = STATE_OK; 442 return_code = STATE_OK;
443 }
446 } 444 }
447 } 445 }
448 break; 446 break;
449 447
450 case CHECK_INSTANCES: 448 case CHECK_INSTANCES:
451 if (value_list == NULL) 449 if (config.value_list == NULL) {
452 output_message = strdup(_("No counter specified")); 450 output_message = strdup(_("No counter specified"));
453 else { 451 } else {
454 xasprintf(&send_buffer, "%s&10&%s", req_password, value_list); 452 xasprintf(&send_buffer, "%s&10&%s", config.req_password, config.value_list);
455 fetch_data(server_address, server_port, send_buffer); 453 fetch_data(config.server_address, config.server_port, send_buffer);
456 if (!strncmp(recv_buffer, "ERROR", 5)) { 454 if (!strncmp(recv_buffer, "ERROR", 5)) {
457 printf("NSClient - %s\n", recv_buffer); 455 printf("NSClient - %s\n", recv_buffer);
458 exit(STATE_UNKNOWN); 456 exit(STATE_UNKNOWN);
@@ -471,18 +469,16 @@ int main(int argc, char **argv) {
471 /* reset timeout */ 469 /* reset timeout */
472 alarm(0); 470 alarm(0);
473 471
474 if (perfdata == NULL) 472 if (perfdata == NULL) {
475 printf("%s\n", output_message); 473 printf("%s\n", output_message);
476 else 474 } else {
477 printf("%s | %s\n", output_message, perfdata); 475 printf("%s | %s\n", output_message, perfdata);
476 }
478 return return_code; 477 return return_code;
479} 478}
480 479
481/* process command-line arguments */ 480/* process command-line arguments */
482int process_arguments(int argc, char **argv) { 481check_nt_config_wrapper process_arguments(int argc, char **argv) {
483 int c;
484
485 int option = 0;
486 static struct option longopts[] = {{"port", required_argument, 0, 'p'}, 482 static struct option longopts[] = {{"port", required_argument, 0, 'p'},
487 {"timeout", required_argument, 0, 't'}, 483 {"timeout", required_argument, 0, 't'},
488 {"critical", required_argument, 0, 'c'}, 484 {"critical", required_argument, 0, 'c'},
@@ -497,34 +493,44 @@ int process_arguments(int argc, char **argv) {
497 {"help", no_argument, 0, 'h'}, 493 {"help", no_argument, 0, 'h'},
498 {0, 0, 0, 0}}; 494 {0, 0, 0, 0}};
499 495
496 check_nt_config_wrapper result = {
497 .errorcode = OK,
498 .config = check_nt_config_init(),
499 };
500
500 /* no options were supplied */ 501 /* no options were supplied */
501 if (argc < 2) 502 if (argc < 2) {
502 return ERROR; 503 result.errorcode = ERROR;
504 return result;
505 }
503 506
504 /* backwards compatibility */ 507 /* backwards compatibility */
505 if (!is_option(argv[1])) { 508 if (!is_option(argv[1])) {
506 server_address = strdup(argv[1]); 509 result.config.server_address = strdup(argv[1]);
507 argv[1] = argv[0]; 510 argv[1] = argv[0];
508 argv = &argv[1]; 511 argv = &argv[1];
509 argc--; 512 argc--;
510 } 513 }
511 514
512 for (c = 1; c < argc; c++) { 515 for (int index = 1; index < argc; index++) {
513 if (strcmp("-to", argv[c]) == 0) 516 if (strcmp("-to", argv[index]) == 0) {
514 strcpy(argv[c], "-t"); 517 strcpy(argv[index], "-t");
515 else if (strcmp("-wv", argv[c]) == 0) 518 } else if (strcmp("-wv", argv[index]) == 0) {
516 strcpy(argv[c], "-w"); 519 strcpy(argv[index], "-w");
517 else if (strcmp("-cv", argv[c]) == 0) 520 } else if (strcmp("-cv", argv[index]) == 0) {
518 strcpy(argv[c], "-c"); 521 strcpy(argv[index], "-c");
522 }
519 } 523 }
520 524
521 while (1) { 525 int option = 0;
522 c = getopt_long(argc, argv, "+hVH:t:c:w:p:v:l:s:d:u", longopts, &option); 526 while (true) {
527 int option_index = getopt_long(argc, argv, "+hVH:t:c:w:p:v:l:s:d:u", longopts, &option);
523 528
524 if (c == -1 || c == EOF || c == 1) 529 if (option_index == -1 || option_index == EOF || option_index == 1) {
525 break; 530 break;
531 }
526 532
527 switch (c) { 533 switch (option_index) {
528 case '?': /* print short usage statement if args not parsable */ 534 case '?': /* print short usage statement if args not parsable */
529 usage5(); 535 usage5();
530 case 'h': /* help */ 536 case 'h': /* help */
@@ -534,118 +540,128 @@ int process_arguments(int argc, char **argv) {
534 print_revision(progname, NP_VERSION); 540 print_revision(progname, NP_VERSION);
535 exit(STATE_UNKNOWN); 541 exit(STATE_UNKNOWN);
536 case 'H': /* hostname */ 542 case 'H': /* hostname */
537 server_address = optarg; 543 result.config.server_address = optarg;
538 break; 544 break;
539 case 's': /* password */ 545 case 's': /* password */
540 req_password = optarg; 546 result.config.req_password = optarg;
541 break; 547 break;
542 case 'p': /* port */ 548 case 'p': /* port */
543 if (is_intnonneg(optarg)) 549 if (is_intnonneg(optarg)) {
544 server_port = atoi(optarg); 550 result.config.server_port = atoi(optarg);
545 else 551 } else {
546 die(STATE_UNKNOWN, _("Server port must be an integer\n")); 552 die(STATE_UNKNOWN, _("Server port must be an integer\n"));
553 }
547 break; 554 break;
548 case 'v': 555 case 'v':
549 if (strlen(optarg) < 4) 556 if (strlen(optarg) < 4) {
550 return ERROR; 557 result.errorcode = ERROR;
551 if (!strcmp(optarg, "CLIENTVERSION")) 558 return result;
552 vars_to_check = CHECK_CLIENTVERSION; 559 }
553 else if (!strcmp(optarg, "CPULOAD")) 560 if (!strcmp(optarg, "CLIENTVERSION")) {
554 vars_to_check = CHECK_CPULOAD; 561 result.config.vars_to_check = CHECK_CLIENTVERSION;
555 else if (!strcmp(optarg, "UPTIME")) 562 } else if (!strcmp(optarg, "CPULOAD")) {
556 vars_to_check = CHECK_UPTIME; 563 result.config.vars_to_check = CHECK_CPULOAD;
557 else if (!strcmp(optarg, "USEDDISKSPACE")) 564 } else if (!strcmp(optarg, "UPTIME")) {
558 vars_to_check = CHECK_USEDDISKSPACE; 565 result.config.vars_to_check = CHECK_UPTIME;
559 else if (!strcmp(optarg, "SERVICESTATE")) 566 } else if (!strcmp(optarg, "USEDDISKSPACE")) {
560 vars_to_check = CHECK_SERVICESTATE; 567 result.config.vars_to_check = CHECK_USEDDISKSPACE;
561 else if (!strcmp(optarg, "PROCSTATE")) 568 } else if (!strcmp(optarg, "SERVICESTATE")) {
562 vars_to_check = CHECK_PROCSTATE; 569 result.config.vars_to_check = CHECK_SERVICESTATE;
563 else if (!strcmp(optarg, "MEMUSE")) 570 } else if (!strcmp(optarg, "PROCSTATE")) {
564 vars_to_check = CHECK_MEMUSE; 571 result.config.vars_to_check = CHECK_PROCSTATE;
565 else if (!strcmp(optarg, "COUNTER")) 572 } else if (!strcmp(optarg, "MEMUSE")) {
566 vars_to_check = CHECK_COUNTER; 573 result.config.vars_to_check = CHECK_MEMUSE;
567 else if (!strcmp(optarg, "FILEAGE")) 574 } else if (!strcmp(optarg, "COUNTER")) {
568 vars_to_check = CHECK_FILEAGE; 575 result.config.vars_to_check = CHECK_COUNTER;
569 else if (!strcmp(optarg, "INSTANCES")) 576 } else if (!strcmp(optarg, "FILEAGE")) {
570 vars_to_check = CHECK_INSTANCES; 577 result.config.vars_to_check = CHECK_FILEAGE;
571 else 578 } else if (!strcmp(optarg, "INSTANCES")) {
572 return ERROR; 579 result.config.vars_to_check = CHECK_INSTANCES;
580 } else {
581 result.errorcode = ERROR;
582 return result;
583 }
573 break; 584 break;
574 case 'l': /* value list */ 585 case 'l': /* value list */
575 value_list = optarg; 586 result.config.value_list = optarg;
576 break; 587 break;
577 case 'w': /* warning threshold */ 588 case 'w': /* warning threshold */
578 warning_value = strtoul(optarg, NULL, 10); 589 result.config.warning_value = strtoul(optarg, NULL, 10);
579 check_warning_value = true; 590 result.config.check_warning_value = true;
580 break; 591 break;
581 case 'c': /* critical threshold */ 592 case 'c': /* critical threshold */
582 critical_value = strtoul(optarg, NULL, 10); 593 result.config.critical_value = strtoul(optarg, NULL, 10);
583 check_critical_value = true; 594 result.config.check_critical_value = true;
584 break; 595 break;
585 case 'd': /* Display select for services */ 596 case 'd': /* Display select for services */
586 if (!strcmp(optarg, "SHOWALL")) 597 if (!strcmp(optarg, "SHOWALL")) {
587 show_all = true; 598 result.config.show_all = true;
599 }
588 break; 600 break;
589 case 'u': 601 case 'u':
590 socket_timeout_state = STATE_UNKNOWN; 602 socket_timeout_state = STATE_UNKNOWN;
591 break; 603 break;
592 case 't': /* timeout */ 604 case 't': /* timeout */
593 socket_timeout = atoi(optarg); 605 socket_timeout = atoi(optarg);
594 if (socket_timeout <= 0) 606 if (socket_timeout <= 0) {
595 return ERROR; 607 result.errorcode = ERROR;
608 return result;
609 }
596 } 610 }
597 } 611 }
598 if (server_address == NULL) 612 if (result.config.server_address == NULL) {
599 usage4(_("You must provide a server address or host name")); 613 usage4(_("You must provide a server address or host name"));
614 }
600 615
601 if (vars_to_check == CHECK_NONE) 616 if (result.config.vars_to_check == CHECK_NONE) {
602 return ERROR; 617 result.errorcode = ERROR;
618 return result;
619 }
603 620
604 if (req_password == NULL) 621 if (result.config.req_password == NULL) {
605 req_password = strdup(_("None")); 622 result.config.req_password = strdup(_("None"));
623 }
606 624
607 return OK; 625 return result;
608} 626}
609 627
610void fetch_data(const char *address, int port, const char *sendb) { 628void fetch_data(const char *address, int port, const char *sendb) {
611 int result; 629 int result = process_tcp_request(address, port, sendb, recv_buffer, sizeof(recv_buffer));
612
613 result = process_tcp_request(address, port, sendb, recv_buffer, sizeof(recv_buffer));
614 630
615 if (result != STATE_OK) 631 if (result != STATE_OK) {
616 die(result, _("could not fetch information from server\n")); 632 die(result, _("could not fetch information from server\n"));
633 }
617 634
618 if (!strncmp(recv_buffer, "ERROR", 5)) 635 if (!strncmp(recv_buffer, "ERROR", 5)) {
619 die(STATE_UNKNOWN, "NSClient - %s\n", recv_buffer); 636 die(STATE_UNKNOWN, "NSClient - %s\n", recv_buffer);
637 }
620} 638}
621 639
622bool strtoularray(unsigned long *array, char *string, const char *delim) { 640bool strtoularray(unsigned long *array, char *string, const char *delim) {
623 /* split a <delim> delimited string into a long array */ 641 /* split a <delim> delimited string into a long array */
624 int idx = 0; 642 for (int idx = 0; idx < MAX_VALUE_LIST; idx++) {
625 char *t1;
626
627 for (idx = 0; idx < MAX_VALUE_LIST; idx++)
628 array[idx] = 0; 643 array[idx] = 0;
644 }
629 645
630 idx = 0; 646 int idx = 0;
631 for (t1 = strtok(string, delim); t1 != NULL; t1 = strtok(NULL, delim)) { 647 for (char *t1 = strtok(string, delim); t1 != NULL; t1 = strtok(NULL, delim)) {
632 if (is_numeric(t1) && idx < MAX_VALUE_LIST) { 648 if (is_numeric(t1) && idx < MAX_VALUE_LIST) {
633 array[idx] = strtoul(t1, NULL, 10); 649 array[idx] = strtoul(t1, NULL, 10);
634 idx++; 650 idx++;
635 } else 651 } else {
636 return false; 652 return false;
653 }
637 } 654 }
638 return true; 655 return true;
639} 656}
640 657
641void preparelist(char *string) { 658void preparelist(char *string) {
642 /* Replace all , with & which is the delimiter for the request */ 659 /* Replace all , with & which is the delimiter for the request */
643 int i; 660 for (int i = 0; (size_t)i < strlen(string); i++) {
644
645 for (i = 0; (size_t)i < strlen(string); i++)
646 if (string[i] == ',') { 661 if (string[i] == ',') {
647 string[i] = '&'; 662 string[i] = '&';
648 } 663 }
664 }
649} 665}
650 666
651void print_help(void) { 667void print_help(void) {
@@ -735,25 +751,31 @@ void print_help(void) {
735 printf(" %s\n", "\"%%.f %%%% paging file used.\""); 751 printf(" %s\n", "\"%%.f %%%% paging file used.\"");
736 printf(" %s\n", "INSTANCES ="); 752 printf(" %s\n", "INSTANCES =");
737 printf(" %s\n", _("Check any performance counter object of Windows NT/2000.")); 753 printf(" %s\n", _("Check any performance counter object of Windows NT/2000."));
738 printf(" %s\n", _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>")); 754 printf(" %s\n",
755 _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>"));
739 printf(" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),")); 756 printf(" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),"));
740 printf(" %s\n", _("if it is two words, it should be enclosed in quotes")); 757 printf(" %s\n", _("if it is two words, it should be enclosed in quotes"));
741 printf(" %s\n", _("The returned results will be a comma-separated list of instances on ")); 758 printf(" %s\n", _("The returned results will be a comma-separated list of instances on "));
742 printf(" %s\n", _(" the selected computer for that object.")); 759 printf(" %s\n", _(" the selected computer for that object."));
743 printf(" %s\n", _("The purpose of this is to be run from command line to determine what instances")); 760 printf(" %s\n",
744 printf(" %s\n", _(" are available for monitoring without having to log onto the Windows server")); 761 _("The purpose of this is to be run from command line to determine what instances"));
762 printf(" %s\n",
763 _(" are available for monitoring without having to log onto the Windows server"));
745 printf(" %s\n", _(" to run Perfmon directly.")); 764 printf(" %s\n", _(" to run Perfmon directly."));
746 printf(" %s\n", _("It can also be used in scripts that automatically create the monitoring service")); 765 printf(" %s\n",
766 _("It can also be used in scripts that automatically create the monitoring service"));
747 printf(" %s\n", _(" configuration files.")); 767 printf(" %s\n", _(" configuration files."));
748 printf(" %s\n", _("Some examples:")); 768 printf(" %s\n", _("Some examples:"));
749 printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process")); 769 printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process"));
750 770
751 printf("%s\n", _("Notes:")); 771 printf("%s\n", _("Notes:"));
752 printf(" %s\n", _("- The NSClient service should be running on the server to get any information")); 772 printf(" %s\n",
773 _("- The NSClient service should be running on the server to get any information"));
753 printf(" %s\n", "(http://nsclient.ready2run.nl)."); 774 printf(" %s\n", "(http://nsclient.ready2run.nl).");
754 printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds")); 775 printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds"));
755 printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error")); 776 printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error"));
756 printf(" %s\n", _("output when this happens contains \"Cannot map xxxxx to protocol number\".")); 777 printf(" %s\n",
778 _("output when this happens contains \"Cannot map xxxxx to protocol number\"."));
757 printf(" %s\n", _("One fix for this is to change the port to something else on check_nt ")); 779 printf(" %s\n", _("One fix for this is to change the port to something else on check_nt "));
758 printf(" %s\n", _("and on the client service it\'s connecting to.")); 780 printf(" %s\n", _("and on the client service it\'s connecting to."));
759 781
diff --git a/plugins/check_nt.d/config.h b/plugins/check_nt.d/config.h
new file mode 100644
index 00000000..431889cb
--- /dev/null
+++ b/plugins/check_nt.d/config.h
@@ -0,0 +1,53 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PORT = 1248,
8};
9
10enum checkvars {
11 CHECK_NONE,
12 CHECK_CLIENTVERSION,
13 CHECK_CPULOAD,
14 CHECK_UPTIME,
15 CHECK_USEDDISKSPACE,
16 CHECK_SERVICESTATE,
17 CHECK_PROCSTATE,
18 CHECK_MEMUSE,
19 CHECK_COUNTER,
20 CHECK_FILEAGE,
21 CHECK_INSTANCES
22};
23
24typedef struct {
25 char *server_address;
26 int server_port;
27 char *req_password;
28 enum checkvars vars_to_check;
29 bool show_all;
30 char *value_list;
31 bool check_warning_value;
32 unsigned long warning_value;
33 bool check_critical_value;
34 unsigned long critical_value;
35} check_nt_config;
36
37check_nt_config check_nt_config_init() {
38 check_nt_config tmp = {
39 .server_address = NULL,
40 .server_port = PORT,
41 .req_password = NULL,
42
43 .vars_to_check = CHECK_NONE,
44 .show_all = false,
45 .value_list = NULL,
46
47 .check_warning_value = false,
48 .warning_value = 0,
49 .check_critical_value = false,
50 .critical_value = 0,
51 };
52 return tmp;
53}
diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c
index d33f8786..b22cc3c1 100644
--- a/plugins/check_ntp.c
+++ b/plugins/check_ntp.c
@@ -1,34 +1,34 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ntp plugin 3 * Monitoring check_ntp 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-2024 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 plugin 11 * This file contains the check_ntp plugin
12* 12 *
13* This plugin to check ntp servers independent of any commandline 13 * This plugin to check ntp servers independent of any commandline
14* programs or external libraries. 14 * programs or external libraries.
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_ntp"; 33const char *progname = "check_ntp";
34const char *copyright = "2006-2024"; 34const char *copyright = "2006-2024";
@@ -38,24 +38,24 @@ const char *email = "devel@monitoring-plugins.org";
38#include "netutils.h" 38#include "netutils.h"
39#include "utils.h" 39#include "utils.h"
40 40
41static char *server_address=NULL; 41static char *server_address = NULL;
42static int verbose=0; 42static int verbose = 0;
43static bool do_offset = false; 43static bool do_offset = false;
44static char *owarn="60"; 44static char *owarn = "60";
45static char *ocrit="120"; 45static char *ocrit = "120";
46static bool do_jitter = false; 46static bool do_jitter = false;
47static char *jwarn="5000"; 47static char *jwarn = "5000";
48static char *jcrit="10000"; 48static char *jcrit = "10000";
49 49
50static int process_arguments (int /*argc*/, char ** /*argv*/); 50static int process_arguments(int /*argc*/, char ** /*argv*/);
51static thresholds *offset_thresholds = NULL; 51static thresholds *offset_thresholds = NULL;
52static thresholds *jitter_thresholds = NULL; 52static thresholds *jitter_thresholds = NULL;
53static void print_help (void); 53static void print_help(void);
54void print_usage (void); 54void print_usage(void);
55 55
56/* number of times to perform each request to get a good average. */ 56/* number of times to perform each request to get a good average. */
57#ifndef AVG_NUM 57#ifndef AVG_NUM
58#define AVG_NUM 4 58# define AVG_NUM 4
59#endif 59#endif
60 60
61/* max size of control message data */ 61/* max size of control message data */
@@ -63,17 +63,17 @@ void print_usage (void);
63 63
64/* this structure holds everything in an ntp request/response as per rfc1305 */ 64/* this structure holds everything in an ntp request/response as per rfc1305 */
65typedef struct { 65typedef struct {
66 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 66 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
67 uint8_t stratum; /* clock stratum */ 67 uint8_t stratum; /* clock stratum */
68 int8_t poll; /* polling interval */ 68 int8_t poll; /* polling interval */
69 int8_t precision; /* precision of the local clock */ 69 int8_t precision; /* precision of the local clock */
70 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */ 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 */ 71 uint32_t rtdisp; /* like above, but for max err to primary src */
72 uint32_t refid; /* ref clock identifier */ 72 uint32_t refid; /* ref clock identifier */
73 uint64_t refts; /* reference timestamp. local time local clock */ 73 uint64_t refts; /* reference timestamp. local time local clock */
74 uint64_t origts; /* time at which request departed client */ 74 uint64_t origts; /* time at which request departed client */
75 uint64_t rxts; /* time at which request arrived at server */ 75 uint64_t rxts; /* time at which request arrived at server */
76 uint64_t txts; /* time at which request departed server */ 76 uint64_t txts; /* time at which request departed server */
77} ntp_message; 77} ntp_message;
78 78
79/* this structure holds data about results from querying offset from a peer */ 79/* this structure holds data about results from querying offset from a peer */
@@ -84,20 +84,20 @@ typedef struct {
84 double rtdelay; /* converted from the ntp_message */ 84 double rtdelay; /* converted from the ntp_message */
85 double rtdisp; /* converted from the ntp_message */ 85 double rtdisp; /* converted from the ntp_message */
86 double offset[AVG_NUM]; /* offsets from each response */ 86 double offset[AVG_NUM]; /* offsets from each response */
87 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 87 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
88} ntp_server_results; 88} ntp_server_results;
89 89
90/* this structure holds everything in an ntp control message as per rfc1305 */ 90/* this structure holds everything in an ntp control message as per rfc1305 */
91typedef struct { 91typedef struct {
92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
93 uint8_t op; /* R,E,M bits and Opcode */ 93 uint8_t op; /* R,E,M bits and Opcode */
94 uint16_t seq; /* Packet sequence */ 94 uint16_t seq; /* Packet sequence */
95 uint16_t status; /* Clock status */ 95 uint16_t status; /* Clock status */
96 uint16_t assoc; /* Association */ 96 uint16_t assoc; /* Association */
97 uint16_t offset; /* Similar to TCP sequence # */ 97 uint16_t offset; /* Similar to TCP sequence # */
98 uint16_t count; /* # bytes of data */ 98 uint16_t count; /* # bytes of data */
99 char data[MAX_CM_SIZE]; /* ASCII data of the request */ 99 char data[MAX_CM_SIZE]; /* ASCII data of the request */
100 /* NB: not necessarily NULL terminated! */ 100 /* NB: not necessarily NULL terminated! */
101} ntp_control_message; 101} ntp_control_message;
102 102
103/* this is an association/status-word pair found in control packet responses */ 103/* this is an association/status-word pair found in control packet responses */
@@ -108,38 +108,50 @@ typedef struct {
108 108
109/* bits 1,2 are the leap indicator */ 109/* bits 1,2 are the leap indicator */
110#define LI_MASK 0xc0 110#define LI_MASK 0xc0
111#define LI(x) ((x&LI_MASK)>>6) 111#define LI(x) ((x & LI_MASK) >> 6)
112#define LI_SET(x,y) do{ x |= ((y<<6)&LI_MASK); }while(0) 112#define LI_SET(x, y) \
113 do { \
114 x |= ((y << 6) & LI_MASK); \
115 } while (0)
113/* and these are the values of the leap indicator */ 116/* and these are the values of the leap indicator */
114#define LI_NOWARNING 0x00 117#define LI_NOWARNING 0x00
115#define LI_EXTRASEC 0x01 118#define LI_EXTRASEC 0x01
116#define LI_MISSINGSEC 0x02 119#define LI_MISSINGSEC 0x02
117#define LI_ALARM 0x03 120#define LI_ALARM 0x03
118/* bits 3,4,5 are the ntp version */ 121/* bits 3,4,5 are the ntp version */
119#define VN_MASK 0x38 122#define VN_MASK 0x38
120#define VN(x) ((x&VN_MASK)>>3) 123#define VN(x) ((x & VN_MASK) >> 3)
121#define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) 124#define VN_SET(x, y) \
125 do { \
126 x |= ((y << 3) & VN_MASK); \
127 } while (0)
122#define VN_RESERVED 0x02 128#define VN_RESERVED 0x02
123/* bits 6,7,8 are the ntp mode */ 129/* bits 6,7,8 are the ntp mode */
124#define MODE_MASK 0x07 130#define MODE_MASK 0x07
125#define MODE(x) (x&MODE_MASK) 131#define MODE(x) (x & MODE_MASK)
126#define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) 132#define MODE_SET(x, y) \
133 do { \
134 x |= (y & MODE_MASK); \
135 } while (0)
127/* here are some values */ 136/* here are some values */
128#define MODE_CLIENT 0x03 137#define MODE_CLIENT 0x03
129#define MODE_CONTROLMSG 0x06 138#define MODE_CONTROLMSG 0x06
130/* In control message, bits 8-10 are R,E,M bits */ 139/* In control message, bits 8-10 are R,E,M bits */
131#define REM_MASK 0xe0 140#define REM_MASK 0xe0
132#define REM_RESP 0x80 141#define REM_RESP 0x80
133#define REM_ERROR 0x40 142#define REM_ERROR 0x40
134#define REM_MORE 0x20 143#define REM_MORE 0x20
135/* In control message, bits 11 - 15 are opcode */ 144/* In control message, bits 11 - 15 are opcode */
136#define OP_MASK 0x1f 145#define OP_MASK 0x1f
137#define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) 146#define OP_SET(x, y) \
147 do { \
148 x |= (y & OP_MASK); \
149 } while (0)
138#define OP_READSTAT 0x01 150#define OP_READSTAT 0x01
139#define OP_READVAR 0x02 151#define OP_READVAR 0x02
140/* In peer status bytes, bits 6,7,8 determine clock selection status */ 152/* In peer status bytes, bits 6,7,8 determine clock selection status */
141#define PEER_SEL(x) ((ntohs(x)>>8)&0x07) 153#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07)
142#define PEER_INCLUDED 0x04 154#define PEER_INCLUDED 0x04
143#define PEER_SYNCSOURCE 0x06 155#define PEER_SYNCSOURCE 0x06
144 156
145/** 157/**
@@ -153,82 +165,92 @@ typedef struct {
153 165
154/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point" 166/* 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 */ 167 number. note that these can be used as lvalues too */
156#define L16(x) (((uint16_t*)&x)[0]) 168#define L16(x) (((uint16_t *)&x)[0])
157#define R16(x) (((uint16_t*)&x)[1]) 169#define R16(x) (((uint16_t *)&x)[1])
158/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point" 170/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
159 number. these too can be used as lvalues */ 171 number. these too can be used as lvalues */
160#define L32(x) (((uint32_t*)&x)[0]) 172#define L32(x) (((uint32_t *)&x)[0])
161#define R32(x) (((uint32_t*)&x)[1]) 173#define R32(x) (((uint32_t *)&x)[1])
162 174
163/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */ 175/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */
164#define EPOCHDIFF 0x83aa7e80UL 176#define EPOCHDIFF 0x83aa7e80UL
165 177
166/* extract a 32-bit ntp fixed point number into a double */ 178/* extract a 32-bit ntp fixed point number into a double */
167#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x))/65536.0) 179#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0)
168 180
169/* likewise for a 64-bit ntp fp number */ 181/* likewise for a 64-bit ntp fp number */
170#define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\ 182#define NTP64asDOUBLE(n) \
171 (ntohl(L32(n))-EPOCHDIFF) + \ 183 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \
172 (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\ 184 (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \
173 0) 185 : 0)
174 186
175/* convert a struct timeval to a double */ 187/* convert a struct timeval to a double */
176#define TVasDOUBLE(x) (double)(x.tv_sec+(0.000001*x.tv_usec)) 188#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec))
177 189
178/* convert an ntp 64-bit fp number to a struct timeval */ 190/* convert an ntp 64-bit fp number to a struct timeval */
179#define NTP64toTV(n,t) \ 191#define NTP64toTV(n, t) \
180 do{ if(!n) t.tv_sec = t.tv_usec = 0; \ 192 do { \
181 else { \ 193 if (!n) \
182 t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \ 194 t.tv_sec = t.tv_usec = 0; \
183 t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \ 195 else { \
184 } \ 196 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \
185 }while(0) 197 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \
198 } \
199 } while (0)
186 200
187/* convert a struct timeval to an ntp 64-bit fp number */ 201/* convert a struct timeval to an ntp 64-bit fp number */
188#define TVtoNTP64(t,n) \ 202#define TVtoNTP64(t, n) \
189 do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \ 203 do { \
190 else { \ 204 if (!t.tv_usec && !t.tv_sec) \
191 L32(n)=htonl(t.tv_sec + EPOCHDIFF); \ 205 n = 0x0UL; \
192 R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \ 206 else { \
193 } \ 207 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \
194 } while(0) 208 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \
209 } \
210 } while (0)
195 211
196/* NTP control message header is 12 bytes, plus any data in the data 212/* 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. 213 * field, plus null padding to the nearest 32-bit boundary per rfc.
198 */ 214 */
199#define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((ntohs(m.count)%4)?4-(ntohs(m.count)%4):0)) 215#define SIZEOF_NTPCM(m) \
216 (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0))
200 217
201/* finally, a little helper or two for debugging: */ 218/* finally, a little helper or two for debugging: */
202#define DBG(x) do{if(verbose>1){ x; }}while(0); 219#define DBG(x) \
203#define PRINTSOCKADDR(x) \ 220 do { \
204 do{ \ 221 if (verbose > 1) { \
205 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ 222 x; \
206 }while(0); 223 } \
224 } while (0);
225#define PRINTSOCKADDR(x) \
226 do { \
227 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
228 } while (0);
207 229
208/* calculate the offset of the local clock */ 230/* calculate the offset of the local clock */
209static inline double calc_offset(const ntp_message *m, const struct timeval *t){ 231static inline double calc_offset(const ntp_message *m, const struct timeval *t) {
210 double client_tx, peer_rx, peer_tx, client_rx; 232 double client_tx, peer_rx, peer_tx, client_rx;
211 client_tx = NTP64asDOUBLE(m->origts); 233 client_tx = NTP64asDOUBLE(m->origts);
212 peer_rx = NTP64asDOUBLE(m->rxts); 234 peer_rx = NTP64asDOUBLE(m->rxts);
213 peer_tx = NTP64asDOUBLE(m->txts); 235 peer_tx = NTP64asDOUBLE(m->txts);
214 client_rx=TVasDOUBLE((*t)); 236 client_rx = TVasDOUBLE((*t));
215 return (.5*((peer_tx-client_rx)+(peer_rx-client_tx))); 237 return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx)));
216} 238}
217 239
218/* print out a ntp packet in human readable/debuggable format */ 240/* print out a ntp packet in human readable/debuggable format */
219void print_ntp_message(const ntp_message *p){ 241void print_ntp_message(const ntp_message *p) {
220 struct timeval ref, orig, rx, tx; 242 struct timeval ref, orig, rx, tx;
221 243
222 NTP64toTV(p->refts,ref); 244 NTP64toTV(p->refts, ref);
223 NTP64toTV(p->origts,orig); 245 NTP64toTV(p->origts, orig);
224 NTP64toTV(p->rxts,rx); 246 NTP64toTV(p->rxts, rx);
225 NTP64toTV(p->txts,tx); 247 NTP64toTV(p->txts, tx);
226 248
227 printf("packet contents:\n"); 249 printf("packet contents:\n");
228 printf("\tflags: 0x%.2x\n", p->flags); 250 printf("\tflags: 0x%.2x\n", p->flags);
229 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); 251 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); 252 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); 253 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK);
232 printf("\tstratum = %d\n", p->stratum); 254 printf("\tstratum = %d\n", p->stratum);
233 printf("\tpoll = %g\n", pow(2, p->poll)); 255 printf("\tpoll = %g\n", pow(2, p->poll));
234 printf("\tprecision = %g\n", pow(2, p->precision)); 256 printf("\tprecision = %g\n", pow(2, p->precision));
@@ -241,32 +263,31 @@ void print_ntp_message(const ntp_message *p){
241 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); 263 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts));
242} 264}
243 265
244void print_ntp_control_message(const ntp_control_message *p){ 266void print_ntp_control_message(const ntp_control_message *p) {
245 int i=0, numpeers=0; 267 int i = 0, numpeers = 0;
246 const ntp_assoc_status_pair *peer=NULL; 268 const ntp_assoc_status_pair *peer = NULL;
247 269
248 printf("control packet contents:\n"); 270 printf("control packet contents:\n");
249 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); 271 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); 272 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); 273 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); 274 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); 275 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); 276 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); 277 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); 278 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)); 279 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)); 280 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)); 281 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)); 282 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)); 283 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count));
262 numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair)); 284 numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair));
263 if(p->op&REM_RESP && p->op&OP_READSTAT){ 285 if (p->op & REM_RESP && p->op & OP_READSTAT) {
264 peer=(ntp_assoc_status_pair*)p->data; 286 peer = (ntp_assoc_status_pair *)p->data;
265 for(i=0;i<numpeers;i++){ 287 for (i = 0; i < numpeers; i++) {
266 printf("\tpeer id %.2x status %.2x", 288 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
267 ntohs(peer[i].assoc), ntohs(peer[i].status)); 289 if (PEER_SEL(peer[i].status) >= PEER_INCLUDED) {
268 if (PEER_SEL(peer[i].status) >= PEER_INCLUDED){ 290 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
269 if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){
270 printf(" <-- current sync source"); 291 printf(" <-- current sync source");
271 } else { 292 } else {
272 printf(" <-- current sync candidate"); 293 printf(" <-- current sync candidate");
@@ -277,41 +298,45 @@ void print_ntp_control_message(const ntp_control_message *p){
277 } 298 }
278} 299}
279 300
280void setup_request(ntp_message *p){ 301void setup_request(ntp_message *p) {
281 struct timeval t; 302 struct timeval t;
282 303
283 memset(p, 0, sizeof(ntp_message)); 304 memset(p, 0, sizeof(ntp_message));
284 LI_SET(p->flags, LI_ALARM); 305 LI_SET(p->flags, LI_ALARM);
285 VN_SET(p->flags, 4); 306 VN_SET(p->flags, 4);
286 MODE_SET(p->flags, MODE_CLIENT); 307 MODE_SET(p->flags, MODE_CLIENT);
287 p->poll=4; 308 p->poll = 4;
288 p->precision=(int8_t)0xfa; 309 p->precision = (int8_t)0xfa;
289 L16(p->rtdelay)=htons(1); 310 L16(p->rtdelay) = htons(1);
290 L16(p->rtdisp)=htons(1); 311 L16(p->rtdisp) = htons(1);
291 312
292 gettimeofday(&t, NULL); 313 gettimeofday(&t, NULL);
293 TVtoNTP64(t,p->txts); 314 TVtoNTP64(t, p->txts);
294} 315}
295 316
296/* select the "best" server from a list of servers, and return its index. 317/* 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 318 * this is done by filtering servers based on stratum, dispersion, and
298 * finally round-trip delay. */ 319 * finally round-trip delay. */
299int best_offset_server(const ntp_server_results *slist, int nservers){ 320int best_offset_server(const ntp_server_results *slist, int nservers) {
300 int cserver=0, best_server=-1; 321 int cserver = 0, best_server = -1;
301 322
302 /* for each server */ 323 /* for each server */
303 for(cserver=0; cserver<nservers; cserver++){ 324 for (cserver = 0; cserver < nservers; cserver++) {
304 /* We don't want any servers that fails these tests */ 325 /* We don't want any servers that fails these tests */
305 /* Sort out servers that didn't respond or responede with a 0 stratum; 326 /* 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 327 * stratum 0 is for reference clocks so no NTP server should ever report
307 * a stratum 0 */ 328 * a stratum 0 */
308 if ( slist[cserver].stratum == 0){ 329 if (slist[cserver].stratum == 0) {
309 if (verbose) printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); 330 if (verbose) {
331 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
332 }
310 continue; 333 continue;
311 } 334 }
312 /* Sort out servers with error flags */ 335 /* Sort out servers with error flags */
313 if ( LI(slist[cserver].flags) == LI_ALARM ){ 336 if (LI(slist[cserver].flags) == LI_ALARM) {
314 if (verbose) printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); 337 if (verbose) {
338 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
339 }
315 continue; 340 continue;
316 } 341 }
317 342
@@ -325,13 +350,13 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
325 /* compare the server to the best one we've seen so far */ 350 /* compare the server to the best one we've seen so far */
326 /* does it have an equal or better stratum? */ 351 /* does it have an equal or better stratum? */
327 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); 352 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server));
328 if(slist[cserver].stratum <= slist[best_server].stratum){ 353 if (slist[cserver].stratum <= slist[best_server].stratum) {
329 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); 354 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server));
330 /* does it have an equal or better dispersion? */ 355 /* does it have an equal or better dispersion? */
331 if(slist[cserver].rtdisp <= slist[best_server].rtdisp){ 356 if (slist[cserver].rtdisp <= slist[best_server].rtdisp) {
332 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); 357 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server));
333 /* does it have a better rtdelay? */ 358 /* does it have a better rtdelay? */
334 if(slist[cserver].rtdelay < slist[best_server].rtdelay){ 359 if (slist[cserver].rtdelay < slist[best_server].rtdelay) {
335 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); 360 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server));
336 best_server = cserver; 361 best_server = cserver;
337 DBG(printf("peer %d is now our best candidate\n", best_server)); 362 DBG(printf("peer %d is now our best candidate\n", best_server));
@@ -340,7 +365,7 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
340 } 365 }
341 } 366 }
342 367
343 if(best_server >= 0) { 368 if (best_server >= 0) {
344 DBG(printf("best server selected: peer %d\n", best_server)); 369 DBG(printf("best server selected: peer %d\n", best_server));
345 return best_server; 370 return best_server;
346 } else { 371 } else {
@@ -354,16 +379,16 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
354 * we don't waste time sitting around waiting for single packets. 379 * we don't waste time sitting around waiting for single packets.
355 * - we also "manually" handle resolving host names and connecting, because 380 * - 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 :( */ 381 * we have to do it in a way that our lazy macros don't handle currently :( */
357double offset_request(const char *host, int *status){ 382double offset_request(const char *host, int *status) {
358 int i=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0; 383 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; 384 int servers_completed = 0, one_read = 0, servers_readable = 0, best_index = -1;
360 time_t now_time=0, start_ts=0; 385 time_t now_time = 0, start_ts = 0;
361 ntp_message *req=NULL; 386 ntp_message *req = NULL;
362 double avg_offset=0.; 387 double avg_offset = 0.;
363 struct timeval recv_time; 388 struct timeval recv_time;
364 struct addrinfo *ai=NULL, *ai_tmp=NULL, hints; 389 struct addrinfo *ai = NULL, *ai_tmp = NULL, hints;
365 struct pollfd *ufds=NULL; 390 struct pollfd *ufds = NULL;
366 ntp_server_results *servers=NULL; 391 ntp_server_results *servers = NULL;
367 392
368 /* setup hints to only return results from getaddrinfo that we'd like */ 393 /* setup hints to only return results from getaddrinfo that we'd like */
369 memset(&hints, 0, sizeof(struct addrinfo)); 394 memset(&hints, 0, sizeof(struct addrinfo));
@@ -373,97 +398,112 @@ double offset_request(const char *host, int *status){
373 398
374 /* fill in ai with the list of hosts resolved by the host name */ 399 /* fill in ai with the list of hosts resolved by the host name */
375 ga_result = getaddrinfo(host, "123", &hints, &ai); 400 ga_result = getaddrinfo(host, "123", &hints, &ai);
376 if(ga_result!=0){ 401 if (ga_result != 0) {
377 die(STATE_UNKNOWN, "error getting address for %s: %s\n", 402 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
378 host, gai_strerror(ga_result));
379 } 403 }
380 404
381 /* count the number of returned hosts, and allocate stuff accordingly */ 405 /* 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++; } 406 for (ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
383 req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts); 407 num_hosts++;
384 if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array"); 408 }
385 socklist=(int*)malloc(sizeof(int)*num_hosts); 409 req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts);
386 if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); 410 if (req == NULL) {
387 ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts); 411 die(STATE_UNKNOWN, "can not allocate ntp message array");
388 if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); 412 }
389 servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts); 413 socklist = (int *)malloc(sizeof(int) * num_hosts);
390 if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array"); 414 if (socklist == NULL) {
391 memset(servers, 0, sizeof(ntp_server_results)*num_hosts); 415 die(STATE_UNKNOWN, "can not allocate socket array");
416 }
417 ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts);
418 if (ufds == NULL) {
419 die(STATE_UNKNOWN, "can not allocate socket array");
420 }
421 servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts);
422 if (servers == NULL) {
423 die(STATE_UNKNOWN, "can not allocate server array");
424 }
425 memset(servers, 0, sizeof(ntp_server_results) * num_hosts);
392 DBG(printf("Found %d peers to check\n", num_hosts)); 426 DBG(printf("Found %d peers to check\n", num_hosts));
393 427
394 /* setup each socket for writing, and the corresponding struct pollfd */ 428 /* setup each socket for writing, and the corresponding struct pollfd */
395 ai_tmp=ai; 429 ai_tmp = ai;
396 for(i=0;ai_tmp;i++){ 430 for (i = 0; ai_tmp; i++) {
397 socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); 431 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
398 if(socklist[i] == -1) { 432 if (socklist[i] == -1) {
399 perror(NULL); 433 perror(NULL);
400 die(STATE_UNKNOWN, "can not create new socket"); 434 die(STATE_UNKNOWN, "can not create new socket");
401 } 435 }
402 if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){ 436 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 437 /* 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 438 answering in time. This also would break for dual ipv4/6 stacked
405 ntp servers when the client only supports on of them. 439 ntp servers when the client only supports on of them.
406 */ 440 */
407 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); 441 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno)));
408 } else { 442 } else {
409 ufds[i].fd=socklist[i]; 443 ufds[i].fd = socklist[i];
410 ufds[i].events=POLLIN; 444 ufds[i].events = POLLIN;
411 ufds[i].revents=0; 445 ufds[i].revents = 0;
412 } 446 }
413 ai_tmp = ai_tmp->ai_next; 447 ai_tmp = ai_tmp->ai_next;
414 } 448 }
415 449
416 /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds 450 /* 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. */ 451 * have passed in order to ensure post-processing and jitter time. */
418 now_time=start_ts=time(NULL); 452 now_time = start_ts = time(NULL);
419 while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){ 453 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) {
420 /* loop through each server and find each one which hasn't 454 /* loop through each server and find each one which hasn't
421 * been touched in the past second or so and is still lacking 455 * been touched in the past second or so and is still lacking
422 * some responses. for each of these servers, send a new request, 456 * some responses. for each of these servers, send a new request,
423 * and update the "waiting" timestamp with the current time. */ 457 * and update the "waiting" timestamp with the current time. */
424 now_time=time(NULL); 458 now_time = time(NULL);
425 459
426 for(i=0; i<num_hosts; i++){ 460 for (i = 0; i < num_hosts; i++) {
427 if(servers[i].waiting<now_time && servers[i].num_responses<AVG_NUM){ 461 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) {
428 if(verbose && servers[i].waiting != 0) printf("re-"); 462 if (verbose && servers[i].waiting != 0) {
429 if(verbose) printf("sending request to peer %d\n", i); 463 printf("re-");
464 }
465 if (verbose) {
466 printf("sending request to peer %d\n", i);
467 }
430 setup_request(&req[i]); 468 setup_request(&req[i]);
431 write(socklist[i], &req[i], sizeof(ntp_message)); 469 write(socklist[i], &req[i], sizeof(ntp_message));
432 servers[i].waiting=now_time; 470 servers[i].waiting = now_time;
433 break; 471 break;
434 } 472 }
435 } 473 }
436 474
437 /* quickly poll for any sockets with pending data */ 475 /* quickly poll for any sockets with pending data */
438 servers_readable=poll(ufds, num_hosts, 100); 476 servers_readable = poll(ufds, num_hosts, 100);
439 if(servers_readable==-1){ 477 if (servers_readable == -1) {
440 perror("polling ntp sockets"); 478 perror("polling ntp sockets");
441 die(STATE_UNKNOWN, "communication errors"); 479 die(STATE_UNKNOWN, "communication errors");
442 } 480 }
443 481
444 /* read from any sockets with pending data */ 482 /* read from any sockets with pending data */
445 for(i=0; servers_readable && i<num_hosts; i++){ 483 for (i = 0; servers_readable && i < num_hosts; i++) {
446 if(ufds[i].revents&POLLIN && servers[i].num_responses < AVG_NUM){ 484 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) {
447 if(verbose) { 485 if (verbose) {
448 printf("response from peer %d: ", i); 486 printf("response from peer %d: ", i);
449 } 487 }
450 488
451 read(ufds[i].fd, &req[i], sizeof(ntp_message)); 489 read(ufds[i].fd, &req[i], sizeof(ntp_message));
452 gettimeofday(&recv_time, NULL); 490 gettimeofday(&recv_time, NULL);
453 DBG(print_ntp_message(&req[i])); 491 DBG(print_ntp_message(&req[i]));
454 respnum=servers[i].num_responses++; 492 respnum = servers[i].num_responses++;
455 servers[i].offset[respnum]=calc_offset(&req[i], &recv_time); 493 servers[i].offset[respnum] = calc_offset(&req[i], &recv_time);
456 if(verbose) { 494 if (verbose) {
457 printf("offset %.10g\n", servers[i].offset[respnum]); 495 printf("offset %.10g\n", servers[i].offset[respnum]);
458 } 496 }
459 servers[i].stratum=req[i].stratum; 497 servers[i].stratum = req[i].stratum;
460 servers[i].rtdisp=NTP32asDOUBLE(req[i].rtdisp); 498 servers[i].rtdisp = NTP32asDOUBLE(req[i].rtdisp);
461 servers[i].rtdelay=NTP32asDOUBLE(req[i].rtdelay); 499 servers[i].rtdelay = NTP32asDOUBLE(req[i].rtdelay);
462 servers[i].waiting=0; 500 servers[i].waiting = 0;
463 servers[i].flags=req[i].flags; 501 servers[i].flags = req[i].flags;
464 servers_readable--; 502 servers_readable--;
465 one_read = 1; 503 one_read = 1;
466 if(servers[i].num_responses==AVG_NUM) servers_completed++; 504 if (servers[i].num_responses == AVG_NUM) {
505 servers_completed++;
506 }
467 } 507 }
468 } 508 }
469 /* lather, rinse, repeat. */ 509 /* lather, rinse, repeat. */
@@ -474,15 +514,15 @@ double offset_request(const char *host, int *status){
474 } 514 }
475 515
476 /* now, pick the best server from the list */ 516 /* now, pick the best server from the list */
477 best_index=best_offset_server(servers, num_hosts); 517 best_index = best_offset_server(servers, num_hosts);
478 if(best_index < 0){ 518 if (best_index < 0) {
479 *status=STATE_UNKNOWN; 519 *status = STATE_UNKNOWN;
480 } else { 520 } else {
481 /* finally, calculate the average offset */ 521 /* finally, calculate the average offset */
482 for(i=0; i<servers[best_index].num_responses;i++){ 522 for (i = 0; i < servers[best_index].num_responses; i++) {
483 avg_offset+=servers[best_index].offset[i]; 523 avg_offset += servers[best_index].offset[i];
484 } 524 }
485 avg_offset/=servers[best_index].num_responses; 525 avg_offset /= servers[best_index].num_responses;
486 } 526 }
487 527
488 /* cleanup */ 528 /* cleanup */
@@ -496,12 +536,13 @@ double offset_request(const char *host, int *status){
496 free(req); 536 free(req);
497 freeaddrinfo(ai); 537 freeaddrinfo(ai);
498 538
499 if(verbose) printf("overall average offset: %.10g\n", avg_offset); 539 if (verbose) {
540 printf("overall average offset: %.10g\n", avg_offset);
541 }
500 return avg_offset; 542 return avg_offset;
501} 543}
502 544
503void 545void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) {
504setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
505 memset(p, 0, sizeof(ntp_control_message)); 546 memset(p, 0, sizeof(ntp_control_message));
506 LI_SET(p->flags, LI_NOWARNING); 547 LI_SET(p->flags, LI_NOWARNING);
507 VN_SET(p->flags, VN_RESERVED); 548 VN_SET(p->flags, VN_RESERVED);
@@ -512,16 +553,16 @@ setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
512} 553}
513 554
514/* XXX handle responses with the error bit set */ 555/* XXX handle responses with the error bit set */
515double jitter_request(int *status){ 556double jitter_request(int *status) {
516 int conn=-1, i, npeers=0, num_candidates=0; 557 int conn = -1, i, npeers = 0, num_candidates = 0;
517 bool syncsource_found = false; 558 bool syncsource_found = false;
518 int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0; 559 int run = 0, min_peer_sel = PEER_INCLUDED, num_selected = 0, num_valid = 0;
519 int peers_size=0, peer_offset=0; 560 int peers_size = 0, peer_offset = 0;
520 ntp_assoc_status_pair *peers=NULL; 561 ntp_assoc_status_pair *peers = NULL;
521 ntp_control_message req; 562 ntp_control_message req;
522 const char *getvar = "jitter"; 563 const char *getvar = "jitter";
523 double rval = 0.0, jitter = -1.0; 564 double rval = 0.0, jitter = -1.0;
524 char *startofvalue=NULL, *nptr=NULL; 565 char *startofvalue = NULL, *nptr = NULL;
525 void *tmp; 566 void *tmp;
526 567
527 /* Long-winded explanation: 568 /* Long-winded explanation:
@@ -542,54 +583,62 @@ double jitter_request(int *status){
542 583
543 /* keep sending requests until the server stops setting the 584 /* keep sending requests until the server stops setting the
544 * REM_MORE bit, though usually this is only 1 packet. */ 585 * REM_MORE bit, though usually this is only 1 packet. */
545 do{ 586 do {
546 setup_control_request(&req, OP_READSTAT, 1); 587 setup_control_request(&req, OP_READSTAT, 1);
547 DBG(printf("sending READSTAT request")); 588 DBG(printf("sending READSTAT request"));
548 write(conn, &req, SIZEOF_NTPCM(req)); 589 write(conn, &req, SIZEOF_NTPCM(req));
549 DBG(print_ntp_control_message(&req)); 590 DBG(print_ntp_control_message(&req));
550 /* Attempt to read the largest size packet possible */ 591 /* Attempt to read the largest size packet possible */
551 req.count=htons(MAX_CM_SIZE); 592 req.count = htons(MAX_CM_SIZE);
552 DBG(printf("receiving READSTAT response")) 593 DBG(printf("receiving READSTAT response"))
553 read(conn, &req, SIZEOF_NTPCM(req)); 594 read(conn, &req, SIZEOF_NTPCM(req));
554 DBG(print_ntp_control_message(&req)); 595 DBG(print_ntp_control_message(&req));
555 /* Each peer identifier is 4 bytes in the data section, which 596 /* Each peer identifier is 4 bytes in the data section, which
556 * we represent as a ntp_assoc_status_pair datatype. 597 * we represent as a ntp_assoc_status_pair datatype.
557 */ 598 */
558 peers_size+=ntohs(req.count); 599 peers_size += ntohs(req.count);
559 if((tmp=realloc(peers, peers_size)) == NULL) 600 if ((tmp = realloc(peers, peers_size)) == NULL) {
560 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 601 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
561 peers=tmp; 602 }
562 memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count)); 603 peers = tmp;
563 npeers=peers_size/sizeof(ntp_assoc_status_pair); 604 memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count));
564 peer_offset+=ntohs(req.count); 605 npeers = peers_size / sizeof(ntp_assoc_status_pair);
565 } while(req.op&REM_MORE); 606 peer_offset += ntohs(req.count);
607 } while (req.op & REM_MORE);
566 608
567 /* first, let's find out if we have a sync source, or if there are 609 /* 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 610 * at least some candidates. in the case of the latter we'll issue
569 * a warning but go ahead with the check on them. */ 611 * a warning but go ahead with the check on them. */
570 for (i = 0; i < npeers; i++){ 612 for (i = 0; i < npeers; i++) {
571 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){ 613 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
572 num_candidates++; 614 num_candidates++;
573 if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){ 615 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
574 syncsource_found = true; 616 syncsource_found = true;
575 min_peer_sel=PEER_SYNCSOURCE; 617 min_peer_sel = PEER_SYNCSOURCE;
576 } 618 }
577 } 619 }
578 } 620 }
579 if(verbose) printf("%d candidate peers available\n", num_candidates); 621 if (verbose) {
580 if(verbose && syncsource_found) printf("synchronization source found\n"); 622 printf("%d candidate peers available\n", num_candidates);
581 if(! syncsource_found){ 623 }
624 if (verbose && syncsource_found) {
625 printf("synchronization source found\n");
626 }
627 if (!syncsource_found) {
582 *status = STATE_UNKNOWN; 628 *status = STATE_UNKNOWN;
583 if(verbose) printf("warning: no synchronization source found\n"); 629 if (verbose) {
630 printf("warning: no synchronization source found\n");
631 }
584 } 632 }
585 633
586 634 for (run = 0; run < AVG_NUM; run++) {
587 for (run=0; run<AVG_NUM; run++){ 635 if (verbose) {
588 if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM); 636 printf("jitter run %d of %d\n", run + 1, AVG_NUM);
589 for (i = 0; i < npeers; i++){ 637 }
638 for (i = 0; i < npeers; i++) {
590 /* Only query this server if it is the current sync source */ 639 /* Only query this server if it is the current sync source */
591 if (PEER_SEL(peers[i].status) >= min_peer_sel){ 640 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
592 char jitter_data[MAX_CM_SIZE+1]; 641 char jitter_data[MAX_CM_SIZE + 1];
593 size_t jitter_data_count; 642 size_t jitter_data_count;
594 643
595 num_selected++; 644 num_selected++;
@@ -602,7 +651,7 @@ double jitter_request(int *status){
602 */ 651 */
603 /* Older servers doesn't know what jitter is, so if we get an 652 /* Older servers doesn't know what jitter is, so if we get an
604 * error on the first pass we redo it with "dispersion" */ 653 * error on the first pass we redo it with "dispersion" */
605 strncpy(req.data, getvar, MAX_CM_SIZE-1); 654 strncpy(req.data, getvar, MAX_CM_SIZE - 1);
606 req.count = htons(strlen(getvar)); 655 req.count = htons(strlen(getvar));
607 DBG(printf("sending READVAR request...\n")); 656 DBG(printf("sending READVAR request...\n"));
608 write(conn, &req, SIZEOF_NTPCM(req)); 657 write(conn, &req, SIZEOF_NTPCM(req));
@@ -613,8 +662,11 @@ double jitter_request(int *status){
613 read(conn, &req, SIZEOF_NTPCM(req)); 662 read(conn, &req, SIZEOF_NTPCM(req));
614 DBG(print_ntp_control_message(&req)); 663 DBG(print_ntp_control_message(&req));
615 664
616 if(req.op&REM_ERROR && strstr(getvar, "jitter")) { 665 if (req.op & REM_ERROR && strstr(getvar, "jitter")) {
617 if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n"); 666 if (verbose) {
667 printf("The 'jitter' command failed (old ntp server?)\nRestarting with "
668 "'dispersion'...\n");
669 }
618 getvar = "dispersion"; 670 getvar = "dispersion";
619 num_selected--; 671 num_selected--;
620 i--; 672 i--;
@@ -622,32 +674,33 @@ double jitter_request(int *status){
622 } 674 }
623 675
624 /* get to the float value */ 676 /* get to the float value */
625 if(verbose) { 677 if (verbose) {
626 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc)); 678 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc));
627 } 679 }
628 if((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)){ 680 if ((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)) {
629 die(STATE_UNKNOWN, 681 die(STATE_UNKNOWN, _("jitter response too large (%lu bytes)\n"),
630 _("jitter response too large (%lu bytes)\n"), 682 (unsigned long)jitter_data_count);
631 (unsigned long)jitter_data_count);
632 } 683 }
633 memcpy(jitter_data, req.data, jitter_data_count); 684 memcpy(jitter_data, req.data, jitter_data_count);
634 jitter_data[jitter_data_count] = '\0'; 685 jitter_data[jitter_data_count] = '\0';
635 startofvalue = strchr(jitter_data, '='); 686 startofvalue = strchr(jitter_data, '=');
636 if(startofvalue != NULL) { 687 if (startofvalue != NULL) {
637 startofvalue++; 688 startofvalue++;
638 jitter = strtod(startofvalue, &nptr); 689 jitter = strtod(startofvalue, &nptr);
639 } 690 }
640 if(startofvalue == NULL || startofvalue==nptr){ 691 if (startofvalue == NULL || startofvalue == nptr) {
641 printf("warning: unable to read server jitter response.\n"); 692 printf("warning: unable to read server jitter response.\n");
642 *status = STATE_UNKNOWN; 693 *status = STATE_UNKNOWN;
643 } else { 694 } else {
644 if(verbose) printf("%g\n", jitter); 695 if (verbose) {
696 printf("%g\n", jitter);
697 }
645 num_valid++; 698 num_valid++;
646 rval += jitter; 699 rval += jitter;
647 } 700 }
648 } 701 }
649 } 702 }
650 if(verbose){ 703 if (verbose) {
651 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected); 704 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
652 } 705 }
653 } 706 }
@@ -655,37 +708,33 @@ double jitter_request(int *status){
655 rval = num_valid ? rval / num_valid : -1.0; 708 rval = num_valid ? rval / num_valid : -1.0;
656 709
657 close(conn); 710 close(conn);
658 if(peers!=NULL) free(peers); 711 if (peers != NULL) {
712 free(peers);
713 }
659 /* If we return -1.0, it means no synchronization source was found */ 714 /* If we return -1.0, it means no synchronization source was found */
660 return rval; 715 return rval;
661} 716}
662 717
663int process_arguments(int argc, char **argv){ 718int process_arguments(int argc, char **argv) {
664 int c; 719 int c;
665 int option=0; 720 int option = 0;
666 static struct option longopts[] = { 721 static struct option longopts[] = {
667 {"version", no_argument, 0, 'V'}, 722 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
668 {"help", no_argument, 0, 'h'}, 723 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'},
669 {"verbose", no_argument, 0, 'v'}, 724 {"use-ipv6", no_argument, 0, '6'}, {"warning", required_argument, 0, 'w'},
670 {"use-ipv4", no_argument, 0, '4'}, 725 {"critical", required_argument, 0, 'c'}, {"jwarn", required_argument, 0, 'j'},
671 {"use-ipv6", no_argument, 0, '6'}, 726 {"jcrit", required_argument, 0, 'k'}, {"timeout", required_argument, 0, 't'},
672 {"warning", required_argument, 0, 'w'}, 727 {"hostname", required_argument, 0, 'H'}, {0, 0, 0, 0}};
673 {"critical", required_argument, 0, 'c'}, 728
674 {"jwarn", required_argument, 0, 'j'}, 729 if (argc < 2) {
675 {"jcrit", required_argument, 0, 'k'}, 730 usage("\n");
676 {"timeout", required_argument, 0, 't'}, 731 }
677 {"hostname", required_argument, 0, 'H'},
678 {0, 0, 0, 0}
679 };
680
681
682 if (argc < 2)
683 usage ("\n");
684 732
685 while (1) { 733 while (1) {
686 c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); 734 c = getopt_long(argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option);
687 if (c == -1 || c == EOF || c == 1) 735 if (c == -1 || c == EOF || c == 1) {
688 break; 736 break;
737 }
689 738
690 switch (c) { 739 switch (c) {
691 case 'h': 740 case 'h':
@@ -716,12 +765,13 @@ int process_arguments(int argc, char **argv){
716 jcrit = optarg; 765 jcrit = optarg;
717 break; 766 break;
718 case 'H': 767 case 'H':
719 if(!is_host(optarg)) 768 if (!is_host(optarg)) {
720 usage2(_("Invalid hostname/address"), optarg); 769 usage2(_("Invalid hostname/address"), optarg);
770 }
721 server_address = strdup(optarg); 771 server_address = strdup(optarg);
722 break; 772 break;
723 case 't': 773 case 't':
724 socket_timeout=atoi(optarg); 774 socket_timeout = atoi(optarg);
725 break; 775 break;
726 case '4': 776 case '4':
727 address_family = AF_INET; 777 address_family = AF_INET;
@@ -730,64 +780,59 @@ int process_arguments(int argc, char **argv){
730#ifdef USE_IPV6 780#ifdef USE_IPV6
731 address_family = AF_INET6; 781 address_family = AF_INET6;
732#else 782#else
733 usage4 (_("IPv6 support not available")); 783 usage4(_("IPv6 support not available"));
734#endif 784#endif
735 break; 785 break;
736 case '?': 786 case '?':
737 /* print short usage statement if args not parsable */ 787 /* print short usage statement if args not parsable */
738 usage5 (); 788 usage5();
739 break; 789 break;
740 } 790 }
741 } 791 }
742 792
743 if(server_address == NULL){ 793 if (server_address == NULL) {
744 usage4(_("Hostname was not supplied")); 794 usage4(_("Hostname was not supplied"));
745 } 795 }
746 796
747 return 0; 797 return 0;
748} 798}
749 799
750char *perfd_offset (double offset) 800char *perfd_offset(double offset) {
751{ 801 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
752 return fperfdata ("offset", offset, "s", 802 offset_thresholds->critical->end, false, 0, false, 0);
753 true, offset_thresholds->warning->end,
754 true, offset_thresholds->critical->end,
755 false, 0, false, 0);
756} 803}
757 804
758char *perfd_jitter (double jitter) 805char *perfd_jitter(double jitter) {
759{ 806 return fperfdata("jitter", jitter, "s", do_jitter, jitter_thresholds->warning->end, do_jitter,
760 return fperfdata ("jitter", jitter, "s", 807 jitter_thresholds->critical->end, true, 0, false, 0);
761 do_jitter, jitter_thresholds->warning->end,
762 do_jitter, jitter_thresholds->critical->end,
763 true, 0, false, 0);
764} 808}
765 809
766int main(int argc, char *argv[]){ 810int main(int argc, char *argv[]) {
767 int result, offset_result, jitter_result; 811 int result, offset_result, jitter_result;
768 double offset=0, jitter=0; 812 double offset = 0, jitter = 0;
769 char *result_line, *perfdata_line; 813 char *result_line, *perfdata_line;
770 814
771 setlocale (LC_ALL, ""); 815 setlocale(LC_ALL, "");
772 bindtextdomain (PACKAGE, LOCALEDIR); 816 bindtextdomain(PACKAGE, LOCALEDIR);
773 textdomain (PACKAGE); 817 textdomain(PACKAGE);
774 818
775 result = offset_result = jitter_result = STATE_OK; 819 result = offset_result = jitter_result = STATE_OK;
776 820
777 /* Parse extra opts if any */ 821 /* Parse extra opts if any */
778 argv=np_extra_opts (&argc, argv, progname); 822 argv = np_extra_opts(&argc, argv, progname);
779 823
780 if (process_arguments (argc, argv) == ERROR) 824 if (process_arguments(argc, argv) == ERROR) {
781 usage4 (_("Could not parse arguments")); 825 usage4(_("Could not parse arguments"));
826 }
782 827
783 set_thresholds(&offset_thresholds, owarn, ocrit); 828 set_thresholds(&offset_thresholds, owarn, ocrit);
784 set_thresholds(&jitter_thresholds, jwarn, jcrit); 829 set_thresholds(&jitter_thresholds, jwarn, jcrit);
785 830
786 /* initialize alarm signal handling */ 831 /* initialize alarm signal handling */
787 signal (SIGALRM, socket_timeout_alarm_handler); 832 signal(SIGALRM, socket_timeout_alarm_handler);
788 833
789 /* set socket timeout */ 834 /* set socket timeout */
790 alarm (socket_timeout); 835 alarm(socket_timeout);
791 836
792 offset = offset_request(server_address, &offset_result); 837 offset = offset_request(server_address, &offset_result);
793 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN. 838 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN.
@@ -803,31 +848,32 @@ int main(int argc, char *argv[]){
803 * servers recognize. Trying to check the jitter on OpenNTPD 848 * servers recognize. Trying to check the jitter on OpenNTPD
804 * (for example) will result in an error 849 * (for example) will result in an error
805 */ 850 */
806 if(do_jitter){ 851 if (do_jitter) {
807 jitter=jitter_request(&jitter_result); 852 jitter = jitter_request(&jitter_result);
808 result = max_state_alt(result, get_status(jitter, jitter_thresholds)); 853 result = max_state_alt(result, get_status(jitter, jitter_thresholds));
809 /* -1 indicates that we couldn't calculate the jitter 854 /* -1 indicates that we couldn't calculate the jitter
810 * Only overrides STATE_OK from the offset */ 855 * Only overrides STATE_OK from the offset */
811 if(jitter == -1.0 && result == STATE_OK) 856 if (jitter == -1.0 && result == STATE_OK) {
812 result = STATE_UNKNOWN; 857 result = STATE_UNKNOWN;
858 }
813 } 859 }
814 result = max_state_alt(result, jitter_result); 860 result = max_state_alt(result, jitter_result);
815 861
816 switch (result) { 862 switch (result) {
817 case STATE_CRITICAL : 863 case STATE_CRITICAL:
818 xasprintf(&result_line, _("NTP CRITICAL:")); 864 xasprintf(&result_line, _("NTP CRITICAL:"));
819 break; 865 break;
820 case STATE_WARNING : 866 case STATE_WARNING:
821 xasprintf(&result_line, _("NTP WARNING:")); 867 xasprintf(&result_line, _("NTP WARNING:"));
822 break; 868 break;
823 case STATE_OK : 869 case STATE_OK:
824 xasprintf(&result_line, _("NTP OK:")); 870 xasprintf(&result_line, _("NTP OK:"));
825 break; 871 break;
826 default : 872 default:
827 xasprintf(&result_line, _("NTP UNKNOWN:")); 873 xasprintf(&result_line, _("NTP UNKNOWN:"));
828 break; 874 break;
829 } 875 }
830 if(offset_result == STATE_UNKNOWN){ 876 if (offset_result == STATE_UNKNOWN) {
831 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 877 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
832 xasprintf(&perfdata_line, ""); 878 xasprintf(&perfdata_line, "");
833 } else { 879 } else {
@@ -836,41 +882,41 @@ int main(int argc, char *argv[]){
836 } 882 }
837 if (do_jitter) { 883 if (do_jitter) {
838 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); 884 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter);
839 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); 885 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter));
840 } 886 }
841 printf("%s|%s\n", result_line, perfdata_line); 887 printf("%s|%s\n", result_line, perfdata_line);
842 888
843 if(server_address!=NULL) free(server_address); 889 if (server_address != NULL) {
890 free(server_address);
891 }
844 return result; 892 return result;
845} 893}
846 894
847 895void print_help(void) {
848
849void print_help(void){
850 print_revision(progname, NP_VERSION); 896 print_revision(progname, NP_VERSION);
851 897
852 printf ("Copyright (c) 2006 Sean Finney\n"); 898 printf("Copyright (c) 2006 Sean Finney\n");
853 printf (COPYRIGHT, copyright, email); 899 printf(COPYRIGHT, copyright, email);
854 900
855 printf ("%s\n", _("This plugin checks the selected ntp server")); 901 printf("%s\n", _("This plugin checks the selected ntp server"));
856 902
857 printf ("\n\n"); 903 printf("\n\n");
858 904
859 print_usage(); 905 print_usage();
860 printf (UT_HELP_VRSN); 906 printf(UT_HELP_VRSN);
861 printf (UT_EXTRA_OPTS); 907 printf(UT_EXTRA_OPTS);
862 printf (UT_HOST_PORT, 'p', "123"); 908 printf(UT_HOST_PORT, 'p', "123");
863 printf (UT_IPv46); 909 printf(UT_IPv46);
864 printf (" %s\n", "-w, --warning=THRESHOLD"); 910 printf(" %s\n", "-w, --warning=THRESHOLD");
865 printf (" %s\n", _("Offset to result in warning status (seconds)")); 911 printf(" %s\n", _("Offset to result in warning status (seconds)"));
866 printf (" %s\n", "-c, --critical=THRESHOLD"); 912 printf(" %s\n", "-c, --critical=THRESHOLD");
867 printf (" %s\n", _("Offset to result in critical status (seconds)")); 913 printf(" %s\n", _("Offset to result in critical status (seconds)"));
868 printf (" %s\n", "-j, --jwarn=THRESHOLD"); 914 printf(" %s\n", "-j, --jwarn=THRESHOLD");
869 printf (" %s\n", _("Warning threshold for jitter")); 915 printf(" %s\n", _("Warning threshold for jitter"));
870 printf (" %s\n", "-k, --jcrit=THRESHOLD"); 916 printf(" %s\n", "-k, --jcrit=THRESHOLD");
871 printf (" %s\n", _("Critical threshold for jitter")); 917 printf(" %s\n", _("Critical threshold for jitter"));
872 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 918 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
873 printf (UT_VERBOSE); 919 printf(UT_VERBOSE);
874 920
875 printf("\n"); 921 printf("\n");
876 printf("%s\n", _("Notes:")); 922 printf("%s\n", _("Notes:"));
@@ -881,21 +927,21 @@ void print_help(void){
881 printf(" %s\n", _("Normal offset check:")); 927 printf(" %s\n", _("Normal offset check:"));
882 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1")); 928 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1"));
883 printf("\n"); 929 printf("\n");
884 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); 930 printf(" %s\n",
931 _("Check jitter too, avoiding critical notifications if jitter isn't available"));
885 printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); 932 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")); 933 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
887 934
888 printf (UT_SUPPORT); 935 printf(UT_SUPPORT);
889 936
890 printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); 937 printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
891 printf ("%s\n\n", _("check_ntp_time instead.")); 938 printf("%s\n\n", _("check_ntp_time instead."));
892} 939}
893 940
894void 941void print_usage(void) {
895print_usage(void) 942 printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
896{ 943 printf("%s\n\n", _("check_ntp_time instead."));
897 printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); 944 printf("%s\n", _("Usage:"));
898 printf ("%s\n\n", _("check_ntp_time instead.")); 945 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n",
899 printf ("%s\n", _("Usage:")); 946 progname);
900 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n", progname);
901} 947}
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index f99e5032..24d1c9b5 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -35,6 +35,7 @@
35 * 35 *
36 *****************************************************************************/ 36 *****************************************************************************/
37 37
38#include "thresholds.h"
38const char *progname = "check_ntp_peer"; 39const char *progname = "check_ntp_peer";
39const char *copyright = "2006-2024"; 40const char *copyright = "2006-2024";
40const char *email = "devel@monitoring-plugins.org"; 41const char *email = "devel@monitoring-plugins.org";
@@ -42,30 +43,18 @@ const char *email = "devel@monitoring-plugins.org";
42#include "common.h" 43#include "common.h"
43#include "netutils.h" 44#include "netutils.h"
44#include "utils.h" 45#include "utils.h"
46#include "../lib/states.h"
47#include "check_ntp_peer.d/config.h"
45 48
46static char *server_address = NULL;
47static int port = 123;
48static int verbose = 0; 49static int verbose = 0;
49static bool quiet = false;
50static char *owarn = "60";
51static char *ocrit = "120";
52static bool do_stratum = false;
53static char *swarn = "-1:16";
54static char *scrit = "-1:16";
55static bool do_jitter = false;
56static char *jwarn = "-1:5000";
57static char *jcrit = "-1:10000";
58static bool do_truechimers = false;
59static char *twarn = "0:";
60static char *tcrit = "0:";
61static bool syncsource_found = false; 50static bool syncsource_found = false;
62static bool li_alarm = false; 51static bool li_alarm = false;
63 52
64static int process_arguments(int /*argc*/, char ** /*argv*/); 53typedef struct {
65static thresholds *offset_thresholds = NULL; 54 int errorcode;
66static thresholds *jitter_thresholds = NULL; 55 check_ntp_peer_config config;
67static thresholds *stratum_thresholds = NULL; 56} check_ntp_peer_config_wrapper;
68static thresholds *truechimer_thresholds = NULL; 57static check_ntp_peer_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
69static void print_help(void); 58static void print_help(void);
70void print_usage(void); 59void print_usage(void);
71 60
@@ -94,9 +83,9 @@ typedef struct {
94/* bits 1,2 are the leap indicator */ 83/* bits 1,2 are the leap indicator */
95#define LI_MASK 0xc0 84#define LI_MASK 0xc0
96#define LI(x) ((x & LI_MASK) >> 6) 85#define LI(x) ((x & LI_MASK) >> 6)
97#define LI_SET(x, y) \ 86#define LI_SET(x, y) \
98 do { \ 87 do { \
99 x |= ((y << 6) & LI_MASK); \ 88 x |= ((y << 6) & LI_MASK); \
100 } while (0) 89 } while (0)
101/* and these are the values of the leap indicator */ 90/* and these are the values of the leap indicator */
102#define LI_NOWARNING 0x00 91#define LI_NOWARNING 0x00
@@ -106,17 +95,17 @@ typedef struct {
106/* bits 3,4,5 are the ntp version */ 95/* bits 3,4,5 are the ntp version */
107#define VN_MASK 0x38 96#define VN_MASK 0x38
108#define VN(x) ((x & VN_MASK) >> 3) 97#define VN(x) ((x & VN_MASK) >> 3)
109#define VN_SET(x, y) \ 98#define VN_SET(x, y) \
110 do { \ 99 do { \
111 x |= ((y << 3) & VN_MASK); \ 100 x |= ((y << 3) & VN_MASK); \
112 } while (0) 101 } while (0)
113#define VN_RESERVED 0x02 102#define VN_RESERVED 0x02
114/* bits 6,7,8 are the ntp mode */ 103/* bits 6,7,8 are the ntp mode */
115#define MODE_MASK 0x07 104#define MODE_MASK 0x07
116#define MODE(x) (x & MODE_MASK) 105#define MODE(x) (x & MODE_MASK)
117#define MODE_SET(x, y) \ 106#define MODE_SET(x, y) \
118 do { \ 107 do { \
119 x |= (y & MODE_MASK); \ 108 x |= (y & MODE_MASK); \
120 } while (0) 109 } while (0)
121/* here are some values */ 110/* here are some values */
122#define MODE_CLIENT 0x03 111#define MODE_CLIENT 0x03
@@ -128,9 +117,9 @@ typedef struct {
128#define REM_MORE 0x20 117#define REM_MORE 0x20
129/* In control message, bits 11 - 15 are opcode */ 118/* In control message, bits 11 - 15 are opcode */
130#define OP_MASK 0x1f 119#define OP_MASK 0x1f
131#define OP_SET(x, y) \ 120#define OP_SET(x, y) \
132 do { \ 121 do { \
133 x |= (y & OP_MASK); \ 122 x |= (y & OP_MASK); \
134 } while (0) 123 } while (0)
135#define OP_READSTAT 0x01 124#define OP_READSTAT 0x01
136#define OP_READVAR 0x02 125#define OP_READVAR 0x02
@@ -143,39 +132,40 @@ typedef struct {
143/* NTP control message header is 12 bytes, plus any data in the data 132/* NTP control message header is 12 bytes, plus any data in the data
144 * field, plus null padding to the nearest 32-bit boundary per rfc. 133 * field, plus null padding to the nearest 32-bit boundary per rfc.
145 */ 134 */
146#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) 135#define SIZEOF_NTPCM(m) \
136 (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0))
147 137
148/* finally, a little helper or two for debugging: */ 138/* finally, a little helper or two for debugging: */
149#define DBG(x) \ 139#define DBG(x) \
150 do { \ 140 do { \
151 if (verbose > 1) { \ 141 if (verbose > 1) { \
152 x; \ 142 x; \
153 } \ 143 } \
154 } while (0); 144 } while (0);
155#define PRINTSOCKADDR(x) \ 145#define PRINTSOCKADDR(x) \
156 do { \ 146 do { \
157 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ 147 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
158 } while (0); 148 } while (0);
159 149
160void print_ntp_control_message(const ntp_control_message *p) { 150void print_ntp_control_message(const ntp_control_message *message) {
161 printf("control packet contents:\n"); 151 printf("control packet contents:\n");
162 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); 152 printf("\tflags: 0x%.2x , 0x%.2x\n", message->flags, message->op);
163 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); 153 printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK);
164 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); 154 printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK);
165 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); 155 printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK);
166 printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP); 156 printf("\t response=%d (0x%.2x)\n", (message->op & REM_RESP) > 0, message->op & REM_RESP);
167 printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE); 157 printf("\t more=%d (0x%.2x)\n", (message->op & REM_MORE) > 0, message->op & REM_MORE);
168 printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR); 158 printf("\t error=%d (0x%.2x)\n", (message->op & REM_ERROR) > 0, message->op & REM_ERROR);
169 printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK); 159 printf("\t op=%d (0x%.2x)\n", message->op & OP_MASK, message->op & OP_MASK);
170 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); 160 printf("\tsequence: %d (0x%.2x)\n", ntohs(message->seq), ntohs(message->seq));
171 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); 161 printf("\tstatus: %d (0x%.2x)\n", ntohs(message->status), ntohs(message->status));
172 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); 162 printf("\tassoc: %d (0x%.2x)\n", ntohs(message->assoc), ntohs(message->assoc));
173 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); 163 printf("\toffset: %d (0x%.2x)\n", ntohs(message->offset), ntohs(message->offset));
174 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); 164 printf("\tcount: %d (0x%.2x)\n", ntohs(message->count), ntohs(message->count));
175 165
176 int numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair)); 166 int numpeers = ntohs(message->count) / (sizeof(ntp_assoc_status_pair));
177 if (p->op & REM_RESP && p->op & OP_READSTAT) { 167 if (message->op & REM_RESP && message->op & OP_READSTAT) {
178 const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)p->data; 168 const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)message->data;
179 for (int i = 0; i < numpeers; i++) { 169 for (int i = 0; i < numpeers; i++) {
180 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status)); 170 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
181 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) { 171 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
@@ -190,13 +180,13 @@ void print_ntp_control_message(const ntp_control_message *p) {
190 } 180 }
191} 181}
192 182
193void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) { 183void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_t seq) {
194 memset(p, 0, sizeof(ntp_control_message)); 184 memset(message, 0, sizeof(ntp_control_message));
195 LI_SET(p->flags, LI_NOWARNING); 185 LI_SET(message->flags, LI_NOWARNING);
196 VN_SET(p->flags, VN_RESERVED); 186 VN_SET(message->flags, VN_RESERVED);
197 MODE_SET(p->flags, MODE_CONTROLMSG); 187 MODE_SET(message->flags, MODE_CONTROLMSG);
198 OP_SET(p->op, opcode); 188 OP_SET(message->op, opcode);
199 p->seq = htons(seq); 189 message->seq = htons(seq);
200 /* Remaining fields are zero for requests */ 190 /* Remaining fields are zero for requests */
201} 191}
202 192
@@ -211,10 +201,23 @@ void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq)
211 * status is pretty much useless as syncsource_found is a global variable 201 * status is pretty much useless as syncsource_found is a global variable
212 * used later in main to check is the server was synchronized. It works 202 * used later in main to check is the server was synchronized. It works
213 * so I left it alone */ 203 * so I left it alone */
214int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum, int *num_truechimers) { 204typedef struct {
215 *offset_result = STATE_UNKNOWN; 205 mp_state_enum state;
216 *jitter = *stratum = -1; 206 mp_state_enum offset_result;
217 *num_truechimers = 0; 207 double offset;
208 double jitter;
209 long stratum;
210 int num_truechimers;
211} ntp_request_result;
212ntp_request_result ntp_request(const check_ntp_peer_config config) {
213
214 ntp_request_result result = {
215 .state = STATE_OK,
216 .offset_result = STATE_UNKNOWN,
217 .jitter = -1,
218 .stratum = -1,
219 .num_truechimers = 0,
220 };
218 221
219 /* Long-winded explanation: 222 /* Long-winded explanation:
220 * Getting the sync peer offset, jitter and stratum requires a number of 223 * Getting the sync peer offset, jitter and stratum requires a number of
@@ -237,10 +240,10 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
237 void *tmp; 240 void *tmp;
238 ntp_assoc_status_pair *peers = NULL; 241 ntp_assoc_status_pair *peers = NULL;
239 int peer_offset = 0; 242 int peer_offset = 0;
240 int peers_size = 0; 243 size_t peers_size = 0;
241 int npeers = 0; 244 size_t npeers = 0;
242 int conn = -1; 245 int conn = -1;
243 my_udp_connect(server_address, port, &conn); 246 my_udp_connect(config.server_address, config.port, &conn);
244 247
245 /* keep sending requests until the server stops setting the 248 /* keep sending requests until the server stops setting the
246 * REM_MORE bit, though usually this is only 1 packet. */ 249 * REM_MORE bit, though usually this is only 1 packet. */
@@ -255,24 +258,28 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
255 /* Attempt to read the largest size packet possible */ 258 /* Attempt to read the largest size packet possible */
256 req.count = htons(MAX_CM_SIZE); 259 req.count = htons(MAX_CM_SIZE);
257 DBG(printf("receiving READSTAT response")) 260 DBG(printf("receiving READSTAT response"))
258 if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) 261 if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) {
259 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 262 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
263 }
260 DBG(print_ntp_control_message(&req)); 264 DBG(print_ntp_control_message(&req));
261 /* discard obviously invalid packets */ 265 /* discard obviously invalid packets */
262 if (ntohs(req.count) > MAX_CM_SIZE) 266 if (ntohs(req.count) > MAX_CM_SIZE) {
263 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");
268 }
264 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); 269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1));
265 270
266 if (LI(req.flags) == LI_ALARM) 271 if (LI(req.flags) == LI_ALARM) {
267 li_alarm = true; 272 li_alarm = true;
273 }
268 /* Each peer identifier is 4 bytes in the data section, which 274 /* Each peer identifier is 4 bytes in the data section, which
269 * we represent as a ntp_assoc_status_pair datatype. 275 * we represent as a ntp_assoc_status_pair datatype.
270 */ 276 */
271 peers_size += ntohs(req.count); 277 peers_size += ntohs(req.count);
272 if ((tmp = realloc(peers, peers_size)) == NULL) 278 if ((tmp = realloc(peers, peers_size)) == NULL) {
273 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 279 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
280 }
274 peers = tmp; 281 peers = tmp;
275 memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count)); 282 memcpy((peers + peer_offset), (void *)req.data, ntohs(req.count));
276 npeers = peers_size / sizeof(ntp_assoc_status_pair); 283 npeers = peers_size / sizeof(ntp_assoc_status_pair);
277 peer_offset += ntohs(req.count); 284 peer_offset += ntohs(req.count);
278 } while (req.op & REM_MORE); 285 } while (req.op & REM_MORE);
@@ -280,9 +287,9 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
280 /* first, let's find out if we have a sync source, or if there are 287 /* first, let's find out if we have a sync source, or if there are
281 * at least some candidates. In the latter case we'll issue 288 * at least some candidates. In the latter case we'll issue
282 * a warning but go ahead with the check on them. */ 289 * a warning but go ahead with the check on them. */
283 for (int i = 0; i < npeers; i++) { 290 for (size_t i = 0; i < npeers; i++) {
284 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { 291 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) {
285 (*num_truechimers)++; 292 result.num_truechimers++;
286 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { 293 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
287 num_candidates++; 294 num_candidates++;
288 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { 295 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
@@ -293,31 +300,35 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
293 } 300 }
294 } 301 }
295 302
296 if (verbose) 303 if (verbose) {
297 printf("%d candidate peers available\n", num_candidates); 304 printf("%d candidate peers available\n", num_candidates);
298 if (verbose && syncsource_found) 305 }
306 if (verbose && syncsource_found) {
299 printf("synchronization source found\n"); 307 printf("synchronization source found\n");
308 }
300 309
301 int status = STATE_OK;
302 if (!syncsource_found) { 310 if (!syncsource_found) {
303 status = STATE_WARNING; 311 result.state = STATE_WARNING;
304 if (verbose) 312 if (verbose) {
305 printf("warning: no synchronization source found\n"); 313 printf("warning: no synchronization source found\n");
314 }
306 } 315 }
307 if (li_alarm) { 316 if (li_alarm) {
308 status = STATE_WARNING; 317 result.state = STATE_WARNING;
309 if (verbose) 318 if (verbose) {
310 printf("warning: LI_ALARM bit is set\n"); 319 printf("warning: LI_ALARM bit is set\n");
320 }
311 } 321 }
312 322
313 const char *getvar = "stratum,offset,jitter"; 323 const char *getvar = "stratum,offset,jitter";
314 char *data; 324 char *data;
315 for (int i = 0; i < npeers; i++) { 325 for (size_t i = 0; i < npeers; i++) {
316 /* Only query this server if it is the current sync source */ 326 /* Only query this server if it is the current sync source */
317 /* If there's no sync.peer, query all candidates and use the best one */ 327 /* If there's no sync.peer, query all candidates and use the best one */
318 if (PEER_SEL(peers[i].status) >= min_peer_sel) { 328 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
319 if (verbose) 329 if (verbose) {
320 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); 330 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
331 }
321 xasprintf(&data, ""); 332 xasprintf(&data, "");
322 do { 333 do {
323 setup_control_request(&req, OP_READVAR, 2); 334 setup_control_request(&req, OP_READVAR, 2);
@@ -342,81 +353,95 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
342 DBG(print_ntp_control_message(&req)); 353 DBG(print_ntp_control_message(&req));
343 } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2)); 354 } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2));
344 355
345 if (!(req.op & REM_ERROR)) 356 if (!(req.op & REM_ERROR)) {
346 xasprintf(&data, "%s%s", data, req.data); 357 xasprintf(&data, "%s%s", data, req.data);
358 }
347 } while (req.op & REM_MORE); 359 } while (req.op & REM_MORE);
348 360
349 if (req.op & REM_ERROR) { 361 if (req.op & REM_ERROR) {
350 if (strstr(getvar, "jitter")) { 362 if (strstr(getvar, "jitter")) {
351 if (verbose) 363 if (verbose) {
352 printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with " 364 printf("The command failed. This is usually caused by servers refusing the "
365 "'jitter'\nvariable. Restarting with "
353 "'dispersion'...\n"); 366 "'dispersion'...\n");
367 }
354 getvar = "stratum,offset,dispersion"; 368 getvar = "stratum,offset,dispersion";
355 i--; 369 i--;
356 continue; 370 continue;
357 } 371 }
358 if (strlen(getvar)) { 372 if (strlen(getvar)) {
359 if (verbose) 373 if (verbose) {
360 printf("Server didn't like dispersion either; will retrieve everything\n"); 374 printf("Server didn't like dispersion either; will retrieve everything\n");
375 }
361 getvar = ""; 376 getvar = "";
362 i--; 377 i--;
363 continue; 378 continue;
364 } 379 }
365 } 380 }
366 381
367 if (verbose > 1) 382 if (verbose > 1) {
368 printf("Server responded: >>>%s<<<\n", data); 383 printf("Server responded: >>>%s<<<\n", data);
384 }
369 385
370 double tmp_offset = 0; 386 double tmp_offset = 0;
371 char *value; 387 char *value;
372 char *nptr; 388 char *nptr;
373 /* get the offset */ 389 /* get the offset */
374 if (verbose) 390 if (verbose) {
375 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc)); 391 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc));
392 }
376 393
377 value = np_extract_ntpvar(data, "offset"); 394 value = np_extract_ntpvar(data, "offset");
378 nptr = NULL; 395 nptr = NULL;
379 /* Convert the value if we have one */ 396 /* Convert the value if we have one */
380 if (value != NULL) 397 if (value != NULL) {
381 tmp_offset = strtod(value, &nptr) / 1000; 398 tmp_offset = strtod(value, &nptr) / 1000;
399 }
382 /* If value is null or no conversion was performed */ 400 /* If value is null or no conversion was performed */
383 if (value == NULL || value == nptr) { 401 if (value == NULL || value == nptr) {
384 if (verbose) 402 if (verbose) {
385 printf("error: unable to read server offset response.\n"); 403 printf("error: unable to read server offset response.\n");
404 }
386 } else { 405 } else {
387 if (verbose) 406 if (verbose) {
388 printf("%.10g\n", tmp_offset); 407 printf("%.10g\n", tmp_offset);
389 if (*offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(*offset)) { 408 }
390 *offset = tmp_offset; 409 if (result.offset_result == STATE_UNKNOWN ||
391 *offset_result = STATE_OK; 410 fabs(tmp_offset) < fabs(result.offset)) {
411 result.offset = tmp_offset;
412 result.offset_result = STATE_OK;
392 } else { 413 } else {
393 /* Skip this one; move to the next */ 414 /* Skip this one; move to the next */
394 continue; 415 continue;
395 } 416 }
396 } 417 }
397 418
398 if (do_jitter) { 419 if (config.do_jitter) {
399 /* get the jitter */ 420 /* get the jitter */
400 if (verbose) { 421 if (verbose) {
401 printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", 422 printf("parsing %s from peer %.2x: ",
423 strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter",
402 ntohs(peers[i].assoc)); 424 ntohs(peers[i].assoc));
403 } 425 }
404 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter"); 426 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion"
427 : "jitter");
405 nptr = NULL; 428 nptr = NULL;
406 /* Convert the value if we have one */ 429 /* Convert the value if we have one */
407 if (value != NULL) 430 if (value != NULL) {
408 *jitter = strtod(value, &nptr); 431 result.jitter = strtod(value, &nptr);
432 }
409 /* If value is null or no conversion was performed */ 433 /* If value is null or no conversion was performed */
410 if (value == NULL || value == nptr) { 434 if (value == NULL || value == nptr) {
411 if (verbose) 435 if (verbose) {
412 printf("error: unable to read server jitter/dispersion response.\n"); 436 printf("error: unable to read server jitter/dispersion response.\n");
413 *jitter = -1; 437 }
438 result.jitter = -1;
414 } else if (verbose) { 439 } else if (verbose) {
415 printf("%.10g\n", *jitter); 440 printf("%.10g\n", result.jitter);
416 } 441 }
417 } 442 }
418 443
419 if (do_stratum) { 444 if (config.do_stratum) {
420 /* get the stratum */ 445 /* get the stratum */
421 if (verbose) { 446 if (verbose) {
422 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc)); 447 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc));
@@ -424,44 +449,59 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
424 value = np_extract_ntpvar(data, "stratum"); 449 value = np_extract_ntpvar(data, "stratum");
425 nptr = NULL; 450 nptr = NULL;
426 /* Convert the value if we have one */ 451 /* Convert the value if we have one */
427 if (value != NULL) 452 if (value != NULL) {
428 *stratum = strtol(value, &nptr, 10); 453 result.stratum = strtol(value, &nptr, 10);
454 }
429 if (value == NULL || value == nptr) { 455 if (value == NULL || value == nptr) {
430 if (verbose) 456 if (verbose) {
431 printf("error: unable to read server stratum response.\n"); 457 printf("error: unable to read server stratum response.\n");
432 *stratum = -1; 458 }
459 result.stratum = -1;
433 } else { 460 } else {
434 if (verbose) 461 if (verbose) {
435 printf("%i\n", *stratum); 462 printf("%li\n", result.stratum);
463 }
436 } 464 }
437 } 465 }
438 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */ 466 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */
439 } /* for (i = 0; i < npeers; i++) */ 467 } /* for (i = 0; i < npeers; i++) */
440 468
441 close(conn); 469 close(conn);
442 if (peers != NULL) 470 if (peers != NULL) {
443 free(peers); 471 free(peers);
472 }
444 473
445 return status; 474 return result;
446} 475}
447 476
448int process_arguments(int argc, char **argv) { 477check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
449 static struct option longopts[] = { 478 static struct option longopts[] = {
450 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, 479 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
451 {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, 480 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'},
452 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"swarn", required_argument, 0, 'W'}, 481 {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'},
453 {"scrit", required_argument, 0, 'C'}, {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, 482 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'},
454 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, {"timeout", required_argument, 0, 't'}, 483 {"swarn", required_argument, 0, 'W'}, {"scrit", required_argument, 0, 'C'},
455 {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; 484 {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'},
456 485 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'},
457 if (argc < 2) 486 {"timeout", required_argument, 0, 't'}, {"hostname", required_argument, 0, 'H'},
487 {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}};
488
489 if (argc < 2) {
458 usage("\n"); 490 usage("\n");
491 }
492
493 check_ntp_peer_config_wrapper result = {
494 .errorcode = OK,
495 .config = check_ntp_peer_config_init(),
496 };
459 497
460 while (true) { 498 while (true) {
461 int option = 0; 499 int option = 0;
462 int option_char = getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); 500 int option_char =
463 if (option_char == -1 || option_char == EOF || option_char == 1) 501 getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option);
502 if (option_char == -1 || option_char == EOF || option_char == 1) {
464 break; 503 break;
504 }
465 505
466 switch (option_char) { 506 switch (option_char) {
467 case 'h': 507 case 'h':
@@ -476,45 +516,46 @@ int process_arguments(int argc, char **argv) {
476 verbose++; 516 verbose++;
477 break; 517 break;
478 case 'q': 518 case 'q':
479 quiet = true; 519 result.config.quiet = true;
480 break; 520 break;
481 case 'w': 521 case 'w':
482 owarn = optarg; 522 result.config.owarn = optarg;
483 break; 523 break;
484 case 'c': 524 case 'c':
485 ocrit = optarg; 525 result.config.ocrit = optarg;
486 break; 526 break;
487 case 'W': 527 case 'W':
488 do_stratum = true; 528 result.config.do_stratum = true;
489 swarn = optarg; 529 result.config.swarn = optarg;
490 break; 530 break;
491 case 'C': 531 case 'C':
492 do_stratum = true; 532 result.config.do_stratum = true;
493 scrit = optarg; 533 result.config.scrit = optarg;
494 break; 534 break;
495 case 'j': 535 case 'j':
496 do_jitter = true; 536 result.config.do_jitter = true;
497 jwarn = optarg; 537 result.config.jwarn = optarg;
498 break; 538 break;
499 case 'k': 539 case 'k':
500 do_jitter = true; 540 result.config.do_jitter = true;
501 jcrit = optarg; 541 result.config.jcrit = optarg;
502 break; 542 break;
503 case 'm': 543 case 'm':
504 do_truechimers = true; 544 result.config.do_truechimers = true;
505 twarn = optarg; 545 result.config.twarn = optarg;
506 break; 546 break;
507 case 'n': 547 case 'n':
508 do_truechimers = true; 548 result.config.do_truechimers = true;
509 tcrit = optarg; 549 result.config.tcrit = optarg;
510 break; 550 break;
511 case 'H': 551 case 'H':
512 if (!is_host(optarg)) 552 if (!is_host(optarg)) {
513 usage2(_("Invalid hostname/address"), optarg); 553 usage2(_("Invalid hostname/address"), optarg);
514 server_address = strdup(optarg); 554 }
555 result.config.server_address = strdup(optarg);
515 break; 556 break;
516 case 'p': 557 case 'p':
517 port = atoi(optarg); 558 result.config.port = atoi(optarg);
518 break; 559 break;
519 case 't': 560 case 't':
520 socket_timeout = atoi(optarg); 561 socket_timeout = atoi(optarg);
@@ -536,30 +577,37 @@ int process_arguments(int argc, char **argv) {
536 } 577 }
537 } 578 }
538 579
539 if (server_address == NULL) { 580 if (result.config.server_address == NULL) {
540 usage4(_("Hostname was not supplied")); 581 usage4(_("Hostname was not supplied"));
541 } 582 }
542 583
543 return 0; 584 set_thresholds(&result.config.offset_thresholds, result.config.owarn, result.config.ocrit);
585 set_thresholds(&result.config.jitter_thresholds, result.config.jwarn, result.config.jcrit);
586 set_thresholds(&result.config.stratum_thresholds, result.config.swarn, result.config.scrit);
587 set_thresholds(&result.config.truechimer_thresholds, result.config.twarn, result.config.tcrit);
588
589 return result;
544} 590}
545 591
546char *perfd_offset(double offset) { 592char *perfd_offset(double offset, thresholds *offset_thresholds) {
547 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, 593 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
548 0); 594 offset_thresholds->critical->end, false, 0, false, 0);
549} 595}
550 596
551char *perfd_jitter(double jitter) { 597char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) {
552 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, jitter_thresholds->critical->end, true, 0, 598 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter,
553 false, 0); 599 jitter_thresholds->critical->end, true, 0, false, 0);
554} 600}
555 601
556char *perfd_stratum(int stratum) { 602char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) {
557 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, do_stratum, 603 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end,
558 (int)stratum_thresholds->critical->end, true, 0, true, 16); 604 do_stratum, (int)stratum_thresholds->critical->end, true, 0, true, 16);
559} 605}
560 606
561char *perfd_truechimers(int num_truechimers) { 607char *perfd_truechimers(int num_truechimers, const bool do_truechimers,
562 return perfdata("truechimers", num_truechimers, "", do_truechimers, (int)truechimer_thresholds->warning->end, do_truechimers, 608 thresholds *truechimer_thresholds) {
609 return perfdata("truechimers", num_truechimers, "", do_truechimers,
610 (int)truechimer_thresholds->warning->end, do_truechimers,
563 (int)truechimer_thresholds->critical->end, true, 0, false, 0); 611 (int)truechimer_thresholds->critical->end, true, 0, false, 0);
564} 612}
565 613
@@ -571,13 +619,13 @@ int main(int argc, char *argv[]) {
571 /* Parse extra opts if any */ 619 /* Parse extra opts if any */
572 argv = np_extra_opts(&argc, argv, progname); 620 argv = np_extra_opts(&argc, argv, progname);
573 621
574 if (process_arguments(argc, argv) == ERROR) 622 check_ntp_peer_config_wrapper tmp_config = process_arguments(argc, argv);
623
624 if (tmp_config.errorcode == ERROR) {
575 usage4(_("Could not parse arguments")); 625 usage4(_("Could not parse arguments"));
626 }
576 627
577 set_thresholds(&offset_thresholds, owarn, ocrit); 628 const check_ntp_peer_config config = tmp_config.config;
578 set_thresholds(&jitter_thresholds, jwarn, jcrit);
579 set_thresholds(&stratum_thresholds, swarn, scrit);
580 set_thresholds(&truechimer_thresholds, twarn, tcrit);
581 629
582 /* initialize alarm signal handling */ 630 /* initialize alarm signal handling */
583 signal(SIGALRM, socket_timeout_alarm_handler); 631 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -585,44 +633,40 @@ int main(int argc, char *argv[]) {
585 /* set socket timeout */ 633 /* set socket timeout */
586 alarm(socket_timeout); 634 alarm(socket_timeout);
587 635
588 int offset_result;
589 int stratum;
590 int num_truechimers;
591 double offset = 0;
592 double jitter = 0;
593 /* This returns either OK or WARNING (See comment preceding ntp_request) */ 636 /* This returns either OK or WARNING (See comment preceding ntp_request) */
594 int result = ntp_request(&offset, &offset_result, &jitter, &stratum, &num_truechimers); 637 ntp_request_result ntp_res = ntp_request(config);
638 mp_state_enum result = STATE_UNKNOWN;
595 639
596 if (offset_result == STATE_UNKNOWN) { 640 if (ntp_res.offset_result == STATE_UNKNOWN) {
597 /* if there's no sync peer (this overrides ntp_request output): */ 641 /* if there's no sync peer (this overrides ntp_request output): */
598 result = (quiet ? STATE_UNKNOWN : STATE_CRITICAL); 642 result = (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL);
599 } else { 643 } else {
600 /* Be quiet if there's no candidates either */ 644 /* Be quiet if there's no candidates either */
601 if (quiet && result == STATE_WARNING) 645 if (config.quiet && result == STATE_WARNING) {
602 result = STATE_UNKNOWN; 646 result = STATE_UNKNOWN;
603 result = max_state_alt(result, get_status(fabs(offset), offset_thresholds)); 647 }
648 result = max_state_alt(result, get_status(fabs(ntp_res.offset), config.offset_thresholds));
604 } 649 }
605 650
606 int oresult = result; 651 mp_state_enum oresult = result;
607 652 mp_state_enum tresult = STATE_UNKNOWN;
608 int tresult = STATE_UNKNOWN;
609 653
610 if (do_truechimers) { 654 if (config.do_truechimers) {
611 tresult = get_status(num_truechimers, truechimer_thresholds); 655 tresult = get_status(ntp_res.num_truechimers, config.truechimer_thresholds);
612 result = max_state_alt(result, tresult); 656 result = max_state_alt(result, tresult);
613 } 657 }
614 658
615 int sresult = STATE_UNKNOWN; 659 mp_state_enum sresult = STATE_UNKNOWN;
616 660
617 if (do_stratum) { 661 if (config.do_stratum) {
618 sresult = get_status(stratum, stratum_thresholds); 662 sresult = get_status((double)ntp_res.stratum, config.stratum_thresholds);
619 result = max_state_alt(result, sresult); 663 result = max_state_alt(result, sresult);
620 } 664 }
621 665
622 int jresult = STATE_UNKNOWN; 666 mp_state_enum jresult = STATE_UNKNOWN;
623 667
624 if (do_jitter) { 668 if (config.do_jitter) {
625 jresult = get_status(jitter, jitter_thresholds); 669 jresult = get_status(ntp_res.jitter, config.jitter_thresholds);
626 result = max_state_alt(result, jresult); 670 result = max_state_alt(result, jresult);
627 } 671 }
628 672
@@ -641,59 +685,74 @@ int main(int argc, char *argv[]) {
641 xasprintf(&result_line, _("NTP UNKNOWN:")); 685 xasprintf(&result_line, _("NTP UNKNOWN:"));
642 break; 686 break;
643 } 687 }
644 if (!syncsource_found) 688
689 if (!syncsource_found) {
645 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); 690 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized"));
646 else if (li_alarm) 691 } else if (li_alarm) {
647 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); 692 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set"));
693 }
648 694
649 char *perfdata_line; 695 char *perfdata_line;
650 if (offset_result == STATE_UNKNOWN) { 696 if (ntp_res.offset_result == STATE_UNKNOWN) {
651 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 697 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
652 xasprintf(&perfdata_line, ""); 698 xasprintf(&perfdata_line, "");
653 } else if (oresult == STATE_WARNING) { 699 } else if (oresult == STATE_WARNING) {
654 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), offset); 700 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"),
701 ntp_res.offset);
655 } else if (oresult == STATE_CRITICAL) { 702 } else if (oresult == STATE_CRITICAL) {
656 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), offset); 703 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"),
704 ntp_res.offset);
657 } else { 705 } else {
658 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 706 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), ntp_res.offset);
659 } 707 }
660 xasprintf(&perfdata_line, "%s", perfd_offset(offset)); 708 xasprintf(&perfdata_line, "%s", perfd_offset(ntp_res.offset, config.offset_thresholds));
661 709
662 if (do_jitter) { 710 if (config.do_jitter) {
663 if (jresult == STATE_WARNING) { 711 if (jresult == STATE_WARNING) {
664 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, jitter); 712 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, ntp_res.jitter);
665 } else if (jresult == STATE_CRITICAL) { 713 } else if (jresult == STATE_CRITICAL) {
666 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, jitter); 714 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, ntp_res.jitter);
667 } else { 715 } else {
668 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); 716 xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter);
669 } 717 }
670 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); 718 xasprintf(&perfdata_line, "%s %s", perfdata_line,
719 perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds));
671 } 720 }
672 if (do_stratum) { 721
722 if (config.do_stratum) {
673 if (sresult == STATE_WARNING) { 723 if (sresult == STATE_WARNING) {
674 xasprintf(&result_line, "%s, stratum=%i (WARNING)", result_line, stratum); 724 xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum);
675 } else if (sresult == STATE_CRITICAL) { 725 } else if (sresult == STATE_CRITICAL) {
676 xasprintf(&result_line, "%s, stratum=%i (CRITICAL)", result_line, stratum); 726 xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum);
677 } else { 727 } else {
678 xasprintf(&result_line, "%s, stratum=%i", result_line, stratum); 728 xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum);
679 } 729 }
680 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(stratum)); 730 xasprintf(&perfdata_line, "%s %s", perfdata_line,
731 perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds));
681 } 732 }
682 if (do_truechimers) { 733
734 if (config.do_truechimers) {
683 if (tresult == STATE_WARNING) { 735 if (tresult == STATE_WARNING) {
684 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, num_truechimers); 736 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line,
737 ntp_res.num_truechimers);
685 } else if (tresult == STATE_CRITICAL) { 738 } else if (tresult == STATE_CRITICAL) {
686 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, num_truechimers); 739 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line,
740 ntp_res.num_truechimers);
687 } else { 741 } else {
688 xasprintf(&result_line, "%s, truechimers=%i", result_line, num_truechimers); 742 xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers);
689 } 743 }
690 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_truechimers(num_truechimers)); 744 xasprintf(&perfdata_line, "%s %s", perfdata_line,
745 perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers,
746 config.truechimer_thresholds));
691 } 747 }
748
692 printf("%s|%s\n", result_line, perfdata_line); 749 printf("%s|%s\n", result_line, perfdata_line);
693 750
694 if (server_address != NULL) 751 if (config.server_address != NULL) {
695 free(server_address); 752 free(config.server_address);
696 return result; 753 }
754
755 exit(result);
697} 756}
698 757
699void print_help(void) { 758void print_help(void) {
@@ -712,7 +771,8 @@ void print_help(void) {
712 printf(UT_IPv46); 771 printf(UT_IPv46);
713 printf(UT_HOST_PORT, 'p', "123"); 772 printf(UT_HOST_PORT, 'p', "123");
714 printf(" %s\n", "-q, --quiet"); 773 printf(" %s\n", "-q, --quiet");
715 printf(" %s\n", _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); 774 printf(" %s\n",
775 _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized"));
716 printf(" %s\n", "-w, --warning=THRESHOLD"); 776 printf(" %s\n", "-w, --warning=THRESHOLD");
717 printf(" %s\n", _("Offset to result in warning status (seconds)")); 777 printf(" %s\n", _("Offset to result in warning status (seconds)"));
718 printf(" %s\n", "-c, --critical=THRESHOLD"); 778 printf(" %s\n", "-c, --critical=THRESHOLD");
@@ -749,7 +809,8 @@ void print_help(void) {
749 printf(" %s\n", _("Simple NTP server check:")); 809 printf(" %s\n", _("Simple NTP server check:"));
750 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1")); 810 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1"));
751 printf("\n"); 811 printf("\n");
752 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); 812 printf(" %s\n",
813 _("Check jitter too, avoiding critical notifications if jitter isn't available"));
753 printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); 814 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
754 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); 815 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
755 printf("\n"); 816 printf("\n");
diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h
new file mode 100644
index 00000000..00e6b05d
--- /dev/null
+++ b/plugins/check_ntp_peer.d/config.h
@@ -0,0 +1,67 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7enum {
8 DEFAULT_NTP_PORT = 123,
9};
10
11typedef struct {
12 char *server_address;
13 int port;
14
15 bool quiet;
16
17 // truechimer stuff
18 bool do_truechimers;
19 char *twarn;
20 char *tcrit;
21 thresholds *truechimer_thresholds;
22
23 char *owarn;
24 char *ocrit;
25 thresholds *offset_thresholds;
26
27 // stratum stuff
28 bool do_stratum;
29 char *swarn;
30 char *scrit;
31 thresholds *stratum_thresholds;
32
33 // jitter stuff
34 bool do_jitter;
35 char *jwarn;
36 char *jcrit;
37 thresholds *jitter_thresholds;
38
39} check_ntp_peer_config;
40
41check_ntp_peer_config check_ntp_peer_config_init() {
42 check_ntp_peer_config tmp = {
43 .server_address = NULL,
44 .port = DEFAULT_NTP_PORT,
45
46 .quiet = false,
47 .do_truechimers = false,
48 .twarn = "0:",
49 .tcrit = "0:",
50 .truechimer_thresholds = NULL,
51
52 .owarn = "60",
53 .ocrit = "120",
54 .offset_thresholds = NULL,
55
56 .do_stratum = false,
57 .swarn = "-1:16",
58 .scrit = "-1:16",
59 .stratum_thresholds = NULL,
60
61 .do_jitter = false,
62 .jwarn = "-1:5000",
63 .jcrit = "-1:10000",
64 .jitter_thresholds = NULL,
65 };
66 return tmp;
67}
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index 703b69df..ad69b804 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -41,17 +41,18 @@ const char *email = "devel@monitoring-plugins.org";
41#include "common.h" 41#include "common.h"
42#include "netutils.h" 42#include "netutils.h"
43#include "utils.h" 43#include "utils.h"
44#include "states.h"
45#include "thresholds.h"
46#include "check_ntp_time.d/config.h"
44 47
45static char *server_address = NULL;
46static char *port = "123";
47static int verbose = 0; 48static int verbose = 0;
48static bool quiet = false;
49static char *owarn = "60";
50static char *ocrit = "120";
51static int time_offset = 0;
52 49
53static int process_arguments(int, char **); 50typedef struct {
54static thresholds *offset_thresholds = NULL; 51 int errorcode;
52 check_ntp_time_config config;
53} check_ntp_time_config_wrapper;
54static check_ntp_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
55
55static void print_help(void); 56static void print_help(void);
56void print_usage(void); 57void print_usage(void);
57 58
@@ -92,9 +93,9 @@ typedef struct {
92/* bits 1,2 are the leap indicator */ 93/* bits 1,2 are the leap indicator */
93#define LI_MASK 0xc0 94#define LI_MASK 0xc0
94#define LI(x) ((x & LI_MASK) >> 6) 95#define LI(x) ((x & LI_MASK) >> 6)
95#define LI_SET(x, y) \ 96#define LI_SET(x, y) \
96 do { \ 97 do { \
97 x |= ((y << 6) & LI_MASK); \ 98 x |= ((y << 6) & LI_MASK); \
98 } while (0) 99 } while (0)
99/* and these are the values of the leap indicator */ 100/* and these are the values of the leap indicator */
100#define LI_NOWARNING 0x00 101#define LI_NOWARNING 0x00
@@ -104,17 +105,17 @@ typedef struct {
104/* bits 3,4,5 are the ntp version */ 105/* bits 3,4,5 are the ntp version */
105#define VN_MASK 0x38 106#define VN_MASK 0x38
106#define VN(x) ((x & VN_MASK) >> 3) 107#define VN(x) ((x & VN_MASK) >> 3)
107#define VN_SET(x, y) \ 108#define VN_SET(x, y) \
108 do { \ 109 do { \
109 x |= ((y << 3) & VN_MASK); \ 110 x |= ((y << 3) & VN_MASK); \
110 } while (0) 111 } while (0)
111#define VN_RESERVED 0x02 112#define VN_RESERVED 0x02
112/* bits 6,7,8 are the ntp mode */ 113/* bits 6,7,8 are the ntp mode */
113#define MODE_MASK 0x07 114#define MODE_MASK 0x07
114#define MODE(x) (x & MODE_MASK) 115#define MODE(x) (x & MODE_MASK)
115#define MODE_SET(x, y) \ 116#define MODE_SET(x, y) \
116 do { \ 117 do { \
117 x |= (y & MODE_MASK); \ 118 x |= (y & MODE_MASK); \
118 } while (0) 119 } while (0)
119/* here are some values */ 120/* here are some values */
120#define MODE_CLIENT 0x03 121#define MODE_CLIENT 0x03
@@ -126,9 +127,9 @@ typedef struct {
126#define REM_MORE 0x20 127#define REM_MORE 0x20
127/* In control message, bits 11 - 15 are opcode */ 128/* In control message, bits 11 - 15 are opcode */
128#define OP_MASK 0x1f 129#define OP_MASK 0x1f
129#define OP_SET(x, y) \ 130#define OP_SET(x, y) \
130 do { \ 131 do { \
131 x |= (y & OP_MASK); \ 132 x |= (y & OP_MASK); \
132 } while (0) 133 } while (0)
133#define OP_READSTAT 0x01 134#define OP_READSTAT 0x01
134#define OP_READVAR 0x02 135#define OP_READVAR 0x02
@@ -159,35 +160,37 @@ typedef struct {
159#define EPOCHDIFF 0x83aa7e80UL 160#define EPOCHDIFF 0x83aa7e80UL
160 161
161/* extract a 32-bit ntp fixed point number into a double */ 162/* extract a 32-bit ntp fixed point number into a double */
162#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0) 163#define NTP32asDOUBLE(x) (ntohs(L16(x)) + ((double)ntohs(R16(x)) / 65536.0))
163 164
164/* likewise for a 64-bit ntp fp number */ 165/* likewise for a 64-bit ntp fp number */
165#define NTP64asDOUBLE(n) \ 166#define NTP64asDOUBLE(n) \
166 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) : 0) 167 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \
168 (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \
169 : 0)
167 170
168/* convert a struct timeval to a double */ 171/* convert a struct timeval to a double */
169#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) 172#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec))
170 173
171/* convert an ntp 64-bit fp number to a struct timeval */ 174/* convert an ntp 64-bit fp number to a struct timeval */
172#define NTP64toTV(n, t) \ 175#define NTP64toTV(n, t) \
173 do { \ 176 do { \
174 if (!n) \ 177 if (!n) \
175 t.tv_sec = t.tv_usec = 0; \ 178 t.tv_sec = t.tv_usec = 0; \
176 else { \ 179 else { \
177 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \ 180 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \
178 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \ 181 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \
179 } \ 182 } \
180 } while (0) 183 } while (0)
181 184
182/* convert a struct timeval to an ntp 64-bit fp number */ 185/* convert a struct timeval to an ntp 64-bit fp number */
183#define TVtoNTP64(t, n) \ 186#define TVtoNTP64(t, n) \
184 do { \ 187 do { \
185 if (!t.tv_usec && !t.tv_sec) \ 188 if (!t.tv_usec && !t.tv_sec) \
186 n = 0x0UL; \ 189 n = 0x0UL; \
187 else { \ 190 else { \
188 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \ 191 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \
189 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \ 192 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \
190 } \ 193 } \
191 } while (0) 194 } while (0)
192 195
193/* NTP control message header is 12 bytes, plus any data in the data 196/* NTP control message header is 12 bytes, plus any data in the data
@@ -196,68 +199,64 @@ typedef struct {
196#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((m.count) ? 4 - (ntohs(m.count) % 4) : 0)) 199#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((m.count) ? 4 - (ntohs(m.count) % 4) : 0))
197 200
198/* finally, a little helper or two for debugging: */ 201/* finally, a little helper or two for debugging: */
199#define DBG(x) \ 202#define DBG(x) \
200 do { \ 203 do { \
201 if (verbose > 1) { \ 204 if (verbose > 1) { \
202 x; \ 205 x; \
203 } \ 206 } \
204 } while (0); 207 } while (0);
205#define PRINTSOCKADDR(x) \ 208#define PRINTSOCKADDR(x) \
206 do { \ 209 do { \
207 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ 210 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
208 } while (0); 211 } while (0);
209 212
210/* calculate the offset of the local clock */ 213/* calculate the offset of the local clock */
211static inline double calc_offset(const ntp_message *m, const struct timeval *t) { 214static inline double calc_offset(const ntp_message *message, const struct timeval *time_value) {
212 double client_tx = NTP64asDOUBLE(m->origts); 215 double client_tx = NTP64asDOUBLE(message->origts);
213 double peer_rx = NTP64asDOUBLE(m->rxts); 216 double peer_rx = NTP64asDOUBLE(message->rxts);
214 double peer_tx = NTP64asDOUBLE(m->txts); 217 double peer_tx = NTP64asDOUBLE(message->txts);
215 double client_rx = TVasDOUBLE((*t)); 218 double client_rx = TVasDOUBLE((*time_value));
216 return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx))); 219 return (((peer_tx - client_rx) + (peer_rx - client_tx)) / 2);
217} 220}
218 221
219/* print out a ntp packet in human readable/debuggable format */ 222/* print out a ntp packet in human readable/debuggable format */
220void print_ntp_message(const ntp_message *p) { 223void print_ntp_message(const ntp_message *message) {
221 struct timeval ref; 224 struct timeval ref;
222 struct timeval orig; 225 struct timeval orig;
223 struct timeval rx;
224 struct timeval tx;
225 226
226 NTP64toTV(p->refts, ref); 227 NTP64toTV(message->refts, ref);
227 NTP64toTV(p->origts, orig); 228 NTP64toTV(message->origts, orig);
228 NTP64toTV(p->rxts, rx);
229 NTP64toTV(p->txts, tx);
230 229
231 printf("packet contents:\n"); 230 printf("packet contents:\n");
232 printf("\tflags: 0x%.2x\n", p->flags); 231 printf("\tflags: 0x%.2x\n", message->flags);
233 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); 232 printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK);
234 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); 233 printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK);
235 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); 234 printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK);
236 printf("\tstratum = %d\n", p->stratum); 235 printf("\tstratum = %d\n", message->stratum);
237 printf("\tpoll = %g\n", pow(2, p->poll)); 236 printf("\tpoll = %g\n", pow(2, message->poll));
238 printf("\tprecision = %g\n", pow(2, p->precision)); 237 printf("\tprecision = %g\n", pow(2, message->precision));
239 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay)); 238 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(message->rtdelay));
240 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp)); 239 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(message->rtdisp));
241 printf("\trefid = %x\n", p->refid); 240 printf("\trefid = %x\n", message->refid);
242 printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts)); 241 printf("\trefts = %-.16g\n", NTP64asDOUBLE(message->refts));
243 printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts)); 242 printf("\torigts = %-.16g\n", NTP64asDOUBLE(message->origts));
244 printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts)); 243 printf("\trxts = %-.16g\n", NTP64asDOUBLE(message->rxts));
245 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); 244 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(message->txts));
246} 245}
247 246
248void setup_request(ntp_message *p) { 247void setup_request(ntp_message *message) {
249 memset(p, 0, sizeof(ntp_message)); 248 memset(message, 0, sizeof(ntp_message));
250 LI_SET(p->flags, LI_ALARM); 249 LI_SET(message->flags, LI_ALARM);
251 VN_SET(p->flags, 4); 250 VN_SET(message->flags, 4);
252 MODE_SET(p->flags, MODE_CLIENT); 251 MODE_SET(message->flags, MODE_CLIENT);
253 p->poll = 4; 252 message->poll = 4;
254 p->precision = (int8_t)0xfa; 253 message->precision = (int8_t)0xfa;
255 L16(p->rtdelay) = htons(1); 254 L16(message->rtdelay) = htons(1);
256 L16(p->rtdisp) = htons(1); 255 L16(message->rtdisp) = htons(1);
257 256
258 struct timeval t; 257 struct timeval t;
259 gettimeofday(&t, NULL); 258 gettimeofday(&t, NULL);
260 TVtoNTP64(t, p->txts); 259 TVtoNTP64(t, message->txts);
261} 260}
262 261
263/* select the "best" server from a list of servers, and return its index. 262/* select the "best" server from a list of servers, and return its index.
@@ -273,14 +272,16 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
273 * stratum 0 is for reference clocks so no NTP server should ever report 272 * stratum 0 is for reference clocks so no NTP server should ever report
274 * a stratum 0 */ 273 * a stratum 0 */
275 if (slist[cserver].stratum == 0) { 274 if (slist[cserver].stratum == 0) {
276 if (verbose) 275 if (verbose) {
277 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); 276 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
277 }
278 continue; 278 continue;
279 } 279 }
280 /* Sort out servers with error flags */ 280 /* Sort out servers with error flags */
281 if (LI(slist[cserver].flags) == LI_ALARM) { 281 if (LI(slist[cserver].flags) == LI_ALARM) {
282 if (verbose) 282 if (verbose) {
283 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); 283 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
284 }
284 continue; 285 continue;
285 } 286 }
286 287
@@ -322,7 +323,7 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
322 * we don't waste time sitting around waiting for single packets. 323 * we don't waste time sitting around waiting for single packets.
323 * - we also "manually" handle resolving host names and connecting, because 324 * - we also "manually" handle resolving host names and connecting, because
324 * we have to do it in a way that our lazy macros don't handle currently :( */ 325 * we have to do it in a way that our lazy macros don't handle currently :( */
325double offset_request(const char *host, int *status) { 326double offset_request(const char *host, const char *port, mp_state_enum *status, int time_offset) {
326 /* setup hints to only return results from getaddrinfo that we'd like */ 327 /* setup hints to only return results from getaddrinfo that we'd like */
327 struct addrinfo hints; 328 struct addrinfo hints;
328 memset(&hints, 0, sizeof(struct addrinfo)); 329 memset(&hints, 0, sizeof(struct addrinfo));
@@ -331,39 +332,44 @@ double offset_request(const char *host, int *status) {
331 hints.ai_socktype = SOCK_DGRAM; 332 hints.ai_socktype = SOCK_DGRAM;
332 333
333 /* fill in ai with the list of hosts resolved by the host name */ 334 /* fill in ai with the list of hosts resolved by the host name */
334 struct addrinfo *ai = NULL; 335 struct addrinfo *addresses = NULL;
335 int ga_result = getaddrinfo(host, port, &hints, &ai); 336 int ga_result = getaddrinfo(host, port, &hints, &addresses);
336 if (ga_result != 0) { 337 if (ga_result != 0) {
337 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result)); 338 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
338 } 339 }
339 340
340 /* count the number of returned hosts, and allocate stuff accordingly */ 341 /* count the number of returned hosts, and allocate stuff accordingly */
341 int num_hosts = 0; 342 size_t num_hosts = 0;
342 for (struct addrinfo *ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) { 343 for (struct addrinfo *ai_tmp = addresses; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
343 num_hosts++; 344 num_hosts++;
344 } 345 }
345 346
346 ntp_message *req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts); 347 ntp_message *req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts);
347 348
348 if (req == NULL) 349 if (req == NULL) {
349 die(STATE_UNKNOWN, "can not allocate ntp message array"); 350 die(STATE_UNKNOWN, "can not allocate ntp message array");
351 }
350 int *socklist = (int *)malloc(sizeof(int) * num_hosts); 352 int *socklist = (int *)malloc(sizeof(int) * num_hosts);
351 353
352 if (socklist == NULL) 354 if (socklist == NULL) {
353 die(STATE_UNKNOWN, "can not allocate socket array"); 355 die(STATE_UNKNOWN, "can not allocate socket array");
356 }
354 357
355 struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts); 358 struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts);
356 if (ufds == NULL) 359 if (ufds == NULL) {
357 die(STATE_UNKNOWN, "can not allocate socket array"); 360 die(STATE_UNKNOWN, "can not allocate socket array");
361 }
358 362
359 ntp_server_results *servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts); 363 ntp_server_results *servers =
360 if (servers == NULL) 364 (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts);
365 if (servers == NULL) {
361 die(STATE_UNKNOWN, "can not allocate server array"); 366 die(STATE_UNKNOWN, "can not allocate server array");
367 }
362 memset(servers, 0, sizeof(ntp_server_results) * num_hosts); 368 memset(servers, 0, sizeof(ntp_server_results) * num_hosts);
363 DBG(printf("Found %d peers to check\n", num_hosts)); 369 DBG(printf("Found %zu peers to check\n", num_hosts));
364 370
365 /* setup each socket for writing, and the corresponding struct pollfd */ 371 /* setup each socket for writing, and the corresponding struct pollfd */
366 struct addrinfo *ai_tmp = ai; 372 struct addrinfo *ai_tmp = addresses;
367 for (int i = 0; ai_tmp; i++) { 373 for (int i = 0; ai_tmp; i++) {
368 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); 374 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
369 if (socklist[i] == -1) { 375 if (socklist[i] == -1) {
@@ -389,7 +395,7 @@ double offset_request(const char *host, int *status) {
389 time_t start_ts = 0; 395 time_t start_ts = 0;
390 time_t now_time = 0; 396 time_t now_time = 0;
391 now_time = start_ts = time(NULL); 397 now_time = start_ts = time(NULL);
392 int servers_completed = 0; 398 size_t servers_completed = 0;
393 bool one_read = false; 399 bool one_read = false;
394 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) { 400 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) {
395 /* loop through each server and find each one which hasn't 401 /* loop through each server and find each one which hasn't
@@ -398,12 +404,14 @@ double offset_request(const char *host, int *status) {
398 * and update the "waiting" timestamp with the current time. */ 404 * and update the "waiting" timestamp with the current time. */
399 now_time = time(NULL); 405 now_time = time(NULL);
400 406
401 for (int i = 0; i < num_hosts; i++) { 407 for (size_t i = 0; i < num_hosts; i++) {
402 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) { 408 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) {
403 if (verbose && servers[i].waiting != 0) 409 if (verbose && servers[i].waiting != 0) {
404 printf("re-"); 410 printf("re-");
405 if (verbose) 411 }
406 printf("sending request to peer %d\n", i); 412 if (verbose) {
413 printf("sending request to peer %zu\n", i);
414 }
407 setup_request(&req[i]); 415 setup_request(&req[i]);
408 write(socklist[i], &req[i], sizeof(ntp_message)); 416 write(socklist[i], &req[i], sizeof(ntp_message));
409 servers[i].waiting = now_time; 417 servers[i].waiting = now_time;
@@ -419,10 +427,10 @@ double offset_request(const char *host, int *status) {
419 } 427 }
420 428
421 /* read from any sockets with pending data */ 429 /* read from any sockets with pending data */
422 for (int i = 0; servers_readable && i < num_hosts; i++) { 430 for (size_t i = 0; servers_readable && i < num_hosts; i++) {
423 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) { 431 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) {
424 if (verbose) { 432 if (verbose) {
425 printf("response from peer %d: ", i); 433 printf("response from peer %zu: ", i);
426 } 434 }
427 435
428 read(ufds[i].fd, &req[i], sizeof(ntp_message)); 436 read(ufds[i].fd, &req[i], sizeof(ntp_message));
@@ -442,14 +450,15 @@ double offset_request(const char *host, int *status) {
442 servers[i].flags = req[i].flags; 450 servers[i].flags = req[i].flags;
443 servers_readable--; 451 servers_readable--;
444 one_read = true; 452 one_read = true;
445 if (servers[i].num_responses == AVG_NUM) 453 if (servers[i].num_responses == AVG_NUM) {
446 servers_completed++; 454 servers_completed++;
455 }
447 } 456 }
448 } 457 }
449 /* lather, rinse, repeat. */ 458 /* lather, rinse, repeat. */
450 } 459 }
451 460
452 if (one_read == false) { 461 if (!one_read) {
453 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 462 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
454 } 463 }
455 464
@@ -467,21 +476,22 @@ double offset_request(const char *host, int *status) {
467 } 476 }
468 477
469 /* cleanup */ 478 /* cleanup */
470 for (int j = 0; j < num_hosts; j++) { 479 for (size_t j = 0; j < num_hosts; j++) {
471 close(socklist[j]); 480 close(socklist[j]);
472 } 481 }
473 free(socklist); 482 free(socklist);
474 free(ufds); 483 free(ufds);
475 free(servers); 484 free(servers);
476 free(req); 485 free(req);
477 freeaddrinfo(ai); 486 freeaddrinfo(addresses);
478 487
479 if (verbose) 488 if (verbose) {
480 printf("overall average offset: %.10g\n", avg_offset); 489 printf("overall average offset: %.10g\n", avg_offset);
490 }
481 return avg_offset; 491 return avg_offset;
482} 492}
483 493
484int process_arguments(int argc, char **argv) { 494check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
485 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 495 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
486 {"help", no_argument, 0, 'h'}, 496 {"help", no_argument, 0, 'h'},
487 {"verbose", no_argument, 0, 'v'}, 497 {"verbose", no_argument, 0, 'v'},
@@ -496,14 +506,24 @@ int process_arguments(int argc, char **argv) {
496 {"port", required_argument, 0, 'p'}, 506 {"port", required_argument, 0, 'p'},
497 {0, 0, 0, 0}}; 507 {0, 0, 0, 0}};
498 508
499 if (argc < 2) 509 if (argc < 2) {
500 usage("\n"); 510 usage("\n");
511 }
512
513 check_ntp_time_config_wrapper result = {
514 .errorcode = OK,
515 .config = check_ntp_time_config_init(),
516 };
517
518 char *owarn = "60";
519 char *ocrit = "120";
501 520
502 while (true) { 521 while (true) {
503 int option = 0; 522 int option = 0;
504 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); 523 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option);
505 if (option_char == -1 || option_char == EOF || option_char == 1) 524 if (option_char == -1 || option_char == EOF || option_char == 1) {
506 break; 525 break;
526 }
507 527
508 switch (option_char) { 528 switch (option_char) {
509 case 'h': 529 case 'h':
@@ -518,7 +538,7 @@ int process_arguments(int argc, char **argv) {
518 verbose++; 538 verbose++;
519 break; 539 break;
520 case 'q': 540 case 'q':
521 quiet = true; 541 result.config.quiet = true;
522 break; 542 break;
523 case 'w': 543 case 'w':
524 owarn = optarg; 544 owarn = optarg;
@@ -527,18 +547,19 @@ int process_arguments(int argc, char **argv) {
527 ocrit = optarg; 547 ocrit = optarg;
528 break; 548 break;
529 case 'H': 549 case 'H':
530 if (!is_host(optarg)) 550 if (!is_host(optarg)) {
531 usage2(_("Invalid hostname/address"), optarg); 551 usage2(_("Invalid hostname/address"), optarg);
532 server_address = strdup(optarg); 552 }
553 result.config.server_address = strdup(optarg);
533 break; 554 break;
534 case 'p': 555 case 'p':
535 port = strdup(optarg); 556 result.config.port = strdup(optarg);
536 break; 557 break;
537 case 't': 558 case 't':
538 socket_timeout = atoi(optarg); 559 socket_timeout = atoi(optarg);
539 break; 560 break;
540 case 'o': 561 case 'o':
541 time_offset = atoi(optarg); 562 result.config.time_offset = atoi(optarg);
542 break; 563 break;
543 case '4': 564 case '4':
544 address_family = AF_INET; 565 address_family = AF_INET;
@@ -557,16 +578,18 @@ int process_arguments(int argc, char **argv) {
557 } 578 }
558 } 579 }
559 580
560 if (server_address == NULL) { 581 if (result.config.server_address == NULL) {
561 usage4(_("Hostname was not supplied")); 582 usage4(_("Hostname was not supplied"));
562 } 583 }
563 584
564 return 0; 585 set_thresholds(&result.config.offset_thresholds, owarn, ocrit);
586
587 return result;
565} 588}
566 589
567char *perfd_offset(double offset) { 590char *perfd_offset(double offset, thresholds *offset_thresholds) {
568 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, 591 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
569 0); 592 offset_thresholds->critical->end, false, 0, false, 0);
570} 593}
571 594
572int main(int argc, char *argv[]) { 595int main(int argc, char *argv[]) {
@@ -577,10 +600,13 @@ int main(int argc, char *argv[]) {
577 /* Parse extra opts if any */ 600 /* Parse extra opts if any */
578 argv = np_extra_opts(&argc, argv, progname); 601 argv = np_extra_opts(&argc, argv, progname);
579 602
580 if (process_arguments(argc, argv) == ERROR) 603 check_ntp_time_config_wrapper tmp_config = process_arguments(argc, argv);
604
605 if (tmp_config.errorcode == ERROR) {
581 usage4(_("Could not parse arguments")); 606 usage4(_("Could not parse arguments"));
607 }
582 608
583 set_thresholds(&offset_thresholds, owarn, ocrit); 609 const check_ntp_time_config config = tmp_config.config;
584 610
585 /* initialize alarm signal handling */ 611 /* initialize alarm signal handling */
586 signal(SIGALRM, socket_timeout_alarm_handler); 612 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -588,13 +614,14 @@ int main(int argc, char *argv[]) {
588 /* set socket timeout */ 614 /* set socket timeout */
589 alarm(socket_timeout); 615 alarm(socket_timeout);
590 616
591 int offset_result = STATE_OK; 617 mp_state_enum offset_result = STATE_OK;
592 int result = STATE_OK; 618 mp_state_enum result = STATE_OK;
593 double offset = offset_request(server_address, &offset_result); 619 double offset =
620 offset_request(config.server_address, config.port, &offset_result, config.time_offset);
594 if (offset_result == STATE_UNKNOWN) { 621 if (offset_result == STATE_UNKNOWN) {
595 result = ((!quiet) ? STATE_UNKNOWN : STATE_CRITICAL); 622 result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
596 } else { 623 } else {
597 result = get_status(fabs(offset), offset_thresholds); 624 result = get_status(fabs(offset), config.offset_thresholds);
598 } 625 }
599 626
600 char *result_line; 627 char *result_line;
@@ -619,13 +646,14 @@ int main(int argc, char *argv[]) {
619 xasprintf(&perfdata_line, ""); 646 xasprintf(&perfdata_line, "");
620 } else { 647 } else {
621 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 648 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
622 xasprintf(&perfdata_line, "%s", perfd_offset(offset)); 649 xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds));
623 } 650 }
624 printf("%s|%s\n", result_line, perfdata_line); 651 printf("%s|%s\n", result_line, perfdata_line);
625 652
626 if (server_address != NULL) 653 if (config.server_address != NULL) {
627 free(server_address); 654 free(config.server_address);
628 return result; 655 }
656 exit(result);
629} 657}
630 658
631void print_help(void) { 659void print_help(void) {
@@ -677,5 +705,6 @@ void print_help(void) {
677 705
678void print_usage(void) { 706void print_usage(void) {
679 printf("%s\n", _("Usage:")); 707 printf("%s\n", _("Usage:"));
680 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n", progname); 708 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n",
709 progname);
681} 710}
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h
new file mode 100644
index 00000000..99dabbbd
--- /dev/null
+++ b/plugins/check_ntp_time.d/config.h
@@ -0,0 +1,28 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7typedef struct {
8 char *server_address;
9 char *port;
10
11 bool quiet;
12 int time_offset;
13
14 thresholds *offset_thresholds;
15} check_ntp_time_config;
16
17check_ntp_time_config check_ntp_time_config_init() {
18 check_ntp_time_config tmp = {
19 .server_address = NULL,
20 .port = "123",
21
22 .quiet = false,
23 .time_offset = 0,
24
25 .offset_thresholds = NULL,
26 };
27 return tmp;
28}
diff --git a/plugins/check_nwstat.c b/plugins/check_nwstat.c
deleted file mode 100644
index 176dfbc8..00000000
--- a/plugins/check_nwstat.c
+++ /dev/null
@@ -1,1527 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_nwstat plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains the check_nwstat plugin
11 *
12 * This plugin attempts to contact the MRTGEXT NLM running on a
13 * Novell server to gather the requested system information.
14 *
15 *
16 * This program is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 *
29 *
30 *****************************************************************************/
31
32const char *progname = "check_nwstat";
33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h"
37#include "netutils.h"
38#include "utils.h"
39
40enum checkvar {
41 NONE,
42 LOAD1, /* check 1 minute CPU load */
43 LOAD5, /* check 5 minute CPU load */
44 LOAD15, /* check 15 minute CPU load */
45 CONNS, /* check number of connections */
46 VPF, /* check % free space on volume */
47 VMF, /* check MB free space on volume */
48 VMU, /* check MB used space on volume */
49 VPU, /* check % used space on volume */
50 VMP, /* check MB purgeable space on volume */
51 VKF, /* check KB free space on volume */
52 LTCH, /* check long-term cache hit percentage */
53 CBUFF, /* check total cache buffers */
54 CDBUFF, /* check dirty cache buffers */
55 LRUM, /* check LRU sitting time in minutes */
56 DSDB, /* check to see if DS Database is open */
57 LOGINS, /* check to see if logins are enabled */
58 NRMH, /* check to see NRM Health Status */
59 PUPRB, /* check % of used packet receive buffers */
60 UPRB, /* check used packet receive buffers */
61 SAPENTRIES, /* check SAP entries */
62 OFILES, /* check number of open files */
63 VKP, /* check KB purgeable space on volume */
64 VPP, /* check % purgeable space on volume */
65 VKNP, /* check KB not yet purgeable space on volume */
66 VPNP, /* check % not yet purgeable space on volume */
67 ABENDS, /* check abended thread count */
68 CSPROCS, /* check number of current service processes */
69 TSYNC, /* check timesync status 0=no 1=yes in sync to the network */
70 LRUS, /* check LRU sitting time in seconds */
71 DCB, /* check dirty cache buffers as a percentage of the total */
72 TCB, /* check total cache buffers as a percentage of the original */
73 DSVER, /* check NDS version */
74 UPTIME, /* check server uptime */
75 NLM, /* check NLM loaded */
76 NRMP, /* check NRM Process Values */
77 NRMM, /* check NRM Memory Values */
78 NRMS, /* check NRM Values */
79 NSS1, /* check Statistics from _Admin:Manage_NSS\GeneralStats.xml */
80 NSS2, /* check Statistics from _Admin:Manage_NSS\BufferCache.xml */
81 NSS3, /* check statistics from _Admin:Manage_NSS\NameCache.xml */
82 NSS4, /* check statistics from _Admin:Manage_NSS\FileStats.xml */
83 NSS5, /* check statistics from _Admin:Manage_NSS\ObjectCache.xml */
84 NSS6, /* check statistics from _Admin:Manage_NSS\Thread.xml */
85 NSS7 /* check statistics from _Admin:Manage_NSS\AuthorizationCache.xml */
86};
87
88enum {
89 PORT = 9999
90};
91
92static char *server_address = NULL;
93static char *volume_name = NULL;
94static char *nlm_name = NULL;
95static char *nrmp_name = NULL;
96static char *nrmm_name = NULL;
97static char *nrms_name = NULL;
98static char *nss1_name = NULL;
99static char *nss2_name = NULL;
100static char *nss3_name = NULL;
101static char *nss4_name = NULL;
102static char *nss5_name = NULL;
103static char *nss6_name = NULL;
104static char *nss7_name = NULL;
105static int server_port = PORT;
106static unsigned long warning_value = 0L;
107static unsigned long critical_value = 0L;
108static bool check_warning_value = false;
109static bool check_critical_value = false;
110static bool check_netware_version = false;
111static enum checkvar vars_to_check = NONE;
112static int sap_number = -1;
113
114static int process_arguments(int /*argc*/, char ** /*argv*/);
115static void print_help(void);
116void print_usage(void);
117
118int main(int argc, char **argv) {
119 int result = STATE_UNKNOWN;
120 int sd;
121 char *send_buffer = NULL;
122 char recv_buffer[MAX_INPUT_BUFFER];
123 char *output_message = NULL;
124 char *temp_buffer = NULL;
125 char *netware_version = NULL;
126
127 int time_sync_status = 0;
128 int nrm_health_status = 0;
129 unsigned long total_cache_buffers = 0;
130 unsigned long dirty_cache_buffers = 0;
131 unsigned long open_files = 0;
132 unsigned long abended_threads = 0;
133 unsigned long max_service_processes = 0;
134 unsigned long current_service_processes = 0;
135 unsigned long free_disk_space = 0L;
136 unsigned long nrmp_value = 0L;
137 unsigned long nrmm_value = 0L;
138 unsigned long nrms_value = 0L;
139 unsigned long nss1_value = 0L;
140 unsigned long nss2_value = 0L;
141 unsigned long nss3_value = 0L;
142 unsigned long nss4_value = 0L;
143 unsigned long nss5_value = 0L;
144 unsigned long nss6_value = 0L;
145 unsigned long nss7_value = 0L;
146 unsigned long total_disk_space = 0L;
147 unsigned long used_disk_space = 0L;
148 unsigned long percent_used_disk_space = 0L;
149 unsigned long purgeable_disk_space = 0L;
150 unsigned long non_purgeable_disk_space = 0L;
151 unsigned long percent_free_space = 0;
152 unsigned long percent_purgeable_space = 0;
153 unsigned long percent_non_purgeable_space = 0;
154 unsigned long current_connections = 0L;
155 unsigned long utilization = 0L;
156 unsigned long cache_hits = 0;
157 unsigned long cache_buffers = 0L;
158 unsigned long lru_time = 0L;
159 unsigned long max_packet_receive_buffers = 0;
160 unsigned long used_packet_receive_buffers = 0;
161 unsigned long percent_used_packet_receive_buffers = 0L;
162 unsigned long sap_entries = 0;
163 char uptime[MAX_INPUT_BUFFER];
164
165 setlocale(LC_ALL, "");
166 bindtextdomain(PACKAGE, LOCALEDIR);
167 textdomain(PACKAGE);
168
169 /* Parse extra opts if any */
170 argv = np_extra_opts(&argc, argv, progname);
171
172 if (process_arguments(argc, argv) == ERROR)
173 usage4(_("Could not parse arguments"));
174
175 /* initialize alarm signal handling */
176 signal(SIGALRM, socket_timeout_alarm_handler);
177
178 /* set socket timeout */
179 alarm(socket_timeout);
180
181 /* open connection */
182 my_tcp_connect(server_address, server_port, &sd);
183
184 /* get OS version string */
185 if (check_netware_version) {
186 send_buffer = strdup("S19\r\n");
187 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
188 if (result != STATE_OK)
189 return result;
190 if (!strcmp(recv_buffer, "-1\n"))
191 netware_version = strdup("");
192 else {
193 recv_buffer[strlen(recv_buffer) - 1] = 0;
194 xasprintf(&netware_version, _("NetWare %s: "), recv_buffer);
195 }
196 } else
197 netware_version = strdup("");
198
199 /* check CPU load */
200 if (vars_to_check == LOAD1 || vars_to_check == LOAD5 || vars_to_check == LOAD15) {
201
202 switch (vars_to_check) {
203 case LOAD1:
204 temp_buffer = strdup("1");
205 break;
206 case LOAD5:
207 temp_buffer = strdup("5");
208 break;
209 default:
210 temp_buffer = strdup("15");
211 break;
212 }
213
214 close(sd);
215 my_tcp_connect(server_address, server_port, &sd);
216
217 xasprintf(&send_buffer, "UTIL%s\r\n", temp_buffer);
218 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
219 if (result != STATE_OK)
220 return result;
221 utilization = strtoul(recv_buffer, NULL, 10);
222
223 close(sd);
224 my_tcp_connect(server_address, server_port, &sd);
225
226 send_buffer = strdup("UPTIME\r\n");
227 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
228 if (result != STATE_OK)
229 return result;
230 recv_buffer[strlen(recv_buffer) - 1] = 0;
231 sprintf(uptime, _("Up %s,"), recv_buffer);
232
233 if (check_critical_value && utilization >= critical_value)
234 result = STATE_CRITICAL;
235 else if (check_warning_value && utilization >= warning_value)
236 result = STATE_WARNING;
237
238 xasprintf(&output_message, _("Load %s - %s %s-min load average = %lu%%|load%s=%lu;%lu;%lu;0;100"), state_text(result), uptime,
239 temp_buffer, utilization, temp_buffer, utilization, warning_value, critical_value);
240
241 /* check number of user connections */
242 } else if (vars_to_check == CONNS) {
243
244 close(sd);
245 my_tcp_connect(server_address, server_port, &sd);
246
247 send_buffer = strdup("CONNECT\r\n");
248 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
249 if (result != STATE_OK)
250 return result;
251 current_connections = strtoul(recv_buffer, NULL, 10);
252
253 if (check_critical_value && current_connections >= critical_value)
254 result = STATE_CRITICAL;
255 else if (check_warning_value && current_connections >= warning_value)
256 result = STATE_WARNING;
257
258 xasprintf(&output_message, _("Conns %s - %lu current connections|Conns=%lu;%lu;%lu;;"), state_text(result), current_connections,
259 current_connections, warning_value, critical_value);
260
261 /* check % long term cache hits */
262 } else if (vars_to_check == LTCH) {
263
264 close(sd);
265 my_tcp_connect(server_address, server_port, &sd);
266
267 send_buffer = strdup("S1\r\n");
268 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
269 if (result != STATE_OK)
270 return result;
271 cache_hits = atoi(recv_buffer);
272
273 if (check_critical_value && cache_hits <= critical_value)
274 result = STATE_CRITICAL;
275 else if (check_warning_value && cache_hits <= warning_value)
276 result = STATE_WARNING;
277
278 xasprintf(&output_message, _("%s: Long term cache hits = %lu%%"), state_text(result), cache_hits);
279
280 /* check cache buffers */
281 } else if (vars_to_check == CBUFF) {
282
283 close(sd);
284 my_tcp_connect(server_address, server_port, &sd);
285
286 send_buffer = strdup("S2\r\n");
287 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
288 if (result != STATE_OK)
289 return result;
290 cache_buffers = strtoul(recv_buffer, NULL, 10);
291
292 if (check_critical_value && cache_buffers <= critical_value)
293 result = STATE_CRITICAL;
294 else if (check_warning_value && cache_buffers <= warning_value)
295 result = STATE_WARNING;
296
297 xasprintf(&output_message, _("%s: Total cache buffers = %lu|Cachebuffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers,
298 cache_buffers, warning_value, critical_value);
299
300 /* check dirty cache buffers */
301 } else if (vars_to_check == CDBUFF) {
302
303 close(sd);
304 my_tcp_connect(server_address, server_port, &sd);
305
306 send_buffer = strdup("S3\r\n");
307 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
308 if (result != STATE_OK)
309 return result;
310 cache_buffers = strtoul(recv_buffer, NULL, 10);
311
312 if (check_critical_value && cache_buffers >= critical_value)
313 result = STATE_CRITICAL;
314 else if (check_warning_value && cache_buffers >= warning_value)
315 result = STATE_WARNING;
316
317 xasprintf(&output_message, _("%s: Dirty cache buffers = %lu|Dirty-Cache-Buffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers,
318 cache_buffers, warning_value, critical_value);
319
320 /* check LRU sitting time in minutes */
321 } else if (vars_to_check == LRUM) {
322
323 close(sd);
324 my_tcp_connect(server_address, server_port, &sd);
325
326 send_buffer = strdup("S5\r\n");
327 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
328 if (result != STATE_OK)
329 return result;
330 lru_time = strtoul(recv_buffer, NULL, 10);
331
332 if (check_critical_value && lru_time <= critical_value)
333 result = STATE_CRITICAL;
334 else if (check_warning_value && lru_time <= warning_value)
335 result = STATE_WARNING;
336
337 xasprintf(&output_message, _("%s: LRU sitting time = %lu minutes"), state_text(result), lru_time);
338
339 /* check KB free space on volume */
340 } else if (vars_to_check == VKF) {
341
342 close(sd);
343 my_tcp_connect(server_address, server_port, &sd);
344
345 xasprintf(&send_buffer, "VKF%s\r\n", volume_name);
346 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
347 if (result != STATE_OK)
348 return result;
349
350 if (!strcmp(recv_buffer, "-1\n")) {
351 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
352 result = STATE_CRITICAL;
353 } else {
354 free_disk_space = strtoul(recv_buffer, NULL, 10);
355 if (check_critical_value && free_disk_space <= critical_value)
356 result = STATE_CRITICAL;
357 else if (check_warning_value && free_disk_space <= warning_value)
358 result = STATE_WARNING;
359 xasprintf(&output_message, _("%s%lu KB free on volume %s|KBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
360 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
361 }
362
363 /* check MB free space on volume */
364 } else if (vars_to_check == VMF) {
365
366 xasprintf(&send_buffer, "VMF%s\r\n", volume_name);
367 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
368 if (result != STATE_OK)
369 return result;
370
371 if (!strcmp(recv_buffer, "-1\n")) {
372 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
373 result = STATE_CRITICAL;
374 } else {
375 free_disk_space = strtoul(recv_buffer, NULL, 10);
376 if (check_critical_value && free_disk_space <= critical_value)
377 result = STATE_CRITICAL;
378 else if (check_warning_value && free_disk_space <= warning_value)
379 result = STATE_WARNING;
380 xasprintf(&output_message, _("%s%lu MB free on volume %s|MBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
381 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
382 }
383 /* check MB used space on volume */
384 } else if (vars_to_check == VMU) {
385
386 xasprintf(&send_buffer, "VMU%s\r\n", volume_name);
387 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
388 if (result != STATE_OK)
389 return result;
390
391 if (!strcmp(recv_buffer, "-1\n")) {
392 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
393 result = STATE_CRITICAL;
394 } else {
395 free_disk_space = strtoul(recv_buffer, NULL, 10);
396 if (check_critical_value && free_disk_space <= critical_value)
397 result = STATE_CRITICAL;
398 else if (check_warning_value && free_disk_space <= warning_value)
399 result = STATE_WARNING;
400 xasprintf(&output_message, _("%s%lu MB used on volume %s|MBUsed%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
401 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
402 }
403 /* check % used space on volume */
404 } else if (vars_to_check == VPU) {
405 close(sd);
406 my_tcp_connect(server_address, server_port, &sd);
407
408 asprintf(&send_buffer, "VMU%s\r\n", volume_name);
409 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
410
411 if (result != STATE_OK)
412 return result;
413
414 if (!strcmp(recv_buffer, "-1\n")) {
415 asprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
416 result = STATE_CRITICAL;
417
418 } else {
419 used_disk_space = strtoul(recv_buffer, NULL, 10);
420 close(sd);
421 my_tcp_connect(server_address, server_port, &sd);
422 /* get total volume in MB */
423 asprintf(&send_buffer, "VMS%s\r\n", volume_name);
424 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
425 if (result != STATE_OK)
426 return result;
427 total_disk_space = strtoul(recv_buffer, NULL, 10);
428 /* calculate percent used on volume */
429 percent_used_disk_space = (unsigned long)(((double)used_disk_space / (double)total_disk_space) * 100.0);
430
431 if (check_critical_value && percent_used_disk_space >= critical_value)
432 result = STATE_CRITICAL;
433 else if (check_warning_value && percent_used_disk_space >= warning_value)
434 result = STATE_WARNING;
435
436 asprintf(&output_message, _("%lu MB (%lu%%) used on volume %s - total %lu MB|Used space in percent on %s=%lu;%lu;%lu;0;100"),
437 used_disk_space, percent_used_disk_space, volume_name, total_disk_space, volume_name, percent_used_disk_space,
438 warning_value, critical_value);
439 }
440
441 /* check % free space on volume */
442 } else if (vars_to_check == VPF) {
443
444 close(sd);
445 my_tcp_connect(server_address, server_port, &sd);
446
447 xasprintf(&send_buffer, "VKF%s\r\n", volume_name);
448 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
449 if (result != STATE_OK)
450 return result;
451
452 if (!strcmp(recv_buffer, "-1\n")) {
453
454 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
455 result = STATE_CRITICAL;
456
457 } else {
458
459 free_disk_space = strtoul(recv_buffer, NULL, 10);
460
461 close(sd);
462 my_tcp_connect(server_address, server_port, &sd);
463
464 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
465 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
466 if (result != STATE_OK)
467 return result;
468 total_disk_space = strtoul(recv_buffer, NULL, 10);
469
470 percent_free_space = (unsigned long)(((double)free_disk_space / (double)total_disk_space) * 100.0);
471
472 if (check_critical_value && percent_free_space <= critical_value)
473 result = STATE_CRITICAL;
474 else if (check_warning_value && percent_free_space <= warning_value)
475 result = STATE_WARNING;
476 free_disk_space /= 1024;
477 total_disk_space /= 1024;
478 xasprintf(&output_message, _("%lu MB (%lu%%) free on volume %s - total %lu MB|FreeMB%s=%lu;%lu;%lu;0;100"), free_disk_space,
479 percent_free_space, volume_name, total_disk_space, volume_name, percent_free_space, warning_value, critical_value);
480 }
481
482 /* check to see if DS Database is open or closed */
483 } else if (vars_to_check == DSDB) {
484
485 close(sd);
486 my_tcp_connect(server_address, server_port, &sd);
487
488 send_buffer = strdup("S11\r\n");
489 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
490 if (result != STATE_OK)
491 return result;
492 if (atoi(recv_buffer) == 1)
493 result = STATE_OK;
494 else
495 result = STATE_WARNING;
496
497 close(sd);
498 my_tcp_connect(server_address, server_port, &sd);
499
500 send_buffer = strdup("S13\r\n");
501 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
502 temp_buffer = strtok(recv_buffer, "\r\n");
503
504 xasprintf(&output_message, _("Directory Services Database is %s (DS version %s)"), (result == STATE_OK) ? "open" : "closed",
505 temp_buffer);
506
507 /* check to see if logins are enabled */
508 } else if (vars_to_check == LOGINS) {
509
510 close(sd);
511 my_tcp_connect(server_address, server_port, &sd);
512
513 send_buffer = strdup("S12\r\n");
514 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
515 if (result != STATE_OK)
516 return result;
517 if (atoi(recv_buffer) == 1)
518 result = STATE_OK;
519 else
520 result = STATE_WARNING;
521
522 xasprintf(&output_message, _("Logins are %s"), (result == STATE_OK) ? _("enabled") : _("disabled"));
523
524 /* check NRM Health Status Summary*/
525 } else if (vars_to_check == NRMH) {
526
527 xasprintf(&send_buffer, "NRMH\r\n");
528 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
529 if (result != STATE_OK)
530 return result;
531
532 nrm_health_status = atoi(recv_buffer);
533
534 if (nrm_health_status == 2) {
535 result = STATE_OK;
536 xasprintf(&output_message, _("CRITICAL - NRM Status is bad!"));
537 } else {
538 if (nrm_health_status == 1) {
539 result = STATE_WARNING;
540 xasprintf(&output_message, _("Warning - NRM Status is suspect!"));
541 }
542
543 xasprintf(&output_message, _("OK - NRM Status is good!"));
544 }
545
546 /* check packet receive buffers */
547 } else if (vars_to_check == UPRB || vars_to_check == PUPRB) {
548
549 close(sd);
550 my_tcp_connect(server_address, server_port, &sd);
551
552 xasprintf(&send_buffer, "S15\r\n");
553 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
554 if (result != STATE_OK)
555 return result;
556
557 used_packet_receive_buffers = atoi(recv_buffer);
558
559 close(sd);
560 my_tcp_connect(server_address, server_port, &sd);
561
562 xasprintf(&send_buffer, "S16\r\n");
563 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
564 if (result != STATE_OK)
565 return result;
566
567 max_packet_receive_buffers = atoi(recv_buffer);
568
569 percent_used_packet_receive_buffers =
570 (unsigned long)(((double)used_packet_receive_buffers / (double)max_packet_receive_buffers) * 100.0);
571
572 if (vars_to_check == UPRB) {
573 if (check_critical_value && used_packet_receive_buffers >= critical_value)
574 result = STATE_CRITICAL;
575 else if (check_warning_value && used_packet_receive_buffers >= warning_value)
576 result = STATE_WARNING;
577 } else {
578 if (check_critical_value && percent_used_packet_receive_buffers >= critical_value)
579 result = STATE_CRITICAL;
580 else if (check_warning_value && percent_used_packet_receive_buffers >= warning_value)
581 result = STATE_WARNING;
582 }
583
584 xasprintf(&output_message, _("%lu of %lu (%lu%%) packet receive buffers used"), used_packet_receive_buffers,
585 max_packet_receive_buffers, percent_used_packet_receive_buffers);
586
587 /* check SAP table entries */
588 } else if (vars_to_check == SAPENTRIES) {
589
590 close(sd);
591 my_tcp_connect(server_address, server_port, &sd);
592
593 if (sap_number == -1)
594 xasprintf(&send_buffer, "S9\r\n");
595 else
596 xasprintf(&send_buffer, "S9.%d\r\n", sap_number);
597 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
598 if (result != STATE_OK)
599 return result;
600
601 sap_entries = atoi(recv_buffer);
602
603 if (check_critical_value && sap_entries >= critical_value)
604 result = STATE_CRITICAL;
605 else if (check_warning_value && sap_entries >= warning_value)
606 result = STATE_WARNING;
607
608 if (sap_number == -1)
609 xasprintf(&output_message, _("%lu entries in SAP table"), sap_entries);
610 else
611 xasprintf(&output_message, _("%lu entries in SAP table for SAP type %d"), sap_entries, sap_number);
612
613 /* check KB purgeable space on volume */
614 } else if (vars_to_check == VKP) {
615
616 close(sd);
617 my_tcp_connect(server_address, server_port, &sd);
618
619 xasprintf(&send_buffer, "VKP%s\r\n", volume_name);
620 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
621 if (result != STATE_OK)
622 return result;
623
624 if (!strcmp(recv_buffer, "-1\n")) {
625 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
626 result = STATE_CRITICAL;
627 } else {
628 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
629 if (check_critical_value && purgeable_disk_space >= critical_value)
630 result = STATE_CRITICAL;
631 else if (check_warning_value && purgeable_disk_space >= warning_value)
632 result = STATE_WARNING;
633 xasprintf(&output_message, _("%s%lu KB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
634 purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value);
635 }
636 /* check MB purgeable space on volume */
637 } else if (vars_to_check == VMP) {
638
639 xasprintf(&send_buffer, "VMP%s\r\n", volume_name);
640 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
641 if (result != STATE_OK)
642 return result;
643
644 if (!strcmp(recv_buffer, "-1\n")) {
645 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
646 result = STATE_CRITICAL;
647 } else {
648 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
649 if (check_critical_value && purgeable_disk_space >= critical_value)
650 result = STATE_CRITICAL;
651 else if (check_warning_value && purgeable_disk_space >= warning_value)
652 result = STATE_WARNING;
653 xasprintf(&output_message, _("%s%lu MB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
654 purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value);
655 }
656
657 /* check % purgeable space on volume */
658 } else if (vars_to_check == VPP) {
659
660 close(sd);
661 my_tcp_connect(server_address, server_port, &sd);
662
663 xasprintf(&send_buffer, "VKP%s\r\n", volume_name);
664 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
665 if (result != STATE_OK)
666 return result;
667
668 if (!strcmp(recv_buffer, "-1\n")) {
669
670 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
671 result = STATE_CRITICAL;
672
673 } else {
674
675 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
676
677 close(sd);
678 my_tcp_connect(server_address, server_port, &sd);
679
680 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
681 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
682 if (result != STATE_OK)
683 return result;
684 total_disk_space = strtoul(recv_buffer, NULL, 10);
685
686 percent_purgeable_space = (unsigned long)(((double)purgeable_disk_space / (double)total_disk_space) * 100.0);
687
688 if (check_critical_value && percent_purgeable_space >= critical_value)
689 result = STATE_CRITICAL;
690 else if (check_warning_value && percent_purgeable_space >= warning_value)
691 result = STATE_WARNING;
692 purgeable_disk_space /= 1024;
693 xasprintf(&output_message, _("%lu MB (%lu%%) purgeable on volume %s|Purgeable%s=%lu;%lu;%lu;0;100"), purgeable_disk_space,
694 percent_purgeable_space, volume_name, volume_name, percent_purgeable_space, warning_value, critical_value);
695 }
696
697 /* check KB not yet purgeable space on volume */
698 } else if (vars_to_check == VKNP) {
699
700 close(sd);
701 my_tcp_connect(server_address, server_port, &sd);
702
703 xasprintf(&send_buffer, "VKNP%s\r\n", volume_name);
704 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
705 if (result != STATE_OK)
706 return result;
707
708 if (!strcmp(recv_buffer, "-1\n")) {
709 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
710 result = STATE_CRITICAL;
711 } else {
712 non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
713 if (check_critical_value && non_purgeable_disk_space >= critical_value)
714 result = STATE_CRITICAL;
715 else if (check_warning_value && non_purgeable_disk_space >= warning_value)
716 result = STATE_WARNING;
717 xasprintf(&output_message, _("%s%lu KB not yet purgeable on volume %s"), (result == STATE_OK) ? "" : _("Only "),
718 non_purgeable_disk_space, volume_name);
719 }
720
721 /* check % not yet purgeable space on volume */
722 } else if (vars_to_check == VPNP) {
723
724 close(sd);
725 my_tcp_connect(server_address, server_port, &sd);
726
727 xasprintf(&send_buffer, "VKNP%s\r\n", volume_name);
728 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
729 if (result != STATE_OK)
730 return result;
731
732 if (!strcmp(recv_buffer, "-1\n")) {
733
734 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
735 result = STATE_CRITICAL;
736
737 } else {
738
739 non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
740
741 close(sd);
742 my_tcp_connect(server_address, server_port, &sd);
743
744 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
745 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
746 if (result != STATE_OK)
747 return result;
748 total_disk_space = strtoul(recv_buffer, NULL, 10);
749
750 percent_non_purgeable_space = (unsigned long)(((double)non_purgeable_disk_space / (double)total_disk_space) * 100.0);
751
752 if (check_critical_value && percent_non_purgeable_space >= critical_value)
753 result = STATE_CRITICAL;
754 else if (check_warning_value && percent_non_purgeable_space >= warning_value)
755 result = STATE_WARNING;
756 purgeable_disk_space /= 1024;
757 xasprintf(&output_message, _("%lu MB (%lu%%) not yet purgeable on volume %s"), non_purgeable_disk_space,
758 percent_non_purgeable_space, volume_name);
759 }
760
761 /* check # of open files */
762 } else if (vars_to_check == OFILES) {
763
764 close(sd);
765 my_tcp_connect(server_address, server_port, &sd);
766
767 xasprintf(&send_buffer, "S18\r\n");
768 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
769 if (result != STATE_OK)
770 return result;
771
772 open_files = atoi(recv_buffer);
773
774 if (check_critical_value && open_files >= critical_value)
775 result = STATE_CRITICAL;
776 else if (check_warning_value && open_files >= warning_value)
777 result = STATE_WARNING;
778
779 xasprintf(&output_message, _("%lu open files|Openfiles=%lu;%lu;%lu;0,0"), open_files, open_files, warning_value, critical_value);
780
781 /* check # of abended threads (Netware > 5.x only) */
782 } else if (vars_to_check == ABENDS) {
783
784 close(sd);
785 my_tcp_connect(server_address, server_port, &sd);
786
787 xasprintf(&send_buffer, "S17\r\n");
788 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
789 if (result != STATE_OK)
790 return result;
791
792 abended_threads = atoi(recv_buffer);
793
794 if (check_critical_value && abended_threads >= critical_value)
795 result = STATE_CRITICAL;
796 else if (check_warning_value && abended_threads >= warning_value)
797 result = STATE_WARNING;
798
799 xasprintf(&output_message, _("%lu abended threads|Abends=%lu;%lu;%lu;;"), abended_threads, abended_threads, warning_value,
800 critical_value);
801
802 /* check # of current service processes (Netware 5.x only) */
803 } else if (vars_to_check == CSPROCS) {
804
805 close(sd);
806 my_tcp_connect(server_address, server_port, &sd);
807
808 xasprintf(&send_buffer, "S20\r\n");
809 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
810 if (result != STATE_OK)
811 return result;
812
813 max_service_processes = atoi(recv_buffer);
814
815 close(sd);
816 my_tcp_connect(server_address, server_port, &sd);
817
818 xasprintf(&send_buffer, "S21\r\n");
819 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
820 if (result != STATE_OK)
821 return result;
822
823 current_service_processes = atoi(recv_buffer);
824
825 if (check_critical_value && current_service_processes >= critical_value)
826 result = STATE_CRITICAL;
827 else if (check_warning_value && current_service_processes >= warning_value)
828 result = STATE_WARNING;
829
830 xasprintf(&output_message, _("%lu current service processes (%lu max)|Processes=%lu;%lu;%lu;0;%lu"), current_service_processes,
831 max_service_processes, current_service_processes, warning_value, critical_value, max_service_processes);
832
833 /* check # Timesync Status */
834 } else if (vars_to_check == TSYNC) {
835
836 close(sd);
837 my_tcp_connect(server_address, server_port, &sd);
838
839 xasprintf(&send_buffer, "S22\r\n");
840 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
841 if (result != STATE_OK)
842 return result;
843
844 time_sync_status = atoi(recv_buffer);
845
846 if (time_sync_status == 0) {
847 result = STATE_CRITICAL;
848 xasprintf(&output_message, _("CRITICAL - Time not in sync with network!"));
849 } else {
850 xasprintf(&output_message, _("OK - Time in sync with network!"));
851 }
852
853 /* check LRU sitting time in secondss */
854 } else if (vars_to_check == LRUS) {
855
856 close(sd);
857 my_tcp_connect(server_address, server_port, &sd);
858
859 send_buffer = strdup("S4\r\n");
860 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
861 if (result != STATE_OK)
862 return result;
863 lru_time = strtoul(recv_buffer, NULL, 10);
864
865 if (check_critical_value && lru_time <= critical_value)
866 result = STATE_CRITICAL;
867 else if (check_warning_value && lru_time <= warning_value)
868 result = STATE_WARNING;
869 xasprintf(&output_message, _("LRU sitting time = %lu seconds"), lru_time);
870
871 /* check % dirty cacheobuffers as a percentage of the total*/
872 } else if (vars_to_check == DCB) {
873
874 close(sd);
875 my_tcp_connect(server_address, server_port, &sd);
876
877 send_buffer = strdup("S6\r\n");
878 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
879 if (result != STATE_OK)
880 return result;
881 dirty_cache_buffers = atoi(recv_buffer);
882
883 if (check_critical_value && dirty_cache_buffers <= critical_value)
884 result = STATE_CRITICAL;
885 else if (check_warning_value && dirty_cache_buffers <= warning_value)
886 result = STATE_WARNING;
887 xasprintf(&output_message, _("Dirty cache buffers = %lu%% of the total|DCB=%lu;%lu;%lu;0;100"), dirty_cache_buffers,
888 dirty_cache_buffers, warning_value, critical_value);
889
890 /* check % total cache buffers as a percentage of the original*/
891 } else if (vars_to_check == TCB) {
892
893 close(sd);
894 my_tcp_connect(server_address, server_port, &sd);
895
896 send_buffer = strdup("S7\r\n");
897 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
898 if (result != STATE_OK)
899 return result;
900 total_cache_buffers = atoi(recv_buffer);
901
902 if (check_critical_value && total_cache_buffers <= critical_value)
903 result = STATE_CRITICAL;
904 else if (check_warning_value && total_cache_buffers <= warning_value)
905 result = STATE_WARNING;
906 xasprintf(&output_message, _("Total cache buffers = %lu%% of the original|TCB=%lu;%lu;%lu;0;100"), total_cache_buffers,
907 total_cache_buffers, warning_value, critical_value);
908
909 } else if (vars_to_check == DSVER) {
910
911 close(sd);
912 my_tcp_connect(server_address, server_port, &sd);
913
914 xasprintf(&send_buffer, "S13\r\n");
915 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
916 if (result != STATE_OK)
917 return result;
918
919 recv_buffer[strlen(recv_buffer) - 1] = 0;
920
921 xasprintf(&output_message, _("NDS Version %s"), recv_buffer);
922
923 } else if (vars_to_check == UPTIME) {
924
925 close(sd);
926 my_tcp_connect(server_address, server_port, &sd);
927
928 xasprintf(&send_buffer, "UPTIME\r\n");
929 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
930 if (result != STATE_OK)
931 return result;
932
933 recv_buffer[sizeof(recv_buffer) - 1] = 0;
934 recv_buffer[strlen(recv_buffer) - 1] = 0;
935
936 xasprintf(&output_message, _("Up %s"), recv_buffer);
937
938 } else if (vars_to_check == NLM) {
939
940 close(sd);
941 my_tcp_connect(server_address, server_port, &sd);
942
943 xasprintf(&send_buffer, "S24:%s\r\n", nlm_name);
944 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
945 if (result != STATE_OK)
946 return result;
947
948 recv_buffer[strlen(recv_buffer) - 1] = 0;
949 if (strcmp(recv_buffer, "-1")) {
950 xasprintf(&output_message, _("Module %s version %s is loaded"), nlm_name, recv_buffer);
951 } else {
952 result = STATE_CRITICAL;
953 xasprintf(&output_message, _("Module %s is not loaded"), nlm_name);
954 }
955 } else if (vars_to_check == NRMP) {
956
957 xasprintf(&send_buffer, "NRMP:%s\r\n", nrmp_name);
958 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
959 if (result != STATE_OK)
960 return result;
961
962 if (!strcmp(recv_buffer, "-1\n")) {
963 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmp_name);
964 result = STATE_CRITICAL;
965 } else {
966 nrmp_value = strtoul(recv_buffer, NULL, 10);
967 if (check_critical_value && nrmp_value <= critical_value)
968 result = STATE_CRITICAL;
969 else if (check_warning_value && nrmp_value <= warning_value)
970 result = STATE_WARNING;
971 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmp_name, nrmp_value, nrmp_name, nrmp_value, warning_value,
972 critical_value);
973 }
974
975 } else if (vars_to_check == NRMM) {
976
977 xasprintf(&send_buffer, "NRMM:%s\r\n", nrmm_name);
978 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
979 if (result != STATE_OK)
980 return result;
981
982 if (!strcmp(recv_buffer, "-1\n")) {
983 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmm_name);
984 result = STATE_CRITICAL;
985 } else {
986 nrmm_value = strtoul(recv_buffer, NULL, 10);
987 if (check_critical_value && nrmm_value <= critical_value)
988 result = STATE_CRITICAL;
989 else if (check_warning_value && nrmm_value <= warning_value)
990 result = STATE_WARNING;
991 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmm_name, nrmm_value, nrmm_name, nrmm_value, warning_value,
992 critical_value);
993 }
994
995 } else if (vars_to_check == NRMS) {
996
997 xasprintf(&send_buffer, "NRMS:%s\r\n", nrms_name);
998 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
999 if (result != STATE_OK)
1000 return result;
1001
1002 if (!strcmp(recv_buffer, "-1\n")) {
1003 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrms_name);
1004 result = STATE_CRITICAL;
1005 } else {
1006 nrms_value = strtoul(recv_buffer, NULL, 10);
1007 if (check_critical_value && nrms_value >= critical_value)
1008 result = STATE_CRITICAL;
1009 else if (check_warning_value && nrms_value >= warning_value)
1010 result = STATE_WARNING;
1011 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrms_name, nrms_value, nrms_name, nrms_value, warning_value,
1012 critical_value);
1013 }
1014
1015 } else if (vars_to_check == NSS1) {
1016
1017 xasprintf(&send_buffer, "NSS1:%s\r\n", nss1_name);
1018 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1019 if (result != STATE_OK)
1020 return result;
1021
1022 if (!strcmp(recv_buffer, "-1\n")) {
1023 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss1_name);
1024 result = STATE_CRITICAL;
1025 } else {
1026 nss1_value = strtoul(recv_buffer, NULL, 10);
1027 if (check_critical_value && nss1_value >= critical_value)
1028 result = STATE_CRITICAL;
1029 else if (check_warning_value && nss1_value >= warning_value)
1030 result = STATE_WARNING;
1031 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss1_name, nss1_value, nss1_name, nss1_value, warning_value,
1032 critical_value);
1033 }
1034
1035 } else if (vars_to_check == NSS2) {
1036
1037 xasprintf(&send_buffer, "NSS2:%s\r\n", nss2_name);
1038 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1039 if (result != STATE_OK)
1040 return result;
1041
1042 if (!strcmp(recv_buffer, "-1\n")) {
1043 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss2_name);
1044 result = STATE_CRITICAL;
1045 } else {
1046 nss2_value = strtoul(recv_buffer, NULL, 10);
1047 if (check_critical_value && nss2_value >= critical_value)
1048 result = STATE_CRITICAL;
1049 else if (check_warning_value && nss2_value >= warning_value)
1050 result = STATE_WARNING;
1051 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss2_name, nss2_value, nss2_name, nss2_value, warning_value,
1052 critical_value);
1053 }
1054
1055 } else if (vars_to_check == NSS3) {
1056
1057 xasprintf(&send_buffer, "NSS3:%s\r\n", nss3_name);
1058 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1059 if (result != STATE_OK)
1060 return result;
1061
1062 if (!strcmp(recv_buffer, "-1\n")) {
1063 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss3_name);
1064 result = STATE_CRITICAL;
1065 } else {
1066 nss3_value = strtoul(recv_buffer, NULL, 10);
1067 if (check_critical_value && nss3_value >= critical_value)
1068 result = STATE_CRITICAL;
1069 else if (check_warning_value && nss3_value >= warning_value)
1070 result = STATE_WARNING;
1071 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss3_name, nss3_value, nss3_name, nss3_value, warning_value,
1072 critical_value);
1073 }
1074
1075 } else if (vars_to_check == NSS4) {
1076
1077 xasprintf(&send_buffer, "NSS4:%s\r\n", nss4_name);
1078 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1079 if (result != STATE_OK)
1080 return result;
1081
1082 if (!strcmp(recv_buffer, "-1\n")) {
1083 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss4_name);
1084 result = STATE_CRITICAL;
1085 } else {
1086 nss4_value = strtoul(recv_buffer, NULL, 10);
1087 if (check_critical_value && nss4_value >= critical_value)
1088 result = STATE_CRITICAL;
1089 else if (check_warning_value && nss4_value >= warning_value)
1090 result = STATE_WARNING;
1091 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss4_name, nss4_value, nss4_name, nss4_value, warning_value,
1092 critical_value);
1093 }
1094
1095 } else if (vars_to_check == NSS5) {
1096
1097 xasprintf(&send_buffer, "NSS5:%s\r\n", nss5_name);
1098 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1099 if (result != STATE_OK)
1100 return result;
1101
1102 if (!strcmp(recv_buffer, "-1\n")) {
1103 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss5_name);
1104 result = STATE_CRITICAL;
1105 } else {
1106 nss5_value = strtoul(recv_buffer, NULL, 10);
1107 if (check_critical_value && nss5_value >= critical_value)
1108 result = STATE_CRITICAL;
1109 else if (check_warning_value && nss5_value >= warning_value)
1110 result = STATE_WARNING;
1111 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss5_name, nss5_value, nss5_name, nss5_value, warning_value,
1112 critical_value);
1113 }
1114
1115 } else if (vars_to_check == NSS6) {
1116
1117 xasprintf(&send_buffer, "NSS6:%s\r\n", nss6_name);
1118 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1119 if (result != STATE_OK)
1120 return result;
1121
1122 if (!strcmp(recv_buffer, "-1\n")) {
1123 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss6_name);
1124 result = STATE_CRITICAL;
1125 } else {
1126 nss6_value = strtoul(recv_buffer, NULL, 10);
1127 if (check_critical_value && nss6_value >= critical_value)
1128 result = STATE_CRITICAL;
1129 else if (check_warning_value && nss6_value >= warning_value)
1130 result = STATE_WARNING;
1131 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss6_name, nss6_value, nss6_name, nss6_value, warning_value,
1132 critical_value);
1133 }
1134
1135 } else if (vars_to_check == NSS7) {
1136
1137 xasprintf(&send_buffer, "NSS7:%s\r\n", nss7_name);
1138 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1139 if (result != STATE_OK)
1140 return result;
1141
1142 if (!strcmp(recv_buffer, "-1\n")) {
1143 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss7_name);
1144 result = STATE_CRITICAL;
1145 } else {
1146 nss7_value = strtoul(recv_buffer, NULL, 10);
1147 if (check_critical_value && nss7_value >= critical_value)
1148 result = STATE_CRITICAL;
1149 else if (check_warning_value && nss7_value >= warning_value)
1150 result = STATE_WARNING;
1151 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss7_name, nss7_value, nss7_name, nss7_value, warning_value,
1152 critical_value);
1153 }
1154
1155 } else {
1156
1157 output_message = strdup(_("Nothing to check!\n"));
1158 result = STATE_UNKNOWN;
1159 }
1160
1161 close(sd);
1162
1163 /* reset timeout */
1164 alarm(0);
1165
1166 printf("%s%s\n", netware_version, output_message);
1167
1168 return result;
1169}
1170
1171/* process command-line arguments */
1172int process_arguments(int argc, char **argv) {
1173 int c;
1174
1175 int option = 0;
1176 static struct option longopts[] = {{"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'},
1177 {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'},
1178 {"variable", required_argument, 0, 'v'}, {"hostname", required_argument, 0, 'H'},
1179 {"osversion", no_argument, 0, 'o'}, {"version", no_argument, 0, 'V'},
1180 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
1181
1182 /* no options were supplied */
1183 if (argc < 2)
1184 return ERROR;
1185
1186 /* backwards compatibility */
1187 if (!is_option(argv[1])) {
1188 server_address = argv[1];
1189 argv[1] = argv[0];
1190 argv = &argv[1];
1191 argc--;
1192 }
1193
1194 for (c = 1; c < argc; c++) {
1195 if (strcmp("-to", argv[c]) == 0)
1196 strcpy(argv[c], "-t");
1197 else if (strcmp("-wv", argv[c]) == 0)
1198 strcpy(argv[c], "-w");
1199 else if (strcmp("-cv", argv[c]) == 0)
1200 strcpy(argv[c], "-c");
1201 }
1202
1203 while (1) {
1204 c = getopt_long(argc, argv, "+hoVH:t:c:w:p:v:", longopts, &option);
1205
1206 if (c == -1 || c == EOF || c == 1)
1207 break;
1208
1209 switch (c) {
1210 case '?': /* print short usage statement if args not parsable */
1211 usage5();
1212 case 'h': /* help */
1213 print_help();
1214 exit(STATE_UNKNOWN);
1215 case 'V': /* version */
1216 print_revision(progname, NP_VERSION);
1217 exit(STATE_UNKNOWN);
1218 case 'H': /* hostname */
1219 server_address = optarg;
1220 break;
1221 case 'o': /* display nos version */
1222 check_netware_version = true;
1223 break;
1224 case 'p': /* port */
1225 if (is_intnonneg(optarg))
1226 server_port = atoi(optarg);
1227 else
1228 die(STATE_UNKNOWN, _("Server port an integer\n"));
1229 break;
1230 case 'v':
1231 if (strlen(optarg) < 3)
1232 return ERROR;
1233 if (!strcmp(optarg, "LOAD1"))
1234 vars_to_check = LOAD1;
1235 else if (!strcmp(optarg, "LOAD5"))
1236 vars_to_check = LOAD5;
1237 else if (!strcmp(optarg, "LOAD15"))
1238 vars_to_check = LOAD15;
1239 else if (!strcmp(optarg, "CONNS"))
1240 vars_to_check = CONNS;
1241 else if (!strcmp(optarg, "LTCH"))
1242 vars_to_check = LTCH;
1243 else if (!strcmp(optarg, "DCB"))
1244 vars_to_check = DCB;
1245 else if (!strcmp(optarg, "TCB"))
1246 vars_to_check = TCB;
1247 else if (!strcmp(optarg, "CBUFF"))
1248 vars_to_check = CBUFF;
1249 else if (!strcmp(optarg, "CDBUFF"))
1250 vars_to_check = CDBUFF;
1251 else if (!strcmp(optarg, "LRUM"))
1252 vars_to_check = LRUM;
1253 else if (!strcmp(optarg, "LRUS"))
1254 vars_to_check = LRUS;
1255 else if (strncmp(optarg, "VPF", 3) == 0) {
1256 vars_to_check = VPF;
1257 volume_name = strdup(optarg + 3);
1258 if (!strcmp(volume_name, ""))
1259 volume_name = strdup("SYS");
1260 } else if (strncmp(optarg, "VKF", 3) == 0) {
1261 vars_to_check = VKF;
1262 volume_name = strdup(optarg + 3);
1263 if (!strcmp(volume_name, ""))
1264 volume_name = strdup("SYS");
1265 } else if (strncmp(optarg, "VMF", 3) == 0) {
1266 vars_to_check = VMF;
1267 volume_name = strdup(optarg + 3);
1268 if (!strcmp(volume_name, ""))
1269 volume_name = strdup("SYS");
1270 } else if (!strcmp(optarg, "DSDB"))
1271 vars_to_check = DSDB;
1272 else if (!strcmp(optarg, "LOGINS"))
1273 vars_to_check = LOGINS;
1274 else if (!strcmp(optarg, "NRMH"))
1275 vars_to_check = NRMH;
1276 else if (!strcmp(optarg, "UPRB"))
1277 vars_to_check = UPRB;
1278 else if (!strcmp(optarg, "PUPRB"))
1279 vars_to_check = PUPRB;
1280 else if (!strncmp(optarg, "SAPENTRIES", 10)) {
1281 vars_to_check = SAPENTRIES;
1282 if (strlen(optarg) > 10)
1283 sap_number = atoi(optarg + 10);
1284 else
1285 sap_number = -1;
1286 } else if (!strcmp(optarg, "OFILES"))
1287 vars_to_check = OFILES;
1288 else if (strncmp(optarg, "VKP", 3) == 0) {
1289 vars_to_check = VKP;
1290 volume_name = strdup(optarg + 3);
1291 if (!strcmp(volume_name, ""))
1292 volume_name = strdup("SYS");
1293 } else if (strncmp(optarg, "VMP", 3) == 0) {
1294 vars_to_check = VMP;
1295 volume_name = strdup(optarg + 3);
1296 if (!strcmp(volume_name, ""))
1297 volume_name = strdup("SYS");
1298 } else if (strncmp(optarg, "VMU", 3) == 0) {
1299 vars_to_check = VMU;
1300 volume_name = strdup(optarg + 3);
1301 if (!strcmp(volume_name, ""))
1302 volume_name = strdup("SYS");
1303 } else if (strncmp(optarg, "VPU", 3) == 0) {
1304 vars_to_check = VPU;
1305 volume_name = strdup(optarg + 3);
1306 if (!strcmp(volume_name, ""))
1307 volume_name = strdup("SYS");
1308 } else if (strncmp(optarg, "VPP", 3) == 0) {
1309 vars_to_check = VPP;
1310 volume_name = strdup(optarg + 3);
1311 if (!strcmp(volume_name, ""))
1312 volume_name = strdup("SYS");
1313 } else if (strncmp(optarg, "VKNP", 4) == 0) {
1314 vars_to_check = VKNP;
1315 volume_name = strdup(optarg + 4);
1316 if (!strcmp(volume_name, ""))
1317 volume_name = strdup("SYS");
1318 } else if (strncmp(optarg, "VPNP", 4) == 0) {
1319 vars_to_check = VPNP;
1320 volume_name = strdup(optarg + 4);
1321 if (!strcmp(volume_name, ""))
1322 volume_name = strdup("SYS");
1323 } else if (!strcmp(optarg, "ABENDS"))
1324 vars_to_check = ABENDS;
1325 else if (!strcmp(optarg, "CSPROCS"))
1326 vars_to_check = CSPROCS;
1327 else if (!strcmp(optarg, "TSYNC"))
1328 vars_to_check = TSYNC;
1329 else if (!strcmp(optarg, "DSVER"))
1330 vars_to_check = DSVER;
1331 else if (!strcmp(optarg, "UPTIME")) {
1332 vars_to_check = UPTIME;
1333 } else if (strncmp(optarg, "NLM:", 4) == 0) {
1334 vars_to_check = NLM;
1335 nlm_name = strdup(optarg + 4);
1336 } else if (strncmp(optarg, "NRMP", 4) == 0) {
1337 vars_to_check = NRMP;
1338 nrmp_name = strdup(optarg + 4);
1339 if (!strcmp(nrmp_name, ""))
1340 nrmp_name = strdup("AVAILABLE_MEMORY");
1341 } else if (strncmp(optarg, "NRMM", 4) == 0) {
1342 vars_to_check = NRMM;
1343 nrmm_name = strdup(optarg + 4);
1344 if (!strcmp(nrmm_name, ""))
1345 nrmm_name = strdup("AVAILABLE_CACHE_MEMORY");
1346
1347 }
1348
1349 else if (strncmp(optarg, "NRMS", 4) == 0) {
1350 vars_to_check = NRMS;
1351 nrms_name = strdup(optarg + 4);
1352 if (!strcmp(nrms_name, ""))
1353 nrms_name = strdup("USED_SWAP_SPACE");
1354
1355 }
1356
1357 else if (strncmp(optarg, "NSS1", 4) == 0) {
1358 vars_to_check = NSS1;
1359 nss1_name = strdup(optarg + 4);
1360 if (!strcmp(nss1_name, ""))
1361 nss1_name = strdup("CURRENTBUFFERCACHESIZE");
1362
1363 }
1364
1365 else if (strncmp(optarg, "NSS2", 4) == 0) {
1366 vars_to_check = NSS2;
1367 nss2_name = strdup(optarg + 4);
1368 if (!strcmp(nss2_name, ""))
1369 nss2_name = strdup("CACHEHITS");
1370
1371 }
1372
1373 else if (strncmp(optarg, "NSS3", 4) == 0) {
1374 vars_to_check = NSS3;
1375 nss3_name = strdup(optarg + 4);
1376 if (!strcmp(nss3_name, ""))
1377 nss3_name = strdup("CACHEGITPERCENT");
1378
1379 }
1380
1381 else if (strncmp(optarg, "NSS4", 4) == 0) {
1382 vars_to_check = NSS4;
1383 nss4_name = strdup(optarg + 4);
1384 if (!strcmp(nss4_name, ""))
1385 nss4_name = strdup("CURRENTOPENCOUNT");
1386
1387 }
1388
1389 else if (strncmp(optarg, "NSS5", 4) == 0) {
1390 vars_to_check = NSS5;
1391 nss5_name = strdup(optarg + 4);
1392 if (!strcmp(nss5_name, ""))
1393 nss5_name = strdup("CACHEMISSES");
1394
1395 }
1396
1397 else if (strncmp(optarg, "NSS6", 4) == 0) {
1398 vars_to_check = NSS6;
1399 nss6_name = strdup(optarg + 4);
1400 if (!strcmp(nss6_name, ""))
1401 nss6_name = strdup("PENDINGWORKSCOUNT");
1402
1403 }
1404
1405 else if (strncmp(optarg, "NSS7", 4) == 0) {
1406 vars_to_check = NSS7;
1407 nss7_name = strdup(optarg + 4);
1408 if (!strcmp(nss7_name, ""))
1409 nss7_name = strdup("CACHESIZE");
1410
1411 }
1412
1413 else
1414 return ERROR;
1415 break;
1416 case 'w': /* warning threshold */
1417 warning_value = strtoul(optarg, NULL, 10);
1418 check_warning_value = true;
1419 break;
1420 case 'c': /* critical threshold */
1421 critical_value = strtoul(optarg, NULL, 10);
1422 check_critical_value = true;
1423 break;
1424 case 't': /* timeout */
1425 socket_timeout = atoi(optarg);
1426 if (socket_timeout <= 0)
1427 return ERROR;
1428 }
1429 }
1430
1431 return OK;
1432}
1433
1434void print_help(void) {
1435 char *myport;
1436 xasprintf(&myport, "%d", PORT);
1437
1438 print_revision(progname, NP_VERSION);
1439
1440 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1441 printf(COPYRIGHT, copyright, email);
1442
1443 printf("%s\n", _("This plugin attempts to contact the MRTGEXT NLM running on a"));
1444 printf("%s\n", _("Novell server to gather the requested system information."));
1445
1446 printf("\n\n");
1447
1448 print_usage();
1449
1450 printf(UT_HELP_VRSN);
1451 printf(UT_EXTRA_OPTS);
1452
1453 printf(UT_HOST_PORT, 'p', myport);
1454
1455 printf(" %s\n", "-v, --variable=STRING");
1456 printf(" %s\n", _("Variable to check. Valid variables include:"));
1457 printf(" %s\n", _("LOAD1 = 1 minute average CPU load"));
1458 printf(" %s\n", _("LOAD5 = 5 minute average CPU load"));
1459 printf(" %s\n", _("LOAD15 = 15 minute average CPU load"));
1460 printf(" %s\n", _("CSPROCS = number of current service processes (NW 5.x only)"));
1461 printf(" %s\n", _("ABENDS = number of abended threads (NW 5.x only)"));
1462 printf(" %s\n", _("UPTIME = server uptime"));
1463 printf(" %s\n", _("LTCH = percent long term cache hits"));
1464 printf(" %s\n", _("CBUFF = current number of cache buffers"));
1465 printf(" %s\n", _("CDBUFF = current number of dirty cache buffers"));
1466 printf(" %s\n", _("DCB = dirty cache buffers as a percentage of the total"));
1467 printf(" %s\n", _("TCB = dirty cache buffers as a percentage of the original"));
1468 printf(" %s\n", _("OFILES = number of open files"));
1469 printf(" %s\n", _(" VMF<vol> = MB of free space on Volume <vol>"));
1470 printf(" %s\n", _(" VMU<vol> = MB used space on Volume <vol>"));
1471 printf(" %s\n", _(" VPU<vol> = percent used space on Volume <vol>"));
1472 printf(" %s\n", _(" VMP<vol> = MB of purgeable space on Volume <vol>"));
1473 printf(" %s\n", _(" VPF<vol> = percent free space on volume <vol>"));
1474 printf(" %s\n", _(" VKF<vol> = KB of free space on volume <vol>"));
1475 printf(" %s\n", _(" VPP<vol> = percent purgeable space on volume <vol>"));
1476 printf(" %s\n", _(" VKP<vol> = KB of purgeable space on volume <vol>"));
1477 printf(" %s\n", _(" VPNP<vol> = percent not yet purgeable space on volume <vol>"));
1478 printf(" %s\n", _(" VKNP<vol> = KB of not yet purgeable space on volume <vol>"));
1479 printf(" %s\n", _(" LRUM = LRU sitting time in minutes"));
1480 printf(" %s\n", _(" LRUS = LRU sitting time in seconds"));
1481 printf(" %s\n", _(" DSDB = check to see if DS Database is open"));
1482 printf(" %s\n", _(" DSVER = NDS version"));
1483 printf(" %s\n", _(" UPRB = used packet receive buffers"));
1484 printf(" %s\n", _(" PUPRB = percent (of max) used packet receive buffers"));
1485 printf(" %s\n", _(" SAPENTRIES = number of entries in the SAP table"));
1486 printf(" %s\n", _(" SAPENTRIES<n> = number of entries in the SAP table for SAP type <n>"));
1487 printf(" %s\n", _(" TSYNC = timesync status"));
1488 printf(" %s\n", _(" LOGINS = check to see if logins are enabled"));
1489 printf(" %s\n", _(" CONNS = number of currently licensed connections"));
1490 printf(" %s\n", _(" NRMH = NRM Summary Status"));
1491 printf(" %s\n", _(" NRMP<stat> = Returns the current value for a NRM health item"));
1492 printf(" %s\n", _(" NRMM<stat> = Returns the current memory stats from NRM"));
1493 printf(" %s\n", _(" NRMS<stat> = Returns the current Swapfile stats from NRM"));
1494 printf(" %s\n", _(" NSS1<stat> = Statistics from _Admin:Manage_NSS\\GeneralStats.xml"));
1495 printf(" %s\n", _(" NSS3<stat> = Statistics from _Admin:Manage_NSS\\NameCache.xml"));
1496 printf(" %s\n", _(" NSS4<stat> = Statistics from _Admin:Manage_NSS\\FileStats.xml"));
1497 printf(" %s\n", _(" NSS5<stat> = Statistics from _Admin:Manage_NSS\\ObjectCache.xml"));
1498 printf(" %s\n", _(" NSS6<stat> = Statistics from _Admin:Manage_NSS\\Thread.xml"));
1499 printf(" %s\n", _(" NSS7<stat> = Statistics from _Admin:Manage_NSS\\AuthorizationCache.xml"));
1500 printf(" %s\n", _(" NLM:<nlm> = check if NLM is loaded and report version"));
1501 printf(" %s\n", _(" (e.g. NLM:TSANDS.NLM)"));
1502 printf("\n");
1503 printf(" %s\n", "-w, --warning=INTEGER");
1504 printf(" %s\n", _("Threshold which will result in a warning status"));
1505 printf(" %s\n", "-c, --critical=INTEGER");
1506 printf(" %s\n", _("Threshold which will result in a critical status"));
1507 printf(" %s\n", "-o, --osversion");
1508 printf(" %s\n", _("Include server version string in results"));
1509
1510 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1511
1512 printf("\n");
1513 printf("%s\n", _("Notes:"));
1514 printf(" %s\n", _("- This plugin requires that the MRTGEXT.NLM file from James Drews' MRTG"));
1515 printf(" %s\n", _(" extension for NetWare be loaded on the Novell servers you wish to check."));
1516 printf(" %s\n", _(" (available from http://www.engr.wisc.edu/~drews/mrtg/)"));
1517 printf(" %s\n", _("- Values for critical thresholds should be lower than warning thresholds"));
1518 printf(" %s\n", _(" when the following variables are checked: VPF, VKF, LTCH, CBUFF, DCB, "));
1519 printf(" %s\n", _(" TCB, LRUS and LRUM."));
1520
1521 printf(UT_SUPPORT);
1522}
1523
1524void print_usage(void) {
1525 printf("%s\n", _("Usage:"));
1526 printf("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname);
1527}
diff --git a/plugins/check_overcr.c b/plugins/check_overcr.c
deleted file mode 100644
index 599540b7..00000000
--- a/plugins/check_overcr.c
+++ /dev/null
@@ -1,417 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_overcr plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2024 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-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h"
37#include "netutils.h"
38#include "utils.h"
39
40enum checkvar {
41 NONE,
42 LOAD1,
43 LOAD5,
44 LOAD15,
45 DPU,
46 PROCS,
47 NETSTAT,
48 UPTIME
49};
50
51enum {
52 PORT = 2000
53};
54
55static char *server_address = NULL;
56static int server_port = PORT;
57static double warning_value = 0L;
58static double critical_value = 0L;
59static bool check_warning_value = false;
60static bool check_critical_value = false;
61static enum checkvar vars_to_check = NONE;
62
63static int netstat_port = 0;
64static char *disk_name = NULL;
65static char *process_name = NULL;
66static char send_buffer[MAX_INPUT_BUFFER];
67
68static int process_arguments(int, char **);
69void print_usage(void);
70static void print_help(void);
71
72int main(int argc, char **argv) {
73 int result = STATE_UNKNOWN;
74 char recv_buffer[MAX_INPUT_BUFFER];
75 char temp_buffer[MAX_INPUT_BUFFER];
76 char *temp_ptr = NULL;
77 bool found_disk = false;
78 unsigned long percent_used_disk_space = 100;
79 double load;
80 double load_1min;
81 double load_5min;
82 double load_15min;
83 int port_connections = 0;
84 int processes = 0;
85 double uptime_raw_hours;
86 int uptime_raw_minutes = 0;
87 int uptime_days = 0;
88 int uptime_hours = 0;
89 int uptime_minutes = 0;
90
91 setlocale(LC_ALL, "");
92 bindtextdomain(PACKAGE, LOCALEDIR);
93 textdomain(PACKAGE);
94
95 /* Parse extra opts if any */
96 argv = np_extra_opts(&argc, argv, progname);
97
98 if (process_arguments(argc, argv) == ERROR)
99 usage4(_("Could not parse arguments"));
100
101 /* initialize alarm signal handling */
102 signal(SIGALRM, socket_timeout_alarm_handler);
103
104 /* set socket timeout */
105 alarm(socket_timeout);
106
107 result = process_tcp_request2(server_address, server_port, send_buffer, recv_buffer, sizeof(recv_buffer));
108
109 switch (vars_to_check) {
110
111 case LOAD1:
112 case LOAD5:
113 case LOAD15:
114
115 if (result != STATE_OK)
116 die(result, _("Unknown error fetching load data\n"));
117
118 temp_ptr = (char *)strtok(recv_buffer, "\r\n");
119 if (temp_ptr == NULL)
120 die(STATE_CRITICAL, _("Invalid response from server - no load information\n"));
121 else
122 load_1min = strtod(temp_ptr, NULL);
123
124 temp_ptr = (char *)strtok(NULL, "\r\n");
125 if (temp_ptr == NULL)
126 die(STATE_CRITICAL, _("Invalid response from server after load 1\n"));
127 else
128 load_5min = strtod(temp_ptr, NULL);
129
130 temp_ptr = (char *)strtok(NULL, "\r\n");
131 if (temp_ptr == NULL)
132 die(STATE_CRITICAL, _("Invalid response from server after load 5\n"));
133 else
134 load_15min = strtod(temp_ptr, NULL);
135
136 switch (vars_to_check) {
137 case LOAD1:
138 strcpy(temp_buffer, "1");
139 load = load_1min;
140 break;
141 case LOAD5:
142 strcpy(temp_buffer, "5");
143 load = load_5min;
144 break;
145 default:
146 strcpy(temp_buffer, "15");
147 load = load_15min;
148 break;
149 }
150
151 if (check_critical_value && (load >= critical_value))
152 result = STATE_CRITICAL;
153 else if (check_warning_value && (load >= warning_value))
154 result = STATE_WARNING;
155
156 die(result, _("Load %s - %s-min load average = %0.2f"), state_text(result), temp_buffer, load);
157
158 break;
159
160 case DPU:
161
162 if (result != STATE_OK)
163 die(result, _("Unknown error fetching disk data\n"));
164
165 for (temp_ptr = (char *)strtok(recv_buffer, " "); temp_ptr != NULL; temp_ptr = (char *)strtok(NULL, " ")) {
166
167 if (!strcmp(temp_ptr, disk_name)) {
168 found_disk = true;
169 temp_ptr = (char *)strtok(NULL, "%");
170 if (temp_ptr == NULL)
171 die(STATE_CRITICAL, _("Invalid response from server\n"));
172 else
173 percent_used_disk_space = strtoul(temp_ptr, NULL, 10);
174 break;
175 }
176
177 temp_ptr = (char *)strtok(NULL, "\r\n");
178 }
179
180 /* error if we couldn't find the info for the disk */
181 if (!found_disk)
182 die(STATE_CRITICAL, "CRITICAL - Disk '%s' non-existent or not mounted", disk_name);
183
184 if (check_critical_value && (percent_used_disk_space >= critical_value))
185 result = STATE_CRITICAL;
186 else if (check_warning_value && (percent_used_disk_space >= warning_value))
187 result = STATE_WARNING;
188
189 die(result, "Disk %s - %lu%% used on %s", state_text(result), percent_used_disk_space, disk_name);
190
191 break;
192
193 case NETSTAT:
194
195 if (result != STATE_OK)
196 die(result, _("Unknown error fetching network status\n"));
197 else
198 port_connections = strtod(recv_buffer, NULL);
199
200 if (check_critical_value && (port_connections >= critical_value))
201 result = STATE_CRITICAL;
202 else if (check_warning_value && (port_connections >= warning_value))
203 result = STATE_WARNING;
204
205 die(result, _("Net %s - %d connection%s on port %d"), state_text(result), port_connections, (port_connections == 1) ? "" : "s",
206 netstat_port);
207
208 break;
209
210 case PROCS:
211
212 if (result != STATE_OK)
213 die(result, _("Unknown error fetching process status\n"));
214
215 temp_ptr = (char *)strtok(recv_buffer, "(");
216 if (temp_ptr == NULL)
217 die(STATE_CRITICAL, _("Invalid response from server\n"));
218
219 temp_ptr = (char *)strtok(NULL, ")");
220 if (temp_ptr == NULL)
221 die(STATE_CRITICAL, _("Invalid response from server\n"));
222 else
223 processes = strtod(temp_ptr, NULL);
224
225 if (check_critical_value && (processes >= critical_value))
226 result = STATE_CRITICAL;
227 else if (check_warning_value && (processes >= warning_value))
228 result = STATE_WARNING;
229
230 die(result, _("Process %s - %d instance%s of %s running"), state_text(result), processes, (processes == 1) ? "" : "s",
231 process_name);
232 break;
233
234 case UPTIME:
235
236 if (result != STATE_OK)
237 return result;
238
239 uptime_raw_hours = strtod(recv_buffer, NULL);
240 uptime_raw_minutes = (unsigned long)(uptime_raw_hours * 60.0);
241
242 if (check_critical_value && (uptime_raw_minutes <= critical_value))
243 result = STATE_CRITICAL;
244 else if (check_warning_value && (uptime_raw_minutes <= warning_value))
245 result = STATE_WARNING;
246
247 uptime_days = uptime_raw_minutes / 1440;
248 uptime_raw_minutes %= 1440;
249 uptime_hours = uptime_raw_minutes / 60;
250 uptime_raw_minutes %= 60;
251 uptime_minutes = uptime_raw_minutes;
252
253 die(result, _("Uptime %s - Up %d days %d hours %d minutes"), state_text(result), uptime_days, uptime_hours, uptime_minutes);
254 break;
255
256 default:
257 die(STATE_UNKNOWN, _("Nothing to check!\n"));
258 break;
259 }
260}
261
262/* process command-line arguments */
263int process_arguments(int argc, char **argv) {
264 int c;
265
266 int option = 0;
267 static struct option longopts[] = {
268 {"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'}, {"critical", required_argument, 0, 'c'},
269 {"warning", required_argument, 0, 'w'}, {"variable", required_argument, 0, 'v'}, {"hostname", required_argument, 0, 'H'},
270 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
271
272 /* no options were supplied */
273 if (argc < 2)
274 return ERROR;
275
276 /* backwards compatibility */
277 if (!is_option(argv[1])) {
278 server_address = argv[1];
279 argv[1] = argv[0];
280 argv = &argv[1];
281 argc--;
282 }
283
284 for (c = 1; c < argc; c++) {
285 if (strcmp("-to", argv[c]) == 0)
286 strcpy(argv[c], "-t");
287 else if (strcmp("-wv", argv[c]) == 0)
288 strcpy(argv[c], "-w");
289 else if (strcmp("-cv", argv[c]) == 0)
290 strcpy(argv[c], "-c");
291 }
292
293 while (1) {
294 c = getopt_long(argc, argv, "+hVH:t:c:w:p:v:", longopts, &option);
295
296 if (c == -1 || c == EOF || c == 1)
297 break;
298
299 switch (c) {
300 case '?': /* print short usage statement if args not parsable */
301 usage5();
302 case 'h': /* help */
303 print_help();
304 exit(STATE_UNKNOWN);
305 case 'V': /* version */
306 print_revision(progname, NP_VERSION);
307 exit(STATE_UNKNOWN);
308 case 'H': /* hostname */
309 server_address = optarg;
310 break;
311 case 'p': /* port */
312 if (is_intnonneg(optarg))
313 server_port = atoi(optarg);
314 else
315 die(STATE_UNKNOWN, _("Server port an integer\n"));
316 break;
317 case 'v': /* variable */
318 if (strcmp(optarg, "LOAD") == 0) {
319 strcpy(send_buffer, "LOAD\r\nQUIT\r\n");
320 if (strcmp(optarg, "LOAD1") == 0)
321 vars_to_check = LOAD1;
322 else if (strcmp(optarg, "LOAD5") == 0)
323 vars_to_check = LOAD5;
324 else if (strcmp(optarg, "LOAD15") == 0)
325 vars_to_check = LOAD15;
326 } else if (strcmp(optarg, "UPTIME") == 0) {
327 vars_to_check = UPTIME;
328 strcpy(send_buffer, "UPTIME\r\n");
329 } else if (strstr(optarg, "PROC") == optarg) {
330 vars_to_check = PROCS;
331 process_name = strscpy(process_name, optarg + 4);
332 sprintf(send_buffer, "PROCESS %s\r\n", process_name);
333 } else if (strstr(optarg, "NET") == optarg) {
334 vars_to_check = NETSTAT;
335 netstat_port = atoi(optarg + 3);
336 sprintf(send_buffer, "NETSTAT %d\r\n", netstat_port);
337 } else if (strstr(optarg, "DPU") == optarg) {
338 vars_to_check = DPU;
339 strcpy(send_buffer, "DISKSPACE\r\n");
340 disk_name = strscpy(disk_name, optarg + 3);
341 } else
342 return ERROR;
343 break;
344 case 'w': /* warning threshold */
345 warning_value = strtoul(optarg, NULL, 10);
346 check_warning_value = true;
347 break;
348 case 'c': /* critical threshold */
349 critical_value = strtoul(optarg, NULL, 10);
350 check_critical_value = true;
351 break;
352 case 't': /* timeout */
353 socket_timeout = atoi(optarg);
354 if (socket_timeout <= 0)
355 return ERROR;
356 }
357 }
358 return OK;
359}
360
361void print_help(void) {
362 char *myport;
363 xasprintf(&myport, "%d", PORT);
364
365 print_revision(progname, NP_VERSION);
366
367 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
368 printf(COPYRIGHT, copyright, email);
369
370 printf("%s\n", _("This plugin attempts to contact the Over-CR collector daemon running on the"));
371 printf("%s\n", _("remote UNIX server in order to gather the requested system information."));
372
373 printf("\n\n");
374
375 print_usage();
376
377 printf(UT_HELP_VRSN);
378 printf(UT_EXTRA_OPTS);
379
380 printf(UT_HOST_PORT, 'p', myport);
381
382 printf(" %s\n", "-w, --warning=INTEGER");
383 printf(" %s\n", _("Threshold which will result in a warning status"));
384 printf(" %s\n", "-c, --critical=INTEGER");
385 printf(" %s\n", _("Threshold which will result in a critical status"));
386 printf(" %s\n", "-v, --variable=STRING");
387 printf(" %s\n", _("Variable to check. Valid variables include:"));
388 printf(" %s\n", _("LOAD1 = 1 minute average CPU load"));
389 printf(" %s\n", _("LOAD5 = 5 minute average CPU load"));
390 printf(" %s\n", _("LOAD15 = 15 minute average CPU load"));
391 printf(" %s\n", _("DPU<filesys> = percent used disk space on filesystem <filesys>"));
392 printf(" %s\n", _("PROC<process> = number of running processes with name <process>"));
393 printf(" %s\n", _("NET<port> = number of active connections on TCP port <port>"));
394 printf(" %s\n", _("UPTIME = system uptime in seconds"));
395
396 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
397
398 printf(UT_VERBOSE);
399
400 printf("\n");
401 printf("%s\n", _("This plugin requires that Eric Molitors' Over-CR collector daemon be"));
402 printf("%s\n", _("running on the remote server."));
403 printf("%s\n", _("Over-CR can be downloaded from http://www.molitor.org/overcr"));
404 printf("%s\n", _("This plugin was tested with version 0.99.53 of the Over-CR collector"));
405
406 printf("\n");
407 printf("%s\n", _("Notes:"));
408 printf(" %s\n", _("For the available options, the critical threshold value should always be"));
409 printf(" %s\n", _("higher than the warning threshold value, EXCEPT with the uptime variable"));
410
411 printf(UT_SUPPORT);
412}
413
414void print_usage(void) {
415 printf("%s\n", _("Usage:"));
416 printf("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname);
417}
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index 6613634d..793a686f 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -28,6 +28,7 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
31const char *progname = "check_pgsql"; 32const char *progname = "check_pgsql";
32const char *copyright = "1999-2024"; 33const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
@@ -35,51 +36,39 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 36#include "common.h"
36#include "utils.h" 37#include "utils.h"
37#include "utils_cmd.h" 38#include "utils_cmd.h"
39#include "check_pgsql.d/config.h"
40#include "thresholds.h"
38 41
39#include "netutils.h" 42#include "netutils.h"
40#include <libpq-fe.h> 43#include <libpq-fe.h>
41#include <pg_config_manual.h> 44#include <pg_config_manual.h>
42 45
43#define DEFAULT_DB "template1"
44#define DEFAULT_HOST "127.0.0.1" 46#define DEFAULT_HOST "127.0.0.1"
45 47
46/* return the PSQL server version as a 3-tuple */ 48/* return the PSQL server version as a 3-tuple */
47#define PSQL_SERVER_VERSION3(server_version) \ 49#define PSQL_SERVER_VERSION3(server_version) \
48 (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \ 50 (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \
49 (server_version) - (int)((server_version) / 100) * 100 51 (server_version) - (int)((server_version) / 100) * 100
50/* return true if the given host is a UNIX domain socket */ 52/* return true if the given host is a UNIX domain socket */
51#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host))) 53#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
52/* return a 3-tuple identifying a host/port independent of the socket type */ 54/* return a 3-tuple identifying a host/port independent of the socket type */
53#define PSQL_SOCKET3(host, port) \ 55#define PSQL_SOCKET3(host, port) \
54 ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port 56 ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \
57 PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port
55 58
56enum { 59typedef struct {
57 DEFAULT_PORT = 5432, 60 int errorcode;
58 DEFAULT_WARN = 2, 61 check_pgsql_config config;
59 DEFAULT_CRIT = 8 62} check_pgsql_config_wrapper;
60}; 63static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
61 64
62static int process_arguments(int /*argc*/, char ** /*argv*/);
63static void print_help(void); 65static void print_help(void);
64static bool is_pg_logname(char * /*username*/); 66static bool is_pg_logname(char * /*username*/);
65static int do_query(PGconn * /*conn*/, char * /*query*/); 67static mp_state_enum do_query(PGconn * /*conn*/, char * /*query*/, const char /*pgqueryname*/[],
68 thresholds * /*qthresholds*/, char * /*query_warning*/,
69 char * /*query_critical*/);
66void print_usage(void); 70void print_usage(void);
67 71
68static char *pghost = NULL; /* host name of the backend server */
69static char *pgport = NULL; /* port of the backend server */
70static char *pgoptions = NULL;
71static char *pgtty = NULL;
72static char dbName[NAMEDATALEN] = DEFAULT_DB;
73static char *pguser = NULL;
74static char *pgpasswd = NULL;
75static char *pgparams = NULL;
76static double twarn = (double)DEFAULT_WARN;
77static double tcrit = (double)DEFAULT_CRIT;
78static char *pgquery = NULL;
79static char *pgqueryname = NULL;
80static char *query_warning = NULL;
81static char *query_critical = NULL;
82static thresholds *qthresholds = NULL;
83static int verbose = 0; 72static int verbose = 0;
84 73
85#define OPTID_QUERYNAME -1000 74#define OPTID_QUERYNAME -1000
@@ -139,21 +128,19 @@ int main(int argc, char **argv) {
139 bindtextdomain(PACKAGE, LOCALEDIR); 128 bindtextdomain(PACKAGE, LOCALEDIR);
140 textdomain(PACKAGE); 129 textdomain(PACKAGE);
141 130
142 /* begin, by setting the parameters for a backend connection if the
143 * parameters are null, then the system will try to use reasonable
144 * defaults by looking up environment variables or, failing that,
145 * using hardwired constants */
146
147 pgoptions = NULL; /* special options to start up the backend server */
148 pgtty = NULL; /* debugging tty for the backend server */
149
150 /* Parse extra opts if any */ 131 /* Parse extra opts if any */
151 argv = np_extra_opts(&argc, argv, progname); 132 argv = np_extra_opts(&argc, argv, progname);
152 133
153 if (process_arguments(argc, argv) == ERROR) 134 check_pgsql_config_wrapper tmp_config = process_arguments(argc, argv);
135 if (tmp_config.errorcode == ERROR) {
154 usage4(_("Could not parse arguments")); 136 usage4(_("Could not parse arguments"));
155 if (verbose > 2) 137 }
138
139 const check_pgsql_config config = tmp_config.config;
140
141 if (verbose > 2) {
156 printf("Arguments initialized\n"); 142 printf("Arguments initialized\n");
143 }
157 144
158 /* Set signal handling and alarm */ 145 /* Set signal handling and alarm */
159 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 146 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
@@ -162,25 +149,33 @@ int main(int argc, char **argv) {
162 alarm(timeout_interval); 149 alarm(timeout_interval);
163 150
164 char *conninfo = NULL; 151 char *conninfo = NULL;
165 if (pgparams) 152 if (config.pgparams) {
166 asprintf(&conninfo, "%s ", pgparams); 153 asprintf(&conninfo, "%s ", config.pgparams);
167 154 }
168 asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", dbName); 155
169 if (pghost) 156 asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", config.dbName);
170 asprintf(&conninfo, "%s host = '%s'", conninfo, pghost); 157 if (config.pghost) {
171 if (pgport) 158 asprintf(&conninfo, "%s host = '%s'", conninfo, config.pghost);
172 asprintf(&conninfo, "%s port = '%s'", conninfo, pgport); 159 }
173 if (pgoptions) 160 if (config.pgport) {
174 asprintf(&conninfo, "%s options = '%s'", conninfo, pgoptions); 161 asprintf(&conninfo, "%s port = '%s'", conninfo, config.pgport);
162 }
163 if (config.pgoptions) {
164 asprintf(&conninfo, "%s options = '%s'", conninfo, config.pgoptions);
165 }
175 /* if (pgtty) -- ignored by PQconnectdb */ 166 /* if (pgtty) -- ignored by PQconnectdb */
176 if (pguser) 167 if (config.pguser) {
177 asprintf(&conninfo, "%s user = '%s'", conninfo, pguser); 168 asprintf(&conninfo, "%s user = '%s'", conninfo, config.pguser);
169 }
178 170
179 if (verbose) /* do not include password (see right below) in output */ 171 if (verbose) { /* do not include password (see right below) in output */
180 printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo, pgpasswd ? " password = <hidden>" : ""); 172 printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo,
173 config.pgpasswd ? " password = <hidden>" : "");
174 }
181 175
182 if (pgpasswd) 176 if (config.pgpasswd) {
183 asprintf(&conninfo, "%s password = '%s'", conninfo, pgpasswd); 177 asprintf(&conninfo, "%s password = '%s'", conninfo, config.pgpasswd);
178 }
184 179
185 /* make a connection to the database */ 180 /* make a connection to the database */
186 struct timeval start_timeval; 181 struct timeval start_timeval;
@@ -193,25 +188,27 @@ int main(int argc, char **argv) {
193 --end_timeval.tv_sec; 188 --end_timeval.tv_sec;
194 end_timeval.tv_usec += 1000000; 189 end_timeval.tv_usec += 1000000;
195 } 190 }
196 double elapsed_time = 191 double elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) +
197 (double)(end_timeval.tv_sec - start_timeval.tv_sec) + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0; 192 ((double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0);
198 193
199 if (verbose) 194 if (verbose) {
200 printf("Time elapsed: %f\n", elapsed_time); 195 printf("Time elapsed: %f\n", elapsed_time);
196 }
201 197
202 /* check to see that the backend connection was successfully made */ 198 /* check to see that the backend connection was successfully made */
203 if (verbose) 199 if (verbose) {
204 printf("Verifying connection\n"); 200 printf("Verifying connection\n");
201 }
205 if (PQstatus(conn) == CONNECTION_BAD) { 202 if (PQstatus(conn) == CONNECTION_BAD) {
206 printf(_("CRITICAL - no connection to '%s' (%s).\n"), dbName, PQerrorMessage(conn)); 203 printf(_("CRITICAL - no connection to '%s' (%s).\n"), config.dbName, PQerrorMessage(conn));
207 PQfinish(conn); 204 PQfinish(conn);
208 return STATE_CRITICAL; 205 return STATE_CRITICAL;
209 } 206 }
210 207
211 int status = STATE_UNKNOWN; 208 mp_state_enum status = STATE_UNKNOWN;
212 if (elapsed_time > tcrit) { 209 if (elapsed_time > config.tcrit) {
213 status = STATE_CRITICAL; 210 status = STATE_CRITICAL;
214 } else if (elapsed_time > twarn) { 211 } else if (elapsed_time > config.twarn) {
215 status = STATE_WARNING; 212 status = STATE_WARNING;
216 } else { 213 } else {
217 status = STATE_OK; 214 status = STATE_OK;
@@ -224,25 +221,29 @@ int main(int argc, char **argv) {
224 printf("Successfully connected to database %s (user %s) " 221 printf("Successfully connected to database %s (user %s) "
225 "at server %s%s%s (server version: %d.%d.%d, " 222 "at server %s%s%s (server version: %d.%d.%d, "
226 "protocol version: %d, pid: %d)\n", 223 "protocol version: %d, pid: %d)\n",
227 PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)), PSQL_SERVER_VERSION3(server_version), 224 PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)),
228 PQprotocolVersion(conn), PQbackendPID(conn)); 225 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn));
229 } 226 }
230 227
231 printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), dbName, elapsed_time, 228 printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), config.dbName, elapsed_time,
232 fperfdata("time", elapsed_time, "s", !!(twarn > 0.0), twarn, !!(tcrit > 0.0), tcrit, true, 0, false, 0)); 229 fperfdata("time", elapsed_time, "s", (config.twarn > 0.0), config.twarn,
230 (config.tcrit > 0.0), config.tcrit, true, 0, false, 0));
233 231
234 int query_status = STATE_UNKNOWN; 232 mp_state_enum query_status = STATE_UNKNOWN;
235 if (pgquery) 233 if (config.pgquery) {
236 query_status = do_query(conn, pgquery); 234 query_status = do_query(conn, config.pgquery, config.pgqueryname, config.qthresholds,
235 config.query_warning, config.query_critical);
236 }
237 237
238 if (verbose) 238 if (verbose) {
239 printf("Closing connection\n"); 239 printf("Closing connection\n");
240 }
240 PQfinish(conn); 241 PQfinish(conn);
241 return (pgquery && query_status > status) ? query_status : status; 242 return (config.pgquery && query_status > status) ? query_status : status;
242} 243}
243 244
244/* process command-line arguments */ 245/* process command-line arguments */
245int process_arguments(int argc, char **argv) { 246check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
246 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 247 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
247 {"version", no_argument, 0, 'V'}, 248 {"version", no_argument, 0, 'V'},
248 {"timeout", required_argument, 0, 't'}, 249 {"timeout", required_argument, 0, 't'},
@@ -262,12 +263,19 @@ int process_arguments(int argc, char **argv) {
262 {"verbose", no_argument, 0, 'v'}, 263 {"verbose", no_argument, 0, 'v'},
263 {0, 0, 0, 0}}; 264 {0, 0, 0, 0}};
264 265
266 check_pgsql_config_wrapper result = {
267 .errorcode = OK,
268 .config = check_pgsql_config_init(),
269 };
270
265 while (true) { 271 while (true) {
266 int option = 0; 272 int option = 0;
267 int option_char = getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option); 273 int option_char =
274 getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option);
268 275
269 if (option_char == EOF) 276 if (option_char == EOF) {
270 break; 277 break;
278 }
271 279
272 switch (option_char) { 280 switch (option_char) {
273 case '?': /* usage */ 281 case '?': /* usage */
@@ -279,68 +287,75 @@ int process_arguments(int argc, char **argv) {
279 print_revision(progname, NP_VERSION); 287 print_revision(progname, NP_VERSION);
280 exit(STATE_UNKNOWN); 288 exit(STATE_UNKNOWN);
281 case 't': /* timeout period */ 289 case 't': /* timeout period */
282 if (!is_integer(optarg)) 290 if (!is_integer(optarg)) {
283 usage2(_("Timeout interval must be a positive integer"), optarg); 291 usage2(_("Timeout interval must be a positive integer"), optarg);
284 else 292 } else {
285 timeout_interval = atoi(optarg); 293 timeout_interval = atoi(optarg);
294 }
286 break; 295 break;
287 case 'c': /* critical time threshold */ 296 case 'c': /* critical time threshold */
288 if (!is_nonnegative(optarg)) 297 if (!is_nonnegative(optarg)) {
289 usage2(_("Critical threshold must be a positive integer"), optarg); 298 usage2(_("Critical threshold must be a positive integer"), optarg);
290 else 299 } else {
291 tcrit = strtod(optarg, NULL); 300 result.config.tcrit = strtod(optarg, NULL);
301 }
292 break; 302 break;
293 case 'w': /* warning time threshold */ 303 case 'w': /* warning time threshold */
294 if (!is_nonnegative(optarg)) 304 if (!is_nonnegative(optarg)) {
295 usage2(_("Warning threshold must be a positive integer"), optarg); 305 usage2(_("Warning threshold must be a positive integer"), optarg);
296 else 306 } else {
297 twarn = strtod(optarg, NULL); 307 result.config.twarn = strtod(optarg, NULL);
308 }
298 break; 309 break;
299 case 'C': /* critical query threshold */ 310 case 'C': /* critical query threshold */
300 query_critical = optarg; 311 result.config.query_critical = optarg;
301 break; 312 break;
302 case 'W': /* warning query threshold */ 313 case 'W': /* warning query threshold */
303 query_warning = optarg; 314 result.config.query_warning = optarg;
304 break; 315 break;
305 case 'H': /* host */ 316 case 'H': /* host */
306 if ((*optarg != '/') && (!is_host(optarg))) 317 if ((*optarg != '/') && (!is_host(optarg))) {
307 usage2(_("Invalid hostname/address"), optarg); 318 usage2(_("Invalid hostname/address"), optarg);
308 else 319 } else {
309 pghost = optarg; 320 result.config.pghost = optarg;
321 }
310 break; 322 break;
311 case 'P': /* port */ 323 case 'P': /* port */
312 if (!is_integer(optarg)) 324 if (!is_integer(optarg)) {
313 usage2(_("Port must be a positive integer"), optarg); 325 usage2(_("Port must be a positive integer"), optarg);
314 else 326 } else {
315 pgport = optarg; 327 result.config.pgport = optarg;
328 }
316 break; 329 break;
317 case 'd': /* database name */ 330 case 'd': /* database name */
318 if (strlen(optarg) >= NAMEDATALEN) { 331 if (strlen(optarg) >= NAMEDATALEN) {
319 usage2(_("Database name exceeds the maximum length"), optarg); 332 usage2(_("Database name exceeds the maximum length"), optarg);
320 } 333 }
321 snprintf(dbName, NAMEDATALEN, "%s", optarg); 334 snprintf(result.config.dbName, NAMEDATALEN, "%s", optarg);
322 break; 335 break;
323 case 'l': /* login name */ 336 case 'l': /* login name */
324 if (!is_pg_logname(optarg)) 337 if (!is_pg_logname(optarg)) {
325 usage2(_("User name is not valid"), optarg); 338 usage2(_("User name is not valid"), optarg);
326 else 339 } else {
327 pguser = optarg; 340 result.config.pguser = optarg;
341 }
328 break; 342 break;
329 case 'p': /* authentication password */ 343 case 'p': /* authentication password */
330 case 'a': 344 case 'a':
331 pgpasswd = optarg; 345 result.config.pgpasswd = optarg;
332 break; 346 break;
333 case 'o': 347 case 'o':
334 if (pgparams) 348 if (result.config.pgparams) {
335 asprintf(&pgparams, "%s %s", pgparams, optarg); 349 asprintf(&result.config.pgparams, "%s %s", result.config.pgparams, optarg);
336 else 350 } else {
337 asprintf(&pgparams, "%s", optarg); 351 asprintf(&result.config.pgparams, "%s", optarg);
352 }
338 break; 353 break;
339 case 'q': 354 case 'q':
340 pgquery = optarg; 355 result.config.pgquery = optarg;
341 break; 356 break;
342 case OPTID_QUERYNAME: 357 case OPTID_QUERYNAME:
343 pgqueryname = optarg; 358 result.config.pgqueryname = optarg;
344 break; 359 break;
345 case 'v': 360 case 'v':
346 verbose++; 361 verbose++;
@@ -348,9 +363,10 @@ int process_arguments(int argc, char **argv) {
348 } 363 }
349 } 364 }
350 365
351 set_thresholds(&qthresholds, query_warning, query_critical); 366 set_thresholds(&result.config.qthresholds, result.config.query_warning,
367 result.config.query_critical);
352 368
353 return OK; 369 return result;
354} 370}
355 371
356/** 372/**
@@ -378,8 +394,9 @@ should be added.</para>
378******************************************************************************/ 394******************************************************************************/
379 395
380bool is_pg_logname(char *username) { 396bool is_pg_logname(char *username) {
381 if (strlen(username) > NAMEDATALEN - 1) 397 if (strlen(username) > NAMEDATALEN - 1) {
382 return (false); 398 return (false);
399 }
383 return (true); 400 return (true);
384} 401}
385 402
@@ -394,7 +411,7 @@ bool is_pg_logname(char *username) {
394void print_help(void) { 411void print_help(void) {
395 char *myport; 412 char *myport;
396 413
397 xasprintf(&myport, "%d", DEFAULT_PORT); 414 xasprintf(&myport, "%d", 5432);
398 415
399 print_revision(progname, NP_VERSION); 416 print_revision(progname, NP_VERSION);
400 417
@@ -447,29 +464,39 @@ void print_help(void) {
447 464
448 printf(" %s\n", _("If a query is specified using the -q option, it will be executed after")); 465 printf(" %s\n", _("If a query is specified using the -q option, it will be executed after"));
449 printf(" %s\n", _("connecting to the server. The result from the query has to be numeric.")); 466 printf(" %s\n", _("connecting to the server. The result from the query has to be numeric."));
450 printf(" %s\n", _("Multiple SQL commands, separated by semicolon, are allowed but the result ")); 467 printf(" %s\n",
468 _("Multiple SQL commands, separated by semicolon, are allowed but the result "));
451 printf(" %s\n", _("of the last command is taken into account only. The value of the first")); 469 printf(" %s\n", _("of the last command is taken into account only. The value of the first"));
452 printf(" %s\n", _("column in the first row is used as the check result. If a second column is")); 470 printf(" %s\n",
471 _("column in the first row is used as the check result. If a second column is"));
453 printf(" %s\n", _("present in the result set, this is added to the plugin output with a")); 472 printf(" %s\n", _("present in the result set, this is added to the plugin output with a"));
454 printf(" %s\n", _("prefix of \"Extra Info:\". This information can be displayed in the system")); 473 printf(" %s\n",
474 _("prefix of \"Extra Info:\". This information can be displayed in the system"));
455 printf(" %s\n\n", _("executing the plugin.")); 475 printf(" %s\n\n", _("executing the plugin."));
456 476
457 printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual")); 477 printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual"));
458 printf(" %s\n\n", _("for details about how to access internal statistics of the database server.")); 478 printf(" %s\n\n",
479 _("for details about how to access internal statistics of the database server."));
459 480
460 printf(" %s\n", _("For a list of available connection parameters which may be used with the -o")); 481 printf(" %s\n",
461 printf(" %s\n", _("command line option, see the documentation for PQconnectdb() in the chapter")); 482 _("For a list of available connection parameters which may be used with the -o"));
483 printf(" %s\n",
484 _("command line option, see the documentation for PQconnectdb() in the chapter"));
462 printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be")); 485 printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be"));
463 printf(" %s\n", _("used to specify a service name in pg_service.conf to be used for additional")); 486 printf(" %s\n",
487 _("used to specify a service name in pg_service.conf to be used for additional"));
464 printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:")); 488 printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:"));
465 printf(" %s\n\n", _("-o 'sslmode=require'.")); 489 printf(" %s\n\n", _("-o 'sslmode=require'."));
466 490
467 printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To")); 491 printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To"));
468 printf(" %s\n", _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP")); 492 printf(" %s\n",
493 _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP"));
469 printf(" %s\n\n", _("connections (start the postmaster with the -i option).")); 494 printf(" %s\n\n", _("connections (start the postmaster with the -i option)."));
470 495
471 printf(" %s\n", _("Typically, the monitoring user (unless the --logname option is used) should be")); 496 printf(" %s\n",
472 printf(" %s\n", _("able to connect to the database without a password. The plugin can also send")); 497 _("Typically, the monitoring user (unless the --logname option is used) should be"));
498 printf(" %s\n",
499 _("able to connect to the database without a password. The plugin can also send"));
473 printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password.")); 500 printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password."));
474 501
475 printf(UT_SUPPORT); 502 printf(UT_SUPPORT);
@@ -482,13 +509,16 @@ void print_usage(void) {
482 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); 509 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
483} 510}
484 511
485int do_query(PGconn *conn, char *query) { 512mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thresholds *qthresholds,
486 if (verbose) 513 char *query_warning, char *query_critical) {
514 if (verbose) {
487 printf("Executing SQL query \"%s\".\n", query); 515 printf("Executing SQL query \"%s\".\n", query);
516 }
488 PGresult *res = PQexec(conn, query); 517 PGresult *res = PQexec(conn, query);
489 518
490 if (PGRES_TUPLES_OK != PQresultStatus(res)) { 519 if (PGRES_TUPLES_OK != PQresultStatus(res)) {
491 printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), PQerrorMessage(conn)); 520 printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"),
521 PQerrorMessage(conn));
492 return STATE_CRITICAL; 522 return STATE_CRITICAL;
493 } 523 }
494 524
@@ -510,8 +540,9 @@ int do_query(PGconn *conn, char *query) {
510 540
511 char *endptr = NULL; 541 char *endptr = NULL;
512 double value = strtod(val_str, &endptr); 542 double value = strtod(val_str, &endptr);
513 if (verbose) 543 if (verbose) {
514 printf("Query result: %f\n", value); 544 printf("Query result: %f\n", value);
545 }
515 546
516 if (endptr == val_str) { 547 if (endptr == val_str) {
517 printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); 548 printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str);
@@ -519,11 +550,12 @@ int do_query(PGconn *conn, char *query) {
519 } 550 }
520 551
521 if ((endptr != NULL) && (*endptr != '\0')) { 552 if ((endptr != NULL) && (*endptr != '\0')) {
522 if (verbose) 553 if (verbose) {
523 printf("Garbage after value: %s.\n", endptr); 554 printf("Garbage after value: %s.\n", endptr);
555 }
524 } 556 }
525 557
526 int my_status = get_status(value, qthresholds); 558 mp_state_enum my_status = get_status(value, qthresholds);
527 printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK") 559 printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK")
528 : (my_status == STATE_WARNING) ? _("WARNING") 560 : (my_status == STATE_WARNING) ? _("WARNING")
529 : (my_status == STATE_CRITICAL) ? _("CRITICAL") 561 : (my_status == STATE_CRITICAL) ? _("CRITICAL")
@@ -534,7 +566,8 @@ int do_query(PGconn *conn, char *query) {
534 printf(_("'%s' returned %f"), query, value); 566 printf(_("'%s' returned %f"), query, value);
535 } 567 }
536 568
537 printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", query_critical ? query_critical : ""); 569 printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "",
570 query_critical ? query_critical : "");
538 if (PQnfields(res) > 1) { 571 if (PQnfields(res) > 1) {
539 char *extra_info = PQgetvalue(res, 0, 1); 572 char *extra_info = PQgetvalue(res, 0, 1);
540 if (extra_info != NULL) { 573 if (extra_info != NULL) {
diff --git a/plugins/check_pgsql.d/config.h b/plugins/check_pgsql.d/config.h
new file mode 100644
index 00000000..2d4b8b89
--- /dev/null
+++ b/plugins/check_pgsql.d/config.h
@@ -0,0 +1,61 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6#include <pg_config_manual.h>
7
8#define DEFAULT_DB "template1"
9
10enum {
11 DEFAULT_WARN = 2,
12 DEFAULT_CRIT = 8,
13};
14
15typedef struct {
16 char *pghost; /* host name of the backend server */
17 char *pgport; /* port of the backend server */
18 char *pgoptions; /* special options to start up the backend server */
19 char *pgtty; /* debugging tty for the backend server */
20 char dbName[NAMEDATALEN];
21 char *pguser;
22 char *pgpasswd;
23 char *pgparams;
24 char *pgquery;
25 char *pgqueryname;
26
27 double twarn;
28 double tcrit;
29 thresholds *qthresholds;
30 char *query_warning;
31 char *query_critical;
32} check_pgsql_config;
33
34/* begin, by setting the parameters for a backend connection if the
35 * parameters are null, then the system will try to use reasonable
36 * defaults by looking up environment variables or, failing that,
37 * using hardwired constants
38 * this targets .pgoptions and .pgtty
39 */
40
41check_pgsql_config check_pgsql_config_init() {
42 check_pgsql_config tmp = {
43 .pghost = NULL,
44 .pgport = NULL,
45 .pgoptions = NULL,
46 .pgtty = NULL,
47 .dbName = DEFAULT_DB,
48 .pguser = NULL,
49 .pgpasswd = NULL,
50 .pgparams = NULL,
51 .pgquery = NULL,
52 .pgqueryname = NULL,
53
54 .twarn = (double)DEFAULT_WARN,
55 .tcrit = (double)DEFAULT_CRIT,
56 .qthresholds = NULL,
57 .query_warning = NULL,
58 .query_critical = NULL,
59 };
60 return tmp;
61}
diff --git a/plugins/check_ping.c b/plugins/check_ping.c
index 4aafaf41..61feb958 100644
--- a/plugins/check_ping.c
+++ b/plugins/check_ping.c
@@ -36,61 +36,52 @@ const char *email = "devel@monitoring-plugins.org";
36#include "netutils.h" 36#include "netutils.h"
37#include "popen.h" 37#include "popen.h"
38#include "utils.h" 38#include "utils.h"
39#include "check_ping.d/config.h"
40#include "../lib/states.h"
39 41
40#include <signal.h> 42#include <signal.h>
41 43
42#define WARN_DUPLICATES "DUPLICATES FOUND! " 44#define WARN_DUPLICATES "DUPLICATES FOUND! "
43#define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
44 45
45enum { 46typedef struct {
46 UNKNOWN_PACKET_LOSS = 200, /* 200% */ 47 int errorcode;
47 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */ 48 check_ping_config config;
48}; 49} check_ping_config_wrapper;
50static check_ping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
51static check_ping_config_wrapper validate_arguments(check_ping_config_wrapper /*config_wrapper*/);
49 52
50static int process_arguments(int /*argc*/, char ** /*argv*/); 53static int get_threshold(char * /*arg*/, double * /*trta*/, int * /*tpl*/);
51static int get_threshold(char * /*arg*/, float * /*trta*/, int * /*tpl*/); 54
52static int validate_arguments(void); 55typedef struct {
53static int run_ping(const char *cmd, const char *addr); 56 mp_state_enum state;
54static int error_scan(char buf[MAX_INPUT_BUFFER], const char *addr); 57 double round_trip_average;
58 int packet_loss;
59} ping_result;
60static ping_result run_ping(const char *cmd, const char *addr, double /*crta*/);
61
62static mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr);
55static void print_help(void); 63static void print_help(void);
56void print_usage(void); 64void print_usage(void);
57 65
58static bool display_html = false;
59static int wpl = UNKNOWN_PACKET_LOSS;
60static int cpl = UNKNOWN_PACKET_LOSS;
61static float wrta = UNKNOWN_TRIP_TIME;
62static float crta = UNKNOWN_TRIP_TIME;
63static char **addresses = NULL;
64static int n_addresses = 0;
65static int max_addr = 1;
66static int max_packets = -1;
67static int verbose = 0; 66static int verbose = 0;
68 67
69static float rta = UNKNOWN_TRIP_TIME;
70static int pl = UNKNOWN_PACKET_LOSS;
71
72static char *warn_text; 68static char *warn_text;
73 69
74int main(int argc, char **argv) { 70int main(int argc, char **argv) {
75 char *cmd = NULL;
76 char *rawcmd = NULL;
77 int result = STATE_UNKNOWN;
78 int this_result = STATE_UNKNOWN;
79 int i;
80
81 setlocale(LC_ALL, ""); 71 setlocale(LC_ALL, "");
82 setlocale(LC_NUMERIC, "C"); 72 setlocale(LC_NUMERIC, "C");
83 bindtextdomain(PACKAGE, LOCALEDIR); 73 bindtextdomain(PACKAGE, LOCALEDIR);
84 textdomain(PACKAGE); 74 textdomain(PACKAGE);
85 75
86 addresses = malloc(sizeof(char *) * max_addr);
87 addresses[0] = NULL;
88
89 /* Parse extra opts if any */ 76 /* Parse extra opts if any */
90 argv = np_extra_opts(&argc, argv, progname); 77 argv = np_extra_opts(&argc, argv, progname);
91 78
92 if (process_arguments(argc, argv) == ERROR) 79 check_ping_config_wrapper tmp_config = process_arguments(argc, argv);
80 if (tmp_config.errorcode == ERROR) {
93 usage4(_("Could not parse arguments")); 81 usage4(_("Could not parse arguments"));
82 }
83
84 const check_ping_config config = tmp_config.config;
94 85
95 /* Set signal handling and alarm */ 86 /* Set signal handling and alarm */
96 if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) { 87 if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
@@ -105,71 +96,90 @@ int main(int argc, char **argv) {
105 alarm(timeout_interval); 96 alarm(timeout_interval);
106#endif 97#endif
107 98
108 for (i = 0; i < n_addresses; i++) { 99 int result = STATE_UNKNOWN;
109 100 char *rawcmd = NULL;
101 for (size_t i = 0; i < config.n_addresses; i++) {
110#ifdef PING6_COMMAND 102#ifdef PING6_COMMAND
111 if (address_family != AF_INET && is_inet6_addr(addresses[i])) 103 if (address_family != AF_INET && is_inet6_addr(config.addresses[i])) {
112 rawcmd = strdup(PING6_COMMAND); 104 rawcmd = strdup(PING6_COMMAND);
113 else 105 } else {
114 rawcmd = strdup(PING_COMMAND); 106 rawcmd = strdup(PING_COMMAND);
107 }
115#else 108#else
116 rawcmd = strdup(PING_COMMAND); 109 rawcmd = strdup(PING_COMMAND);
117#endif 110#endif
118 111
119 /* does the host address of number of packets argument come first? */ 112 char *cmd = NULL;
113
114 /* does the host address of number of packets argument come first? */
120#ifdef PING_PACKETS_FIRST 115#ifdef PING_PACKETS_FIRST
121# ifdef PING_HAS_TIMEOUT 116# ifdef PING_HAS_TIMEOUT
122 xasprintf(&cmd, rawcmd, timeout_interval, max_packets, addresses[i]); 117 xasprintf(&cmd, rawcmd, timeout_interval, config.max_packets, config.addresses[i]);
123# else 118# else
124 xasprintf(&cmd, rawcmd, max_packets, addresses[i]); 119 xasprintf(&cmd, rawcmd, config.max_packets, config.addresses[i]);
125# endif 120# endif
126#else 121#else
127 xasprintf(&cmd, rawcmd, addresses[i], max_packets); 122 xasprintf(&cmd, rawcmd, config.addresses[i], config.max_packets);
128#endif 123#endif
129 124
130 if (verbose >= 2) 125 if (verbose >= 2) {
131 printf("CMD: %s\n", cmd); 126 printf("CMD: %s\n", cmd);
127 }
132 128
133 /* run the command */ 129 /* run the command */
134 this_result = run_ping(cmd, addresses[i]);
135 130
136 if (pl == UNKNOWN_PACKET_LOSS || rta < 0.0) { 131 ping_result pinged = run_ping(cmd, config.addresses[i], config.crta);
132
133 if (pinged.packet_loss == UNKNOWN_PACKET_LOSS || pinged.round_trip_average < 0.0) {
137 printf("%s\n", cmd); 134 printf("%s\n", cmd);
138 die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n")); 135 die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n"));
139 } 136 }
140 137
141 if (pl >= cpl || rta >= crta || rta < 0) 138 if (pinged.packet_loss >= config.cpl || pinged.round_trip_average >= config.crta ||
142 this_result = STATE_CRITICAL; 139 pinged.round_trip_average < 0) {
143 else if (pl >= wpl || rta >= wrta) 140 pinged.state = STATE_CRITICAL;
144 this_result = STATE_WARNING; 141 } else if (pinged.packet_loss >= config.wpl || pinged.round_trip_average >= config.wrta) {
145 else if (pl >= 0 && rta >= 0) 142 pinged.state = STATE_WARNING;
146 this_result = max_state(STATE_OK, this_result); 143 } else if (pinged.packet_loss >= 0 && pinged.round_trip_average >= 0) {
147 144 pinged.state = max_state(STATE_OK, pinged.state);
148 if (n_addresses > 1 && this_result != STATE_UNKNOWN) 145 }
149 die(STATE_OK, "%s is alive\n", addresses[i]); 146
150 147 if (config.n_addresses > 1 && pinged.state != STATE_UNKNOWN) {
151 if (display_html == true) 148 die(STATE_OK, "%s is alive\n", config.addresses[i]);
152 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]); 149 }
153 if (pl == 100) 150
154 printf(_("PING %s - %sPacket loss = %d%%"), state_text(this_result), warn_text, pl); 151 if (config.display_html) {
155 else 152 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, config.addresses[i]);
156 printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(this_result), warn_text, pl, rta); 153 }
157 if (display_html == true) 154 if (pinged.packet_loss == 100) {
155 printf(_("PING %s - %sPacket loss = %d%%"), state_text(pinged.state), warn_text,
156 pinged.packet_loss);
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) {
158 printf("</A>"); 162 printf("</A>");
163 }
159 164
160 /* Print performance data */ 165 /* Print performance data */
161 if (pl != 100) { 166 if (pinged.packet_loss != 100) {
162 printf("|%s", 167 printf("|%s",
163 fperfdata("rta", (double)rta, "ms", wrta > 0 ? true : false, wrta, crta > 0 ? true : false, crta, true, 0, false, 0)); 168 fperfdata("rta", pinged.round_trip_average, "ms", (bool)(config.wrta > 0),
169 config.wrta, (bool)(config.crta > 0), config.crta, true, 0, false, 0));
164 } else { 170 } else {
165 printf("| rta=U;%f;%f;;", wrta, crta); 171 printf("| rta=U;%f;%f;;", config.wrta, config.crta);
166 } 172 }
167 printf(" %s\n", perfdata("pl", (long)pl, "%", wpl > 0 ? true : false, wpl, cpl > 0 ? true : false, cpl, true, 0, false, 0));
168 173
169 if (verbose >= 2) 174 printf(" %s\n",
170 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));
177
178 if (verbose >= 2) {
179 printf("%f:%d%% %f:%d%%\n", config.wrta, config.wpl, config.crta, config.cpl);
180 }
171 181
172 result = max_state(result, this_result); 182 result = max_state(result, pinged.state);
173 free(rawcmd); 183 free(rawcmd);
174 free(cmd); 184 free(cmd);
175 } 185 }
@@ -178,11 +188,7 @@ int main(int argc, char **argv) {
178} 188}
179 189
180/* process command-line arguments */ 190/* process command-line arguments */
181int process_arguments(int argc, char **argv) { 191check_ping_config_wrapper process_arguments(int argc, char **argv) {
182 int c = 1;
183 char *ptr;
184
185 int option = 0;
186 static struct option longopts[] = {STD_LONG_OPTS, 192 static struct option longopts[] = {STD_LONG_OPTS,
187 {"packets", required_argument, 0, 'p'}, 193 {"packets", required_argument, 0, 'p'},
188 {"nohtml", no_argument, 0, 'n'}, 194 {"nohtml", no_argument, 0, 'n'},
@@ -191,23 +197,35 @@ int process_arguments(int argc, char **argv) {
191 {"use-ipv6", no_argument, 0, '6'}, 197 {"use-ipv6", no_argument, 0, '6'},
192 {0, 0, 0, 0}}; 198 {0, 0, 0, 0}};
193 199
194 if (argc < 2) 200 check_ping_config_wrapper result = {
195 return ERROR; 201 .errorcode = OK,
202 .config = check_ping_config_init(),
203 };
204
205 if (argc < 2) {
206 result.errorcode = ERROR;
207 return result;
208 }
196 209
197 for (c = 1; c < argc; c++) { 210 for (int index = 1; index < argc; index++) {
198 if (strcmp("-to", argv[c]) == 0) 211 if (strcmp("-to", argv[index]) == 0) {
199 strcpy(argv[c], "-t"); 212 strcpy(argv[index], "-t");
200 if (strcmp("-nohtml", argv[c]) == 0) 213 }
201 strcpy(argv[c], "-n"); 214 if (strcmp("-nohtml", argv[index]) == 0) {
215 strcpy(argv[index], "-n");
216 }
202 } 217 }
203 218
204 while (1) { 219 int option = 0;
205 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);
206 223
207 if (c == -1 || c == EOF) 224 if (option_index == -1 || option_index == EOF) {
208 break; 225 break;
226 }
209 227
210 switch (c) { 228 switch (option_index) {
211 case '?': /* usage */ 229 case '?': /* usage */
212 usage5(); 230 usage5();
213 case 'h': /* help */ 231 case 'h': /* help */
@@ -234,17 +252,19 @@ int process_arguments(int argc, char **argv) {
234 usage(_("IPv6 support not available\n")); 252 usage(_("IPv6 support not available\n"));
235#endif 253#endif
236 break; 254 break;
237 case 'H': /* hostname */ 255 case 'H': /* hostname */ {
238 ptr = optarg; 256 char *ptr = optarg;
239 while (1) { 257 while (true) {
240 n_addresses++; 258 result.config.n_addresses++;
241 if (n_addresses > max_addr) { 259 if (result.config.n_addresses > max_addr) {
242 max_addr *= 2; 260 max_addr *= 2;
243 addresses = realloc(addresses, sizeof(char *) * max_addr); 261 result.config.addresses =
244 if (addresses == NULL) 262 realloc(result.config.addresses, sizeof(char *) * max_addr);
263 if (result.config.addresses == NULL) {
245 die(STATE_UNKNOWN, _("Could not realloc() addresses\n")); 264 die(STATE_UNKNOWN, _("Could not realloc() addresses\n"));
265 }
246 } 266 }
247 addresses[n_addresses - 1] = ptr; 267 result.config.addresses[result.config.n_addresses - 1] = ptr;
248 if ((ptr = index(ptr, ','))) { 268 if ((ptr = index(ptr, ','))) {
249 strcpy(ptr, ""); 269 strcpy(ptr, "");
250 ptr += sizeof(char); 270 ptr += sizeof(char);
@@ -252,219 +272,302 @@ int process_arguments(int argc, char **argv) {
252 break; 272 break;
253 } 273 }
254 } 274 }
255 break; 275 } break;
256 case 'p': /* number of packets to send */ 276 case 'p': /* number of packets to send */
257 if (is_intnonneg(optarg)) 277 if (is_intnonneg(optarg)) {
258 max_packets = atoi(optarg); 278 result.config.max_packets = atoi(optarg);
259 else 279 } else {
260 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg); 280 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg);
281 }
261 break; 282 break;
262 case 'n': /* no HTML */ 283 case 'n': /* no HTML */
263 display_html = false; 284 result.config.display_html = false;
264 break; 285 break;
265 case 'L': /* show HTML */ 286 case 'L': /* show HTML */
266 display_html = true; 287 result.config.display_html = true;
267 break; 288 break;
268 case 'c': 289 case 'c':
269 get_threshold(optarg, &crta, &cpl); 290 get_threshold(optarg, &result.config.crta, &result.config.cpl);
270 break; 291 break;
271 case 'w': 292 case 'w':
272 get_threshold(optarg, &wrta, &wpl); 293 get_threshold(optarg, &result.config.wrta, &result.config.wpl);
273 break; 294 break;
274 } 295 }
275 } 296 }
276 297
277 c = optind; 298 int arg_counter = optind;
278 if (c == argc) 299 if (arg_counter == argc) {
279 return validate_arguments(); 300 return validate_arguments(result);
301 }
280 302
281 if (addresses[0] == NULL) { 303 if (result.config.addresses[0] == NULL) {
282 if (!is_host(argv[c])) { 304 if (!is_host(argv[arg_counter])) {
283 usage2(_("Invalid hostname/address"), argv[c]); 305 usage2(_("Invalid hostname/address"), argv[arg_counter]);
284 } else { 306 } else {
285 addresses[0] = argv[c++]; 307 result.config.addresses[0] = argv[arg_counter++];
286 n_addresses++; 308 result.config.n_addresses++;
287 if (c == argc) 309 if (arg_counter == argc) {
288 return validate_arguments(); 310 return validate_arguments(result);
311 }
289 } 312 }
290 } 313 }
291 314
292 if (wpl == UNKNOWN_PACKET_LOSS) { 315 if (result.config.wpl == UNKNOWN_PACKET_LOSS) {
293 if (!is_intpercent(argv[c])) { 316 if (!is_intpercent(argv[arg_counter])) {
294 printf(_("<wpl> (%s) must be an integer percentage\n"), argv[c]); 317 printf(_("<wpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
295 return ERROR; 318 result.errorcode = ERROR;
296 } else { 319 return result;
297 wpl = atoi(argv[c++]); 320 }
298 if (c == argc) 321 result.config.wpl = atoi(argv[arg_counter++]);
299 return validate_arguments(); 322 if (arg_counter == argc) {
323 return validate_arguments(result);
300 } 324 }
301 } 325 }
302 326
303 if (cpl == UNKNOWN_PACKET_LOSS) { 327 if (result.config.cpl == UNKNOWN_PACKET_LOSS) {
304 if (!is_intpercent(argv[c])) { 328 if (!is_intpercent(argv[arg_counter])) {
305 printf(_("<cpl> (%s) must be an integer percentage\n"), argv[c]); 329 printf(_("<cpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
306 return ERROR; 330 result.errorcode = ERROR;
307 } else { 331 return result;
308 cpl = atoi(argv[c++]); 332 }
309 if (c == argc) 333 result.config.cpl = atoi(argv[arg_counter++]);
310 return validate_arguments(); 334 if (arg_counter == argc) {
335 return validate_arguments(result);
311 } 336 }
312 } 337 }
313 338
314 if (wrta < 0.0) { 339 if (result.config.wrta < 0.0) {
315 if (is_negative(argv[c])) { 340 if (is_negative(argv[arg_counter])) {
316 printf(_("<wrta> (%s) must be a non-negative number\n"), argv[c]); 341 printf(_("<wrta> (%s) must be a non-negative number\n"), argv[arg_counter]);
317 return ERROR; 342 result.errorcode = ERROR;
318 } else { 343 return result;
319 wrta = atof(argv[c++]); 344 }
320 if (c == argc) 345 result.config.wrta = atof(argv[arg_counter++]);
321 return validate_arguments(); 346 if (arg_counter == argc) {
347 return validate_arguments(result);
322 } 348 }
323 } 349 }
324 350
325 if (crta < 0.0) { 351 if (result.config.crta < 0.0) {
326 if (is_negative(argv[c])) { 352 if (is_negative(argv[arg_counter])) {
327 printf(_("<crta> (%s) must be a non-negative number\n"), argv[c]); 353 printf(_("<crta> (%s) must be a non-negative number\n"), argv[arg_counter]);
328 return ERROR; 354 result.errorcode = ERROR;
329 } else { 355 return result;
330 crta = atof(argv[c++]); 356 }
331 if (c == argc) 357 result.config.crta = atof(argv[arg_counter++]);
332 return validate_arguments(); 358 if (arg_counter == argc) {
359 return validate_arguments(result);
333 } 360 }
334 } 361 }
335 362
336 if (max_packets == -1) { 363 if (result.config.max_packets == -1) {
337 if (is_intnonneg(argv[c])) { 364 if (is_intnonneg(argv[arg_counter])) {
338 max_packets = atoi(argv[c++]); 365 result.config.max_packets = atoi(argv[arg_counter++]);
339 } else { 366 } else {
340 printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[c]); 367 printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[arg_counter]);
341 return ERROR; 368 result.errorcode = ERROR;
369 return result;
342 } 370 }
343 } 371 }
344 372
345 return validate_arguments(); 373 return validate_arguments(result);
346} 374}
347 375
348int get_threshold(char *arg, float *trta, int *tpl) { 376int get_threshold(char *arg, double *trta, int *tpl) {
349 if (is_intnonneg(arg) && sscanf(arg, "%f", trta) == 1) 377 if (is_intnonneg(arg) && sscanf(arg, "%lf", trta) == 1) {
350 return OK; 378 return OK;
351 else if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%f%*[:,]%d%%", trta, tpl) == 2) 379 }
380
381 if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%lf%*[:,]%d%%", trta, tpl) == 2) {
352 return OK; 382 return OK;
353 else if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) 383 }
384
385 if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) {
354 return OK; 386 return OK;
387 }
355 388
356 usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg); 389 usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg);
357 return STATE_UNKNOWN; 390 return STATE_UNKNOWN;
358} 391}
359 392
360int validate_arguments() { 393check_ping_config_wrapper validate_arguments(check_ping_config_wrapper config_wrapper) {
361 float max_seconds; 394 if (config_wrapper.config.wrta < 0.0) {
362 int i;
363
364 if (wrta < 0.0) {
365 printf(_("<wrta> was not set\n")); 395 printf(_("<wrta> was not set\n"));
366 return ERROR; 396 config_wrapper.errorcode = ERROR;
367 } else if (crta < 0.0) { 397 return config_wrapper;
398 }
399
400 if (config_wrapper.config.crta < 0.0) {
368 printf(_("<crta> was not set\n")); 401 printf(_("<crta> was not set\n"));
369 return ERROR; 402 config_wrapper.errorcode = ERROR;
370 } else if (wpl == UNKNOWN_PACKET_LOSS) { 403 return config_wrapper;
404 }
405
406 if (config_wrapper.config.wpl == UNKNOWN_PACKET_LOSS) {
371 printf(_("<wpl> was not set\n")); 407 printf(_("<wpl> was not set\n"));
372 return ERROR; 408 config_wrapper.errorcode = ERROR;
373 } else if (cpl == UNKNOWN_PACKET_LOSS) { 409 return config_wrapper;
410 }
411
412 if (config_wrapper.config.cpl == UNKNOWN_PACKET_LOSS) {
374 printf(_("<cpl> was not set\n")); 413 printf(_("<cpl> was not set\n"));
375 return ERROR; 414 config_wrapper.errorcode = ERROR;
376 } else if (wrta > crta) { 415 return config_wrapper;
377 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta); 416 }
378 return ERROR; 417
379 } else if (wpl > cpl) { 418 if (config_wrapper.config.wrta > config_wrapper.config.crta) {
380 printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl); 419 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), config_wrapper.config.wrta,
381 return ERROR; 420 config_wrapper.config.crta);
421 config_wrapper.errorcode = ERROR;
422 return config_wrapper;
382 } 423 }
383 424
384 if (max_packets == -1) 425 if (config_wrapper.config.wpl > config_wrapper.config.cpl) {
385 max_packets = DEFAULT_MAX_PACKETS; 426 printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), config_wrapper.config.wpl,
427 config_wrapper.config.cpl);
428 config_wrapper.errorcode = ERROR;
429 return config_wrapper;
430 }
386 431
387 max_seconds = crta / 1000.0 * max_packets + max_packets; 432 if (config_wrapper.config.max_packets == -1) {
388 if (max_seconds > timeout_interval) 433 config_wrapper.config.max_packets = DEFAULT_MAX_PACKETS;
389 timeout_interval = (int)max_seconds; 434 }
390 435
391 for (i = 0; i < n_addresses; i++) { 436 double max_seconds = (config_wrapper.config.crta / 1000.0 * config_wrapper.config.max_packets) +
392 if (!is_host(addresses[i])) 437 config_wrapper.config.max_packets;
393 usage2(_("Invalid hostname/address"), addresses[i]); 438 if (max_seconds > timeout_interval) {
439 timeout_interval = (unsigned int)max_seconds;
440 }
441
442 for (size_t i = 0; i < config_wrapper.config.n_addresses; i++) {
443 if (!is_host(config_wrapper.config.addresses[i])) {
444 usage2(_("Invalid hostname/address"), config_wrapper.config.addresses[i]);
445 }
394 } 446 }
395 447
396 if (n_addresses == 0) { 448 if (config_wrapper.config.n_addresses == 0) {
397 usage(_("You must specify a server address or host name")); 449 usage(_("You must specify a server address or host name"));
398 } 450 }
399 451
400 return OK; 452 return config_wrapper;
401} 453}
402 454
403int run_ping(const char *cmd, const char *addr) { 455ping_result run_ping(const char *cmd, const char *addr, double crta) {
404 char buf[MAX_INPUT_BUFFER]; 456 if ((child_process = spopen(cmd)) == NULL) {
405 int result = STATE_UNKNOWN;
406 int match;
407
408 if ((child_process = spopen(cmd)) == NULL)
409 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 457 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
458 }
410 459
411 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); 460 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
412 if (child_stderr == NULL) 461 if (child_stderr == NULL) {
413 printf(_("Cannot open stderr for %s\n"), cmd); 462 printf(_("Cannot open stderr for %s\n"), cmd);
463 }
414 464
415 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) { 465 char buf[MAX_INPUT_BUFFER];
466 ping_result result = {
467 .state = STATE_UNKNOWN,
468 .packet_loss = UNKNOWN_PACKET_LOSS,
469 .round_trip_average = UNKNOWN_TRIP_TIME,
470 };
416 471
417 if (verbose >= 3) 472 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) {
473 if (verbose >= 3) {
418 printf("Output: %s", buf); 474 printf("Output: %s", buf);
475 }
419 476
420 result = max_state(result, error_scan(buf, addr)); 477 result.state = max_state(result.state, error_scan(buf, addr));
421 478
422 /* get the percent loss statistics */ 479 /* get the percent loss statistics */
423 match = 0; 480 int match = 0;
424 if ((sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 481 if ((sscanf(
425 (sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss%n", &pl, &match) && match) || 482 buf,
426 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n", &pl, &match) && match) || 483 "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n",
427 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n", &pl, &match) && match) || 484 &result.packet_loss, &match) == 1 &&
428 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n", &pl, &match) && match) || 485 match) ||
429 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n", &pl, &match) && match) || 486 (sscanf(buf,
430 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n", &pl, &match) && match) || 487 "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet "
431 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 488 "loss%n",
432 (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 489 &result.packet_loss, &match) == 1 &&
433 (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &pl, &match) && match)) 490 match) ||
491 (sscanf(buf,
492 "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n",
493 &result.packet_loss, &match) == 1 &&
494 match) ||
495 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n",
496 &result.packet_loss, &match) == 1 &&
497 match) ||
498 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n",
499 &result.packet_loss, &match) == 1 &&
500 match) ||
501 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n",
502 &result.packet_loss, &match) == 1 &&
503 match) ||
504 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n",
505 &result.packet_loss, &match) == 1 &&
506 match) == 1 ||
507 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n",
508 &result.packet_loss, &match) == 1 &&
509 match) ||
510 (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n",
511 &result.packet_loss, &match) == 1 &&
512 match) ||
513 (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &result.packet_loss, &match) == 1 && match)) {
434 continue; 514 continue;
515 }
435 516
436 /* get the round trip average */ 517 /* get the round trip average */
437 else if ((sscanf(buf, "round-trip min/avg/max = %*f/%f/%*f%n", &rta, &match) && match) || 518 if ((sscanf(buf, "round-trip min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average,
438 (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 519 &match) == 1 &&
439 (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 520 match) ||
440 (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 521 (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%lf/%*f/%*f%n",
441 (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 522 &result.round_trip_average, &match) == 1 &&
442 (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%f/%*f%n", &rta, &match) && match) || 523 match) ||
443 (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 524 (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%lf/%*f/%*f%n",
444 (sscanf(buf, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms%n", &rta, &match) && match) || 525 &result.round_trip_average, &match) == 1 &&
445 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %fms%n", &rta, &match) && match)) 526 match) ||
527 (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%lf/%*f/%*f%n",
528 &result.round_trip_average, &match) == 1 &&
529 match) ||
530 (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%lf/%*f/%*f%n",
531 &result.round_trip_average, &match) == 1 &&
532 match) ||
533 (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average,
534 &match) == 1 &&
535 match) ||
536 (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%lf/%*f/%*f%n",
537 &result.round_trip_average, &match) == 1 &&
538 match) ||
539 (sscanf(buf, "rtt min/avg/max/mdev = %*f/%lf/%*f/%*f ms%n", &result.round_trip_average,
540 &match) == 1 &&
541 match) ||
542 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %lfms%n",
543 &result.round_trip_average, &match) == 1 &&
544 match)) {
446 continue; 545 continue;
546 }
447 } 547 }
448 548
449 /* this is needed because there is no rta if all packets are lost */ 549 /* this is needed because there is no rta if all packets are lost */
450 if (pl == 100) 550 if (result.packet_loss == 100) {
451 rta = crta; 551 result.round_trip_average = crta;
552 }
452 553
453 /* check stderr, setting at least WARNING if there is output here */ 554 /* check stderr, setting at least WARNING if there is output here */
454 /* Add warning into warn_text */ 555 /* Add warning into warn_text */
455 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) { 556 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
456 if (!strstr(buf, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") && !strstr(buf, "Warning: time of day goes back") 557 if (!strstr(buf, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") &&
558 !strstr(buf, "Warning: time of day goes back")
457 559
458 ) { 560 ) {
459 if (verbose >= 3) { 561 if (verbose >= 3) {
460 printf("Got stderr: %s", buf); 562 printf("Got stderr: %s", buf);
461 } 563 }
462 if ((result = error_scan(buf, addr)) == STATE_OK) { 564 if ((result.state = error_scan(buf, addr)) == STATE_OK) {
463 result = STATE_WARNING; 565 result.state = STATE_WARNING;
464 if (warn_text == NULL) { 566 if (warn_text == NULL) {
465 warn_text = strdup(_("System call sent warnings to stderr ")); 567 warn_text = strdup(_("System call sent warnings to stderr "));
466 } else { 568 } else {
467 xasprintf(&warn_text, "%s %s", warn_text, _("System call sent warnings to stderr ")); 569 xasprintf(&warn_text, "%s %s", warn_text,
570 _("System call sent warnings to stderr "));
468 } 571 }
469 } 572 }
470 } 573 }
@@ -474,43 +577,48 @@ int run_ping(const char *cmd, const char *addr) {
474 577
475 spclose(child_process); 578 spclose(child_process);
476 579
477 if (warn_text == NULL) 580 if (warn_text == NULL) {
478 warn_text = strdup(""); 581 warn_text = strdup("");
582 }
479 583
480 return result; 584 return result;
481} 585}
482 586
483int error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) { 587mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) {
484 if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") || strstr(buf, "No route")) 588 if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") ||
589 strstr(buf, "No route")) {
485 die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr); 590 die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr);
486 else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) 591 } else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) {
487 die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr); 592 die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr);
488 else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) 593 } else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) {
489 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr); 594 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr);
490 else if (strstr(buf, "Destination Protocol Unreachable")) 595 } else if (strstr(buf, "Destination Protocol Unreachable")) {
491 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr); 596 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr);
492 else if (strstr(buf, "Destination Net Prohibited")) 597 } else if (strstr(buf, "Destination Net Prohibited")) {
493 die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr); 598 die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr);
494 else if (strstr(buf, "Destination Host Prohibited")) 599 } else if (strstr(buf, "Destination Host Prohibited")) {
495 die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr); 600 die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr);
496 else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) 601 } else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) {
497 die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr); 602 die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr);
498 else if (strstr(buf, "unknown host")) 603 } else if (strstr(buf, "unknown host")) {
499 die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr); 604 die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr);
500 else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) 605 } else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) {
501 die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr); 606 die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr);
502 else if (strstr(buf, "Destination unreachable: ")) 607 } else if (strstr(buf, "Destination unreachable: ")) {
503 die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr); 608 die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr);
609 }
504 610
505 if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) { 611 if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) {
506 if (warn_text == NULL) 612 if (warn_text == NULL) {
507 warn_text = strdup(_(WARN_DUPLICATES)); 613 warn_text = strdup(_(WARN_DUPLICATES));
508 else if (!strstr(warn_text, _(WARN_DUPLICATES)) && xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) 614 } else if (!strstr(warn_text, _(WARN_DUPLICATES)) &&
615 xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) {
509 die(STATE_UNKNOWN, _("Unable to realloc warn_text\n")); 616 die(STATE_UNKNOWN, _("Unable to realloc warn_text\n"));
510 return (STATE_WARNING); 617 }
618 return STATE_WARNING;
511 } 619 }
512 620
513 return (STATE_OK); 621 return STATE_OK;
514} 622}
515 623
516void print_help(void) { 624void print_help(void) {
@@ -550,10 +658,10 @@ void print_help(void) {
550 printf("%s\n", _("percentage of packet loss to trigger an alarm state.")); 658 printf("%s\n", _("percentage of packet loss to trigger an alarm state."));
551 659
552 printf("\n"); 660 printf("\n");
553 printf("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss")); 661 printf("%s\n",
554 printf("%s\n", _("(percentage) and round trip average (milliseconds). It can produce HTML output")); 662 _("This plugin uses the ping command to probe the specified host for packet loss"));
555 printf("%s\n", _("linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in")); 663 printf("%s\n",
556 printf("%s\n", _("the contrib area of the downloads section at http://www.nagios.org/")); 664 _("(percentage) and round trip average (milliseconds). It can produce HTML output."));
557 665
558 printf(UT_SUPPORT); 666 printf(UT_SUPPORT);
559} 667}
diff --git a/plugins/check_ping.d/config.h b/plugins/check_ping.d/config.h
new file mode 100644
index 00000000..eb2735a7
--- /dev/null
+++ b/plugins/check_ping.d/config.h
@@ -0,0 +1,46 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7enum {
8 UNKNOWN_PACKET_LOSS = 200, /* 200% */
9 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */
10};
11
12#define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
13
14#define MAX_ADDR_START 1
15
16typedef struct {
17 bool display_html;
18 int max_packets;
19
20 char **addresses;
21 size_t n_addresses;
22
23 int wpl;
24 int cpl;
25 double wrta;
26 double crta;
27} check_ping_config;
28
29check_ping_config check_ping_config_init() {
30 check_ping_config tmp = {
31 .display_html = false,
32 .max_packets = -1,
33
34 .addresses = NULL,
35 .n_addresses = 0,
36
37 .wpl = UNKNOWN_PACKET_LOSS,
38 .cpl = UNKNOWN_PACKET_LOSS,
39 .wrta = UNKNOWN_TRIP_TIME,
40 .crta = UNKNOWN_TRIP_TIME,
41 };
42
43 tmp.addresses = calloc(MAX_ADDR_START, sizeof(char *));
44 tmp.addresses[0] = NULL;
45 return tmp;
46}
diff --git a/plugins/check_procs.c b/plugins/check_procs.c
index 1d78ccee..ae6e9c23 100644
--- a/plugins/check_procs.c
+++ b/plugins/check_procs.c
@@ -1,41 +1,41 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_procs plugin 3 * Monitoring check_procs plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_procs plugin 10 * This file contains the check_procs plugin
11* 11 *
12* Checks all processes and generates WARNING or CRITICAL states if the 12 * Checks all processes and generates WARNING or CRITICAL states if the
13* specified metric is outside the required threshold ranges. The metric 13 * specified metric is outside the required threshold ranges. The metric
14* defaults to number of processes. Search filters can be applied to limit 14 * defaults to number of processes. Search filters can be applied to limit
15* the processes to check. 15 * the processes to check.
16* 16 *
17* The parent process, check_procs itself and any child process of 17 * The parent process, check_procs itself and any child process of
18* check_procs (ps) are excluded from any checks to prevent false positives. 18 * check_procs (ps) are excluded from any checks to prevent false positives.
19* 19 *
20* 20 *
21* This program is free software: you can redistribute it and/or modify 21 * This program is free software: you can redistribute it and/or modify
22* it under the terms of the GNU General Public License as published by 22 * it under the terms of the GNU General Public License as published by
23* the Free Software Foundation, either version 3 of the License, or 23 * the Free Software Foundation, either version 3 of the License, or
24* (at your option) any later version. 24 * (at your option) any later version.
25* 25 *
26* This program is distributed in the hope that it will be useful, 26 * This program is distributed in the hope that it will be useful,
27* but WITHOUT ANY WARRANTY; without even the implied warranty of 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29* GNU General Public License for more details. 29 * GNU General Public License for more details.
30* 30 *
31* You should have received a copy of the GNU General Public License 31 * You should have received a copy of the GNU General Public License
32* along with this program. If not, see <http://www.gnu.org/licenses/>. 32 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33* 33 *
34* 34 *
35*****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_procs"; 37const char *progname = "check_procs";
38const char *program_name = "check_procs"; /* Required for coreutils libs */ 38const char *program_name = "check_procs"; /* Required for coreutils libs */
39const char *copyright = "2000-2024"; 39const char *copyright = "2000-2024";
40const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
41 41
@@ -43,313 +43,297 @@ const char *email = "devel@monitoring-plugins.org";
43#include "utils.h" 43#include "utils.h"
44#include "utils_cmd.h" 44#include "utils_cmd.h"
45#include "regex.h" 45#include "regex.h"
46#include "states.h"
47#include "check_procs.d/config.h"
46 48
47#include <pwd.h> 49#include <pwd.h>
48#include <errno.h> 50#include <errno.h>
49 51
50#ifdef HAVE_SYS_STAT_H 52#ifdef HAVE_SYS_STAT_H
51#include <sys/stat.h> 53# include <sys/stat.h>
52#endif 54#endif
53 55
54static int process_arguments (int /*argc*/, char ** /*argv*/); 56typedef struct {
55static int validate_arguments (void); 57 int errorcode;
56static int convert_to_seconds (char * /*etime*/); 58 check_procs_config config;
57static void print_help (void); 59} check_procs_config_wrapper;
58void print_usage (void); 60static check_procs_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
59 61static check_procs_config_wrapper validate_arguments(check_procs_config_wrapper /*config_wrapper*/);
60static char *warning_range = NULL; 62
61static char *critical_range = NULL; 63static int convert_to_seconds(char * /*etime*/, enum metric /*metric*/);
62static thresholds *procs_thresholds = NULL; 64static void print_help(void);
63 65void print_usage(void);
64static int options = 0; /* bitmask of filter criteria to test against */ 66
65#define ALL 1 67#define ALL 1
66#define STAT 2 68#define STAT 2
67#define PPID 4 69#define PPID 4
68#define USER 8 70#define USER 8
69#define PROG 16 71#define PROG 16
70#define ARGS 32 72#define ARGS 32
71#define VSZ 64 73#define VSZ 64
72#define RSS 128 74#define RSS 128
73#define PCPU 256 75#define PCPU 256
74#define ELAPSED 512 76#define ELAPSED 512
75#define EREG_ARGS 1024 77#define EREG_ARGS 1024
76#define EXCLUDE_PROGS 2048 78#define EXCLUDE_PROGS 2048
77 79
78#define KTHREAD_PARENT "kthreadd" /* the parent process of kernel threads: 80#define KTHREAD_PARENT \
79 ppid of procs are compared to pid of this proc*/ 81 "kthreadd" /* the parent process of kernel threads: \
80 82 ppid of procs are compared to pid of this proc*/
81/* Different metrics */
82char *metric_name;
83enum metric {
84 METRIC_PROCS,
85 METRIC_VSZ,
86 METRIC_RSS,
87 METRIC_CPU,
88 METRIC_ELAPSED
89};
90enum metric metric = METRIC_PROCS;
91 83
92static int verbose = 0; 84static int verbose = 0;
93static int uid; 85
94static pid_t ppid; 86static int stat_exe(const pid_t pid, struct stat *buf) {
95static int vsz;
96static int rss;
97static float pcpu;
98static char *statopts;
99static char *prog;
100static char *exclude_progs;
101static char **exclude_progs_arr = NULL;
102static char exclude_progs_counter = 0;
103static char *args;
104static char *input_filename = NULL;
105static regex_t re_args;
106static char *fmt;
107static char *fails;
108static char tmp[MAX_INPUT_BUFFER];
109static int kthread_filter = 0;
110static int usepid = 0; /* whether to test for pid or /proc/pid/exe */
111
112static int
113stat_exe (const pid_t pid, struct stat *buf) {
114 char *path; 87 char *path;
115 int ret;
116 xasprintf(&path, "/proc/%d/exe", pid); 88 xasprintf(&path, "/proc/%d/exe", pid);
117 ret = stat(path, buf); 89 int ret = stat(path, buf);
118 free(path); 90 free(path);
119 return ret; 91 return ret;
120} 92}
121 93
122 94int main(int argc, char **argv) {
123int 95 setlocale(LC_ALL, "");
124main (int argc, char **argv)
125{
126 char *input_buffer;
127 char *input_line;
128 char *procprog;
129
130 pid_t mypid = 0;
131 pid_t myppid = 0;
132 struct stat statbuf;
133 dev_t mydev = 0;
134 ino_t myino = 0;
135 int procuid = 0;
136 pid_t procpid = 0;
137 pid_t procppid = 0;
138 pid_t kthread_ppid = 0;
139 int procvsz = 0;
140 int procrss = 0;
141 int procseconds = 0;
142 float procpcpu = 0;
143 char procstat[8];
144 char procetime[MAX_INPUT_BUFFER] = { '\0' };
145 char *procargs;
146
147 const char *zombie = "Z";
148
149 int resultsum = 0; /* bitmask of the filter criteria met by a process */
150 int found = 0; /* counter for number of lines returned in `ps` output */
151 int procs = 0; /* counter for number of processes meeting filter criteria */
152 int pos; /* number of spaces before 'args' in `ps` output */
153 int cols; /* number of columns in ps output */
154 int expected_cols = PS_COLS - 1;
155 int warn = 0; /* number of processes in warn state */
156 int crit = 0; /* number of processes in crit state */
157 int i = 0;
158 int result = STATE_UNKNOWN;
159 int ret = 0;
160 output chld_out, chld_err;
161
162 setlocale (LC_ALL, "");
163 bindtextdomain (PACKAGE, LOCALEDIR);
164 textdomain (PACKAGE);
165 setlocale(LC_NUMERIC, "POSIX"); 96 setlocale(LC_NUMERIC, "POSIX");
166 97 bindtextdomain(PACKAGE, LOCALEDIR);
167 input_buffer = malloc (MAX_INPUT_BUFFER); 98 textdomain(PACKAGE);
168 procprog = malloc (MAX_INPUT_BUFFER);
169
170 xasprintf (&metric_name, "PROCS");
171 metric = METRIC_PROCS;
172 99
173 /* Parse extra opts if any */ 100 /* Parse extra opts if any */
174 argv=np_extra_opts (&argc, argv, progname); 101 argv = np_extra_opts(&argc, argv, progname);
102
103 check_procs_config_wrapper tmp_config = process_arguments(argc, argv);
104 if (tmp_config.errorcode == ERROR) {
105 usage4(_("Could not parse arguments"));
106 }
175 107
176 if (process_arguments (argc, argv) == ERROR) 108 check_procs_config config = tmp_config.config;
177 usage4 (_("Could not parse arguments"));
178 109
179 /* find ourself */ 110 /* find ourself */
180 mypid = getpid(); 111 pid_t mypid = getpid();
181 myppid = getppid(); 112 pid_t myppid = getppid();
182 if (usepid || stat_exe(mypid, &statbuf) == -1) { 113 dev_t mydev = 0;
114 ino_t myino = 0;
115 struct stat statbuf;
116 if (config.usepid || stat_exe(mypid, &statbuf) == -1) {
183 /* usepid might have been set by -T */ 117 /* usepid might have been set by -T */
184 usepid = 1; 118 config.usepid = true;
185 } else { 119 } else {
186 usepid = 0; 120 config.usepid = false;
187 mydev = statbuf.st_dev; 121 mydev = statbuf.st_dev;
188 myino = statbuf.st_ino; 122 myino = statbuf.st_ino;
189 } 123 }
190 124
191 /* Set signal handling and alarm timeout */ 125 /* Set signal handling and alarm timeout */
192 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 126 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
193 die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); 127 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
194 } 128 }
195 (void) alarm ((unsigned) timeout_interval); 129 (void)alarm(timeout_interval);
196 130
197 if (verbose >= 2) 131 if (verbose >= 2) {
198 printf (_("CMD: %s\n"), PS_COMMAND); 132 printf(_("CMD: %s\n"), PS_COMMAND);
133 }
199 134
200 if (input_filename == NULL) { 135 output chld_out;
201 result = cmd_run( PS_COMMAND, &chld_out, &chld_err, 0); 136 output chld_err;
137 mp_state_enum result = STATE_UNKNOWN;
138 if (config.input_filename == NULL) {
139 result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0);
202 if (chld_err.lines > 0) { 140 if (chld_err.lines > 0) {
203 printf ("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); 141 printf("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]);
204 exit(STATE_WARNING); 142 exit(STATE_WARNING);
205 } 143 }
206 } else { 144 } else {
207 result = cmd_file_read( input_filename, &chld_out, 0); 145 result = cmd_file_read(config.input_filename, &chld_out, 0);
208 } 146 }
209 147
148 int pos; /* number of spaces before 'args' in `ps` output */
149 uid_t procuid = 0;
150 pid_t procpid = 0;
151 pid_t procppid = 0;
152 pid_t kthread_ppid = 0;
153 int warn = 0; /* number of processes in warn state */
154 int crit = 0; /* number of processes in crit state */
155 int procvsz = 0;
156 int procrss = 0;
157 int procseconds = 0;
158 float procpcpu = 0;
159 char procstat[8];
160 char procetime[MAX_INPUT_BUFFER] = {'\0'};
161 int resultsum = 0; /* bitmask of the filter criteria met by a process */
162 int found = 0; /* counter for number of lines returned in `ps` output */
163 int procs = 0; /* counter for number of processes meeting filter criteria */
164 char *input_buffer = malloc(MAX_INPUT_BUFFER);
165 char *procprog = malloc(MAX_INPUT_BUFFER);
166 const int expected_cols = PS_COLS - 1;
167
210 /* flush first line: j starts at 1 */ 168 /* flush first line: j starts at 1 */
211 for (size_t j = 1; j < chld_out.lines; j++) { 169 for (size_t j = 1; j < chld_out.lines; j++) {
212 input_line = chld_out.line[j]; 170 char *input_line = chld_out.line[j];
213 171
214 if (verbose >= 3) 172 if (verbose >= 3) {
215 printf ("%s", input_line); 173 printf("%s", input_line);
174 }
216 175
217 strcpy (procprog, ""); 176 strcpy(procprog, "");
218 xasprintf (&procargs, "%s", ""); 177 char *procargs;
178 xasprintf(&procargs, "%s", "");
219 179
220 cols = sscanf (input_line, PS_FORMAT, PS_VARLIST); 180 /* number of columns in ps output */
181 int cols = sscanf(input_line, PS_FORMAT, PS_VARLIST);
221 182
222 /* Zombie processes do not give a procprog command */ 183 /* Zombie processes do not give a procprog command */
223 if ( cols < expected_cols && strstr(procstat, zombie) ) { 184 const char *zombie = "Z";
185 if (cols < expected_cols && strstr(procstat, zombie)) {
224 cols = expected_cols; 186 cols = expected_cols;
225 } 187 }
226 if ( cols >= expected_cols ) { 188 if (cols >= expected_cols) {
227 resultsum = 0; 189 resultsum = 0;
228 xasprintf (&procargs, "%s", input_line + pos); 190 xasprintf(&procargs, "%s", input_line + pos);
229 strip (procargs); 191 strip(procargs);
230 192
231 /* Some ps return full pathname for command. This removes path */ 193 /* Some ps return full pathname for command. This removes path */
232 strcpy(procprog, base_name(procprog)); 194 strcpy(procprog, base_name(procprog));
233 195
234 /* we need to convert the elapsed time to seconds */ 196 /* we need to convert the elapsed time to seconds */
235 procseconds = convert_to_seconds(procetime); 197 procseconds = convert_to_seconds(procetime, config.metric);
236 198
237 if (verbose >= 3) 199 if (verbose >= 3) {
238 printf ("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 200 printf("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s "
239 procs, procuid, procvsz, procrss, 201 "prog=%s args=%s\n",
240 procpid, procppid, procpcpu, procstat, 202 procs, procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat,
241 procetime, procprog, procargs); 203 procetime, procprog, procargs);
204 }
242 205
243 /* Ignore self */ 206 /* Ignore self */
244 if ((usepid && mypid == procpid) || 207 int ret = 0;
245 ( ((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || 208 if ((config.usepid && mypid == procpid) ||
246 (ret == -1 && errno == ENOENT)) 209 (((!config.usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) &&
247 ) { 210 statbuf.st_dev == mydev && statbuf.st_ino == myino)) ||
248 if (verbose >= 3) 211 (ret == -1 && errno == ENOENT))) {
249 printf("not considering - is myself or gone\n"); 212 if (verbose >= 3) {
213 printf("not considering - is myself or gone\n");
214 }
250 continue; 215 continue;
251 } 216 }
252 /* Ignore parent*/ 217 /* Ignore parent*/
253 else if (myppid == procpid) { 218 if (myppid == procpid) {
254 if (verbose >= 3) 219 if (verbose >= 3) {
255 printf("not considering - is parent\n"); 220 printf("not considering - is parent\n");
221 }
256 continue; 222 continue;
257 } 223 }
258 224
259 /* Ignore our own children */ 225 /* Ignore our own children */
260 if (procppid == mypid) { 226 if (procppid == mypid) {
261 if (verbose >= 3) 227 if (verbose >= 3) {
262 printf("not considering - is our child\n"); 228 printf("not considering - is our child\n");
229 }
263 continue; 230 continue;
264 } 231 }
265 232
266 /* Ignore excluded processes by name */ 233 /* Ignore excluded processes by name */
267 if(options & EXCLUDE_PROGS) { 234 if (config.options & EXCLUDE_PROGS) {
268 int found = 0; 235 bool found = false;
269 int i = 0; 236 for (int i = 0; i < (config.exclude_progs_counter); i++) {
270 237 if (!strcmp(procprog, config.exclude_progs_arr[i])) {
271 for(i=0; i < (exclude_progs_counter); i++) { 238 found = true;
272 if(!strcmp(procprog, exclude_progs_arr[i])) { 239 }
273 found = 1; 240 }
274 } 241 if (!found) {
275 } 242 resultsum |= EXCLUDE_PROGS;
276 if(found == 0) { 243 } else {
277 resultsum |= EXCLUDE_PROGS; 244 if (verbose >= 3) {
278 } else 245 printf("excluding - by ignorelist\n");
279 { 246 }
280 if(verbose >= 3) 247 }
281 printf("excluding - by ignorelist\n");
282 }
283 } 248 }
284 249
285 /* filter kernel threads (children of KTHREAD_PARENT)*/ 250 /* filter kernel threads (children of KTHREAD_PARENT)*/
286 /* TODO adapt for other OSes than GNU/Linux 251 /* TODO adapt for other OSes than GNU/Linux
287 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 :-( */
288 if (kthread_filter == 1) { 253 if (config.kthread_filter) {
289 /* get pid KTHREAD_PARENT */ 254 /* get pid KTHREAD_PARENT */
290 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT) ) 255 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) {
291 kthread_ppid = procpid; 256 kthread_ppid = procpid;
257 }
292 258
293 if (kthread_ppid == procppid) { 259 if (kthread_ppid == procppid) {
294 if (verbose >= 2) 260 if (verbose >= 2) {
295 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 }
296 continue; 264 continue;
297 } 265 }
298 } 266 }
299 267
300 if ((options & STAT) && (strstr (procstat, statopts))) 268 if ((config.options & STAT) && (strstr(procstat, config.statopts))) {
301 resultsum |= STAT; 269 resultsum |= STAT;
302 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL)) 270 }
271 if ((config.options & ARGS) && procargs && (strstr(procargs, config.args) != NULL)) {
303 resultsum |= ARGS; 272 resultsum |= ARGS;
304 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)) {
305 resultsum |= EREG_ARGS; 276 resultsum |= EREG_ARGS;
306 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0)) 277 }
278 if ((config.options & PROG) && procprog && (strcmp(config.prog, procprog) == 0)) {
307 resultsum |= PROG; 279 resultsum |= PROG;
308 if ((options & PPID) && (procppid == ppid)) 280 }
281 if ((config.options & PPID) && (procppid == config.ppid)) {
309 resultsum |= PPID; 282 resultsum |= PPID;
310 if ((options & USER) && (procuid == uid)) 283 }
284 if ((config.options & USER) && (procuid == config.uid)) {
311 resultsum |= USER; 285 resultsum |= USER;
312 if ((options & VSZ) && (procvsz >= vsz)) 286 }
287 if ((config.options & VSZ) && (procvsz >= config.vsz)) {
313 resultsum |= VSZ; 288 resultsum |= VSZ;
314 if ((options & RSS) && (procrss >= rss)) 289 }
290 if ((config.options & RSS) && (procrss >= config.rss)) {
315 resultsum |= RSS; 291 resultsum |= RSS;
316 if ((options & PCPU) && (procpcpu >= pcpu)) 292 }
293 if ((config.options & PCPU) && (procpcpu >= config.pcpu)) {
317 resultsum |= PCPU; 294 resultsum |= PCPU;
295 }
318 296
319 found++; 297 found++;
320 298
321 /* Next line if filters not matched */ 299 /* Next line if filters not matched */
322 if (!(options == resultsum || options == ALL)) 300 if (!(config.options == resultsum || config.options == ALL)) {
323 continue; 301 continue;
302 }
324 303
325 procs++; 304 procs++;
326 if (verbose >= 2) { 305 if (verbose >= 2) {
327 printf ("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 306 printf("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s "
328 procuid, procvsz, procrss, 307 "prog=%s args=%s\n",
329 procpid, procppid, procpcpu, procstat, 308 procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat, procetime,
330 procetime, procprog, procargs); 309 procprog, procargs);
331 } 310 }
332 311
333 if (metric == METRIC_VSZ) 312 mp_state_enum temporary_result = STATE_OK;
334 i = get_status ((double)procvsz, procs_thresholds); 313 if (config.metric == METRIC_VSZ) {
335 else if (metric == METRIC_RSS) 314 temporary_result = get_status((double)procvsz, config.procs_thresholds);
336 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 }
337 /* TODO? float thresholds for --metric=CPU */ 318 /* TODO? float thresholds for --metric=CPU */
338 else if (metric == METRIC_CPU) 319 else if (config.metric == METRIC_CPU) {
339 i = get_status (procpcpu, procs_thresholds); 320 temporary_result = get_status(procpcpu, config.procs_thresholds);
340 else if (metric == METRIC_ELAPSED) 321 } else if (config.metric == METRIC_ELAPSED) {
341 i = get_status ((double)procseconds, procs_thresholds); 322 temporary_result = get_status((double)procseconds, config.procs_thresholds);
323 }
342 324
343 if (metric != METRIC_PROCS) { 325 if (config.metric != METRIC_PROCS) {
344 if (i == STATE_WARNING) { 326 if (temporary_result == STATE_WARNING) {
345 warn++; 327 warn++;
346 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 328 xasprintf(&config.fails, "%s%s%s", config.fails,
347 result = max_state (result, i); 329 (strcmp(config.fails, "") ? ", " : ""), procprog);
330 result = max_state(result, temporary_result);
348 } 331 }
349 if (i == STATE_CRITICAL) { 332 if (temporary_result == STATE_CRITICAL) {
350 crit++; 333 crit++;
351 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 334 xasprintf(&config.fails, "%s%s%s", config.fails,
352 result = max_state (result, i); 335 (strcmp(config.fails, "") ? ", " : ""), procprog);
336 result = max_state(result, temporary_result);
353 } 337 }
354 } 338 }
355 } 339 }
@@ -359,339 +343,366 @@ main (int argc, char **argv)
359 } 343 }
360 } 344 }
361 345
362 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ 346 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
363 printf (_("Unable to read output\n")); 347 printf(_("Unable to read output\n"));
364 return STATE_UNKNOWN; 348 return STATE_UNKNOWN;
365 } 349 }
366 350
367 if ( result == STATE_UNKNOWN ) 351 if (result == STATE_UNKNOWN) {
368 result = STATE_OK; 352 result = STATE_OK;
353 }
369 354
370 /* Needed if procs found, but none match filter */ 355 /* Needed if procs found, but none match filter */
371 if ( metric == METRIC_PROCS ) { 356 if (config.metric == METRIC_PROCS) {
372 result = max_state (result, get_status ((double)procs, procs_thresholds) ); 357 result = max_state(result, get_status((double)procs, config.procs_thresholds));
373 } 358 }
374 359
375 if ( result == STATE_OK ) { 360 if (result == STATE_OK) {
376 printf ("%s %s: ", metric_name, _("OK")); 361 printf("%s %s: ", config.metric_name, _("OK"));
377 } else if (result == STATE_WARNING) { 362 } else if (result == STATE_WARNING) {
378 printf ("%s %s: ", metric_name, _("WARNING")); 363 printf("%s %s: ", config.metric_name, _("WARNING"));
379 if ( metric != METRIC_PROCS ) { 364 if (config.metric != METRIC_PROCS) {
380 printf (_("%d warn out of "), warn); 365 printf(_("%d warn out of "), warn);
381 } 366 }
382 } else if (result == STATE_CRITICAL) { 367 } else if (result == STATE_CRITICAL) {
383 printf ("%s %s: ", metric_name, _("CRITICAL")); 368 printf("%s %s: ", config.metric_name, _("CRITICAL"));
384 if (metric != METRIC_PROCS) { 369 if (config.metric != METRIC_PROCS) {
385 printf (_("%d crit, %d warn out of "), crit, warn); 370 printf(_("%d crit, %d warn out of "), crit, warn);
386 } 371 }
387 } 372 }
388 printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs); 373 printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs);
389 374
390 if (strcmp(fmt,"") != 0) { 375 if (strcmp(config.fmt, "") != 0) {
391 printf (_(" with %s"), fmt); 376 printf(_(" with %s"), config.fmt);
392 } 377 }
393 378
394 if ( verbose >= 1 && strcmp(fails,"") ) 379 if (verbose >= 1 && strcmp(config.fails, "")) {
395 printf (" [%s]", fails); 380 printf(" [%s]", config.fails);
381 }
396 382
397 if (metric == METRIC_PROCS) 383 if (config.metric == METRIC_PROCS) {
398 printf (" | procs=%d;%s;%s;0;", procs, 384 printf(" | procs=%d;%s;%s;0;", procs, config.warning_range ? config.warning_range : "",
399 warning_range ? warning_range : "", 385 config.critical_range ? config.critical_range : "");
400 critical_range ? critical_range : ""); 386 } else {
401 else 387 printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit);
402 printf (" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); 388 }
403 389
404 printf ("\n"); 390 printf("\n");
405 return result; 391 exit(result);
406} 392}
407 393
408
409
410/* process command-line arguments */ 394/* process command-line arguments */
411int 395check_procs_config_wrapper process_arguments(int argc, char **argv) {
412process_arguments (int argc, char **argv) 396 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
413{ 397 {"critical", required_argument, 0, 'c'},
414 int c = 1; 398 {"metric", required_argument, 0, 'm'},
415 char *user; 399 {"timeout", required_argument, 0, 't'},
416 struct passwd *pw; 400 {"status", required_argument, 0, 's'},
417 int option = 0; 401 {"ppid", required_argument, 0, 'p'},
418 int err; 402 {"user", required_argument, 0, 'u'},
419 int cflags = REG_NOSUB | REG_EXTENDED; 403 {"command", required_argument, 0, 'C'},
420 char errbuf[MAX_INPUT_BUFFER]; 404 {"vsz", required_argument, 0, 'z'},
421 char *temp_string; 405 {"rss", required_argument, 0, 'r'},
422 int i=0; 406 {"pcpu", required_argument, 0, 'P'},
423 static struct option longopts[] = { 407 {"elapsed", required_argument, 0, 'e'},
424 {"warning", required_argument, 0, 'w'}, 408 {"argument-array", required_argument, 0, 'a'},
425 {"critical", required_argument, 0, 'c'}, 409 {"help", no_argument, 0, 'h'},
426 {"metric", required_argument, 0, 'm'}, 410 {"version", no_argument, 0, 'V'},
427 {"timeout", required_argument, 0, 't'}, 411 {"verbose", no_argument, 0, 'v'},
428 {"status", required_argument, 0, 's'}, 412 {"ereg-argument-array", required_argument, 0, CHAR_MAX + 1},
429 {"ppid", required_argument, 0, 'p'}, 413 {"input-file", required_argument, 0, CHAR_MAX + 2},
430 {"user", required_argument, 0, 'u'}, 414 {"no-kthreads", required_argument, 0, 'k'},
431 {"command", required_argument, 0, 'C'}, 415 {"traditional-filter", no_argument, 0, 'T'},
432 {"vsz", required_argument, 0, 'z'}, 416 {"exclude-process", required_argument, 0, 'X'},
433 {"rss", required_argument, 0, 'r'}, 417 {0, 0, 0, 0}};
434 {"pcpu", required_argument, 0, 'P'}, 418
435 {"elapsed", required_argument, 0, 'e'}, 419 for (int index = 1; index < argc; index++) {
436 {"argument-array", required_argument, 0, 'a'}, 420 if (strcmp("-to", argv[index]) == 0) {
437 {"help", no_argument, 0, 'h'}, 421 strcpy(argv[index], "-t");
438 {"version", no_argument, 0, 'V'}, 422 }
439 {"verbose", no_argument, 0, 'v'}, 423 }
440 {"ereg-argument-array", required_argument, 0, CHAR_MAX+1},
441 {"input-file", required_argument, 0, CHAR_MAX+2},
442 {"no-kthreads", required_argument, 0, 'k'},
443 {"traditional-filter", no_argument, 0, 'T'},
444 {"exclude-process", required_argument, 0, 'X'},
445 {0, 0, 0, 0}
446 };
447 424
448 for (c = 1; c < argc; c++) 425 check_procs_config_wrapper result = {
449 if (strcmp ("-to", argv[c]) == 0) 426 .errorcode = OK,
450 strcpy (argv[c], "-t"); 427 .config = check_procs_config_init(),
428 };
451 429
452 while (1) { 430 while (true) {
453 c = getopt_long (argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", 431 int option = 0;
454 longopts, &option); 432 int option_index =
433 getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option);
455 434
456 if (c == -1 || c == EOF) 435 if (option_index == -1 || option_index == EOF) {
457 break; 436 break;
437 }
458 438
459 switch (c) { 439 switch (option_index) {
460 case '?': /* help */ 440 case '?': /* help */
461 usage5 (); 441 usage5();
462 case 'h': /* help */ 442 case 'h': /* help */
463 print_help (); 443 print_help();
464 exit (STATE_UNKNOWN); 444 exit(STATE_UNKNOWN);
465 case 'V': /* version */ 445 case 'V': /* version */
466 print_revision (progname, NP_VERSION); 446 print_revision(progname, NP_VERSION);
467 exit (STATE_UNKNOWN); 447 exit(STATE_UNKNOWN);
468 case 't': /* timeout period */ 448 case 't': /* timeout period */
469 if (!is_integer (optarg)) 449 if (!is_integer(optarg)) {
470 usage2 (_("Timeout interval must be a positive integer"), optarg); 450 usage2(_("Timeout interval must be a positive integer"), optarg);
471 else 451 } else {
472 timeout_interval = atoi (optarg); 452 timeout_interval = atoi(optarg);
453 }
473 break; 454 break;
474 case 'c': /* critical threshold */ 455 case 'c': /* critical threshold */
475 critical_range = optarg; 456 result.config.critical_range = optarg;
476 break; 457 break;
477 case 'w': /* warning threshold */ 458 case 'w': /* warning threshold */
478 warning_range = optarg; 459 result.config.warning_range = optarg;
479 break; 460 break;
480 case 'p': /* process id */ 461 case 'p': { /* process id */
481 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) { 462 static char tmp[MAX_INPUT_BUFFER];
482 xasprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid); 463 if (sscanf(optarg, "%d%[^0-9]", &result.config.ppid, tmp) == 1) {
483 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;
484 break; 468 break;
485 } 469 }
486 usage4 (_("Parent Process ID must be an integer!")); 470 usage4(_("Parent Process ID must be an integer!"));
487 case 's': /* status */ 471 }
488 if (statopts) 472 case 's': /* status */
473 if (result.config.statopts) {
489 break; 474 break;
490 else 475 } else {
491 statopts = optarg; 476 result.config.statopts = optarg;
492 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 477 }
493 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;
494 break; 482 break;
495 case 'u': /* user or user id */ 483 case 'u': /* user or user id */ {
496 if (is_integer (optarg)) { 484 struct passwd *pw;
497 uid = atoi (optarg); 485 if (is_integer(optarg)) {
498 pw = getpwuid ((uid_t) uid); 486 result.config.uid = atoi(optarg);
487 pw = getpwuid(result.config.uid);
499 /* check to be sure user exists */ 488 /* check to be sure user exists */
500 if (pw == NULL) 489 if (pw == NULL) {
501 usage2 (_("UID was not found"), optarg); 490 usage2(_("UID was not found"), optarg);
502 } 491 }
503 else { 492 } else {
504 pw = getpwnam (optarg); 493 pw = getpwnam(optarg);
505 /* check to be sure user exists */ 494 /* check to be sure user exists */
506 if (pw == NULL) 495 if (pw == NULL) {
507 usage2 (_("User name was not found"), optarg); 496 usage2(_("User name was not found"), optarg);
497 }
508 /* then get uid */ 498 /* then get uid */
509 uid = pw->pw_uid; 499 result.config.uid = pw->pw_uid;
510 } 500 }
511 user = pw->pw_name; 501
512 xasprintf (&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), 502 char *user = pw->pw_name;
513 uid, user); 503 xasprintf(&result.config.fmt, "%s%sUID = %d (%s)",
514 options |= USER; 504 (result.config.fmt ? result.config.fmt : ""),
515 break; 505 (result.config.options ? ", " : ""), result.config.uid, user);
516 case 'C': /* command */ 506 result.config.options |= USER;
507 } break;
508 case 'C': /* command */
517 /* TODO: allow this to be passed in with --metric */ 509 /* TODO: allow this to be passed in with --metric */
518 if (prog) 510 if (result.config.prog) {
519 break; 511 break;
520 else 512 } else {
521 prog = optarg; 513 result.config.prog = optarg;
522 xasprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 514 }
523 prog); 515 xasprintf(&result.config.fmt, _("%s%scommand name '%s'"),
524 options |= PROG; 516 (result.config.fmt ? result.config.fmt : ""),
517 (result.config.options ? ", " : ""), result.config.prog);
518 result.config.options |= PROG;
525 break; 519 break;
526 case 'X': 520 case 'X':
527 if(exclude_progs) 521 if (result.config.exclude_progs) {
528 break; 522 break;
529 else 523 } else {
530 exclude_progs = optarg; 524 result.config.exclude_progs = optarg;
531 xasprintf (&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 525 }
532 exclude_progs); 526 xasprintf(&result.config.fmt, _("%s%sexclude progs '%s'"),
533 char *p = strtok(exclude_progs, ","); 527 (result.config.fmt ? result.config.fmt : ""),
534 528 (result.config.options ? ", " : ""), result.config.exclude_progs);
535 while(p){ 529 char *tmp_pointer = strtok(result.config.exclude_progs, ",");
536 exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char*) * ++exclude_progs_counter); 530
537 exclude_progs_arr[exclude_progs_counter-1] = p; 531 while (tmp_pointer) {
538 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, ",");
539 } 538 }
540 539
541 options |= EXCLUDE_PROGS; 540 result.config.options |= EXCLUDE_PROGS;
542 break; 541 break;
543 case 'a': /* args (full path name with args) */ 542 case 'a': /* args (full path name with args) */
544 /* TODO: allow this to be passed in with --metric */ 543 /* TODO: allow this to be passed in with --metric */
545 if (args) 544 if (result.config.args) {
546 break; 545 break;
547 else 546 } else {
548 args = optarg; 547 result.config.args = optarg;
549 xasprintf (&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); 548 }
550 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;
551 break; 553 break;
552 case CHAR_MAX+1: 554 case CHAR_MAX + 1: {
553 err = regcomp(&re_args, optarg, cflags); 555 int cflags = REG_NOSUB | REG_EXTENDED;
556 int err = regcomp(&result.config.re_args, optarg, cflags);
554 if (err != 0) { 557 if (err != 0) {
555 regerror (err, &re_args, errbuf, MAX_INPUT_BUFFER); 558 char errbuf[MAX_INPUT_BUFFER];
556 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);
557 } 562 }
558 /* Strip off any | within the regex optarg */ 563 /* Strip off any | within the regex optarg */
559 temp_string = strdup(optarg); 564 char *temp_string = strdup(optarg);
560 while(temp_string[i]!='\0'){ 565 int index = 0;
561 if(temp_string[i]=='|') 566 while (temp_string[index] != '\0') {
562 temp_string[i]=','; 567 if (temp_string[index] == '|') {
563 i++; 568 temp_string[index] = ',';
564 } 569 }
565 xasprintf (&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); 570 index++;
566 options |= EREG_ARGS; 571 }
567 break; 572 xasprintf(&result.config.fmt, "%s%sregex args '%s'",
568 case 'r': /* RSS */ 573 (result.config.fmt ? result.config.fmt : ""),
569 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) { 574 (result.config.options ? ", " : ""), temp_string);
570 xasprintf (&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); 575 result.config.options |= EREG_ARGS;
571 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;
572 break; 584 break;
573 } 585 }
574 usage4 (_("RSS must be an integer!")); 586 usage4(_("RSS must be an integer!"));
575 case 'z': /* VSZ */ 587 }
576 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) { 588 case 'z': { /* VSZ */
577 xasprintf (&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); 589 static char tmp[MAX_INPUT_BUFFER];
578 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;
579 break; 595 break;
580 } 596 }
581 usage4 (_("VSZ must be an integer!")); 597 usage4(_("VSZ must be an integer!"));
582 case 'P': /* PCPU */ 598 }
599 case 'P': { /* PCPU */
583 /* TODO: -P 1.5.5 is accepted */ 600 /* TODO: -P 1.5.5 is accepted */
584 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { 601 static char tmp[MAX_INPUT_BUFFER];
585 xasprintf (&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); 602 if (sscanf(optarg, "%f%[^0-9.]", &result.config.pcpu, tmp) == 1) {
586 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;
587 break; 607 break;
588 } 608 }
589 usage4 (_("PCPU must be a float!")); 609 usage4(_("PCPU must be a float!"));
610 }
590 case 'm': 611 case 'm':
591 xasprintf (&metric_name, "%s", optarg); 612 xasprintf(&result.config.metric_name, "%s", optarg);
592 if ( strcmp(optarg, "PROCS") == 0) { 613 if (strcmp(optarg, "PROCS") == 0) {
593 metric = METRIC_PROCS; 614 result.config.metric = METRIC_PROCS;
594 break; 615 break;
595 } 616 }
596 else if ( strcmp(optarg, "VSZ") == 0) { 617 if (strcmp(optarg, "VSZ") == 0) {
597 metric = METRIC_VSZ; 618 result.config.metric = METRIC_VSZ;
598 break; 619 break;
599 } 620 }
600 else if ( strcmp(optarg, "RSS") == 0 ) { 621 if (strcmp(optarg, "RSS") == 0) {
601 metric = METRIC_RSS; 622 result.config.metric = METRIC_RSS;
602 break; 623 break;
603 } 624 }
604 else if ( strcmp(optarg, "CPU") == 0 ) { 625 if (strcmp(optarg, "CPU") == 0) {
605 metric = METRIC_CPU; 626 result.config.metric = METRIC_CPU;
606 break; 627 break;
607 } 628 }
608 else if ( strcmp(optarg, "ELAPSED") == 0) { 629 if (strcmp(optarg, "ELAPSED") == 0) {
609 metric = METRIC_ELAPSED; 630 result.config.metric = METRIC_ELAPSED;
610 break; 631 break;
611 } 632 }
612 633
613 usage4 (_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); 634 usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!"));
614 case 'k': /* linux kernel thread filter */ 635 case 'k': /* linux kernel thread filter */
615 kthread_filter = 1; 636 result.config.kthread_filter = true;
616 break; 637 break;
617 case 'v': /* command */ 638 case 'v': /* command */
618 verbose++; 639 verbose++;
619 break; 640 break;
620 case 'T': 641 case 'T':
621 usepid = 1; 642 result.config.usepid = true;
622 break; 643 break;
623 case CHAR_MAX+2: 644 case CHAR_MAX + 2:
624 input_filename = optarg; 645 result.config.input_filename = optarg;
625 break; 646 break;
626 } 647 }
627 } 648 }
628 649
629 c = optind; 650 int index = optind;
630 if ((! warning_range) && argv[c]) 651 if ((!result.config.warning_range) && argv[index]) {
631 warning_range = argv[c++]; 652 result.config.warning_range = argv[index++];
632 if ((! critical_range) && argv[c]) 653 }
633 critical_range = argv[c++]; 654 if ((!result.config.critical_range) && argv[index]) {
634 if (statopts == NULL && argv[c]) { 655 result.config.critical_range = argv[index++];
635 xasprintf (&statopts, "%s", argv[c++]); 656 }
636 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 657 if (result.config.statopts == NULL && argv[index]) {
637 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;
638 } 663 }
639 664
640 /* this will abort in case of invalid ranges */ 665 /* this will abort in case of invalid ranges */
641 set_thresholds (&procs_thresholds, warning_range, critical_range); 666 set_thresholds(&result.config.procs_thresholds, result.config.warning_range,
667 result.config.critical_range);
642 668
643 return validate_arguments (); 669 return validate_arguments(result);
644} 670}
645 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 }
646 676
677 if (config_wrapper.config.statopts == NULL) {
678 config_wrapper.config.statopts = strdup("");
679 }
647 680
648int 681 if (config_wrapper.config.prog == NULL) {
649validate_arguments () 682 config_wrapper.config.prog = strdup("");
650{ 683 }
651 if (options == 0)
652 options = ALL;
653
654 if (statopts==NULL)
655 statopts = strdup("");
656
657 if (prog==NULL)
658 prog = strdup("");
659 684
660 if (args==NULL) 685 if (config_wrapper.config.args == NULL) {
661 args = strdup(""); 686 config_wrapper.config.args = strdup("");
687 }
662 688
663 if (fmt==NULL) 689 if (config_wrapper.config.fmt == NULL) {
664 fmt = strdup(""); 690 config_wrapper.config.fmt = strdup("");
691 }
665 692
666 if (fails==NULL) 693 if (config_wrapper.config.fails == NULL) {
667 fails = strdup(""); 694 config_wrapper.config.fails = strdup("");
695 }
668 696
669 return options; 697 // return options;
698 return config_wrapper;
670} 699}
671 700
672
673/* convert the elapsed time to seconds */ 701/* convert the elapsed time to seconds */
674int 702int convert_to_seconds(char *etime, enum metric metric) {
675convert_to_seconds(char *etime) { 703 int hyphcnt = 0;
676 704 int coloncnt = 0;
677 char *ptr; 705 for (char *ptr = etime; *ptr != '\0'; ptr++) {
678 int total;
679
680 int hyphcnt;
681 int coloncnt;
682 int days;
683 int hours;
684 int minutes;
685 int seconds;
686
687 hyphcnt = 0;
688 coloncnt = 0;
689 days = 0;
690 hours = 0;
691 minutes = 0;
692 seconds = 0;
693
694 for (ptr = etime; *ptr != '\0'; ptr++) {
695 706
696 if (*ptr == '-') { 707 if (*ptr == '-') {
697 hyphcnt++; 708 hyphcnt++;
@@ -703,9 +714,12 @@ convert_to_seconds(char *etime) {
703 } 714 }
704 } 715 }
705 716
717 int days = 0;
718 int hours = 0;
719 int minutes = 0;
720 int seconds = 0;
706 if (hyphcnt > 0) { 721 if (hyphcnt > 0) {
707 sscanf(etime, "%d-%d:%d:%d", 722 sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds);
708 &days, &hours, &minutes, &seconds);
709 /* linux 2.6.5/2.6.6 reporting some processes with infinite 723 /* linux 2.6.5/2.6.6 reporting some processes with infinite
710 * elapsed times for some reason */ 724 * elapsed times for some reason */
711 if (days == 49710) { 725 if (days == 49710) {
@@ -713,135 +727,129 @@ convert_to_seconds(char *etime) {
713 } 727 }
714 } else { 728 } else {
715 if (coloncnt == 2) { 729 if (coloncnt == 2) {
716 sscanf(etime, "%d:%d:%d", 730 sscanf(etime, "%d:%d:%d", &hours, &minutes, &seconds);
717 &hours, &minutes, &seconds);
718 } else if (coloncnt == 1) { 731 } else if (coloncnt == 1) {
719 sscanf(etime, "%d:%d", 732 sscanf(etime, "%d:%d", &minutes, &seconds);
720 &minutes, &seconds);
721 } 733 }
722 } 734 }
723 735
724 total = (days * 86400) + 736 int total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds;
725 (hours * 3600) +
726 (minutes * 60) +
727 seconds;
728 737
729 if (verbose >= 3 && metric == METRIC_ELAPSED) { 738 if (verbose >= 3 && metric == METRIC_ELAPSED) {
730 printf("seconds: %d\n", total); 739 printf("seconds: %d\n", total);
731 } 740 }
732 return total; 741 return total;
733} 742}
734 743
735 744void print_help(void) {
736void 745 print_revision(progname, NP_VERSION);
737print_help (void) 746
738{ 747 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
739 print_revision (progname, NP_VERSION); 748 printf(COPYRIGHT, copyright, email);
740 749
741 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 750 printf("%s\n",
742 printf (COPYRIGHT, copyright, email); 751 _("Checks all processes and generates WARNING or CRITICAL states if the specified"));
743 752 printf("%s\n",
744 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"));
745 printf ("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); 754 printf("%s\n",
746 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."));
747 756
748 printf ("\n\n"); 757 printf("\n\n");
749 758
750 printf ("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); 759 printf("%s\n",
751 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)"));
752 761 printf("%s\n", _("are excluded from any checks to prevent false positives."));
753 printf ("\n\n"); 762
754 763 printf("\n\n");
755 print_usage (); 764
756 765 print_usage();
757 printf (UT_HELP_VRSN); 766
758 printf (UT_EXTRA_OPTS); 767 printf(UT_HELP_VRSN);
759 printf (" %s\n", "-w, --warning=RANGE"); 768 printf(UT_EXTRA_OPTS);
760 printf (" %s\n", _("Generate warning state if metric is outside this range")); 769 printf(" %s\n", "-w, --warning=RANGE");
761 printf (" %s\n", "-c, --critical=RANGE"); 770 printf(" %s\n", _("Generate warning state if metric is outside this range"));
762 printf (" %s\n", _("Generate critical state if metric is outside this range")); 771 printf(" %s\n", "-c, --critical=RANGE");
763 printf (" %s\n", "-m, --metric=TYPE"); 772 printf(" %s\n", _("Generate critical state if metric is outside this range"));
764 printf (" %s\n", _("Check thresholds against metric. Valid types:")); 773 printf(" %s\n", "-m, --metric=TYPE");
765 printf (" %s\n", _("PROCS - number of processes (default)")); 774 printf(" %s\n", _("Check thresholds against metric. Valid types:"));
766 printf (" %s\n", _("VSZ - virtual memory size")); 775 printf(" %s\n", _("PROCS - number of processes (default)"));
767 printf (" %s\n", _("RSS - resident set memory size")); 776 printf(" %s\n", _("VSZ - virtual memory size"));
768 printf (" %s\n", _("CPU - percentage CPU")); 777 printf(" %s\n", _("RSS - resident set memory size"));
778 printf(" %s\n", _("CPU - percentage CPU"));
769/* only linux etime is support currently */ 779/* only linux etime is support currently */
770#if defined( __linux__ ) 780#if defined(__linux__)
771 printf (" %s\n", _("ELAPSED - time elapsed in seconds")); 781 printf(" %s\n", _("ELAPSED - time elapsed in seconds"));
772#endif /* defined(__linux__) */ 782#endif /* defined(__linux__) */
773 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 783 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
774 784
775 printf (" %s\n", "-v, --verbose"); 785 printf(" %s\n", "-v, --verbose");
776 printf (" %s\n", _("Extra information. Up to 3 verbosity levels")); 786 printf(" %s\n", _("Extra information. Up to 3 verbosity levels"));
777 787
778 printf (" %s\n", "-T, --traditional"); 788 printf(" %s\n", "-T, --traditional");
779 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"));
780 790
781 printf ("\n"); 791 printf("\n");
782 printf ("%s\n", "Filters:"); 792 printf("%s\n", "Filters:");
783 printf (" %s\n", "-s, --state=STATUSFLAGS"); 793 printf(" %s\n", "-s, --state=STATUSFLAGS");
784 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"));
785 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,"));
786 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)."));
787 printf (" %s\n", "-p, --ppid=PPID"); 797 printf(" %s\n", "-p, --ppid=PPID");
788 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."));
789 printf (" %s\n", "-z, --vsz=VSZ"); 799 printf(" %s\n", "-z, --vsz=VSZ");
790 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."));
791 printf (" %s\n", "-r, --rss=RSS"); 801 printf(" %s\n", "-r, --rss=RSS");
792 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."));
793 printf (" %s\n", "-P, --pcpu=PCPU"); 803 printf(" %s\n", "-P, --pcpu=PCPU");
794 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."));
795 printf (" %s\n", "-u, --user=USER"); 805 printf(" %s\n", "-u, --user=USER");
796 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."));
797 printf (" %s\n", "-a, --argument-array=STRING"); 807 printf(" %s\n", "-a, --argument-array=STRING");
798 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."));
799 printf (" %s\n", "--ereg-argument-array=STRING"); 809 printf(" %s\n", "--ereg-argument-array=STRING");
800 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."));
801 printf (" %s\n", "-C, --command=COMMAND"); 811 printf(" %s\n", "-C, --command=COMMAND");
802 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)."));
803 printf (" %s\n", "-X, --exclude-process"); 813 printf(" %s\n", "-X, --exclude-process");
804 printf (" %s\n", _("Exclude processes which match this comma separated list")); 814 printf(" %s\n", _("Exclude processes which match this comma separated list"));
805 printf (" %s\n", "-k, --no-kthreads"); 815 printf(" %s\n", "-k, --no-kthreads");
806 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)."));
807 817
808 printf(_("\n\ 818 printf(_("\n\
809RANGEs 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\
810specified 'max:min', a warning status will be generated if the\n\ 820specified 'max:min', a warning status will be generated if the\n\
811count is inside the specified range\n\n")); 821count is inside the specified range\n\n"));
812 822
813 printf(_("\ 823 printf(_("\
814This plugin checks the number of currently running processes and\n\ 824This plugin checks the number of currently running processes and\n\
815generates WARNING or CRITICAL states if the process count is outside\n\ 825generates WARNING or CRITICAL states if the process count is outside\n\
816the specified threshold ranges. The process count can be filtered by\n\ 826the specified threshold ranges. The process count can be filtered by\n\
817process 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\
818be the total number of running processes\n\n")); 828be the total number of running processes\n\n"));
819 829
820 printf ("%s\n", _("Examples:")); 830 printf("%s\n", _("Examples:"));
821 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");
822 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."));
823 printf (" %s\n\n", _("Critical if < 2 or > 1024 processes")); 833 printf(" %s\n\n", _("Critical if < 2 or > 1024 processes"));
824 printf (" %s\n", "check_procs -c 1: -C sshd"); 834 printf(" %s\n", "check_procs -c 1: -C sshd");
825 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"));
826 printf (" %s\n", "check_procs -w 1024 -c 1: -C sshd"); 836 printf(" %s\n", "check_procs -w 1024 -c 1: -C sshd");
827 printf (" %s\n", _("Warning if > 1024 processes with command name sshd.")); 837 printf(" %s\n", _("Warning if > 1024 processes with command name sshd."));
828 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."));
829 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");
830 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"));
831 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"));
832 printf (" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); 842 printf(" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ");
833 printf (" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); 843 printf(" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K"));
834 printf (" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); 844 printf(" %s\n", "check_procs -w 10 -c 20 --metric=CPU");
835 printf (" %s\n", _("Alert if CPU of any processes over 10%% or 20%%")); 845 printf(" %s\n", _("Alert if CPU of any processes over 10%% or 20%%"));
836 846
837 printf (UT_SUPPORT); 847 printf(UT_SUPPORT);
838} 848}
839 849
840void 850void print_usage(void) {
841print_usage (void) 851 printf("%s\n", _("Usage:"));
842{ 852 printf("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname);
843 printf ("%s\n", _("Usage:")); 853 printf(" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
844 printf ("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname); 854 printf(" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
845 printf (" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
846 printf (" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
847} 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 d9ff8fa7..d26f7cf3 100644
--- a/plugins/check_radius.c
+++ b/plugins/check_radius.c
@@ -1,32 +1,32 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_radius plugin 3 * Monitoring check_radius plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2024 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_radius plugin 10 * This file contains the check_radius plugin
11* 11 *
12* Tests to see if a radius server is accepting connections. 12 * Tests to see if a radius server is accepting connections.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_radius"; 31const char *progname = "check_radius";
32const char *copyright = "2000-2024"; 32const char *copyright = "2000-2024";
@@ -35,64 +35,58 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 35#include "common.h"
36#include "utils.h" 36#include "utils.h"
37#include "netutils.h" 37#include "netutils.h"
38#include "states.h"
39#include "check_radius.d/config.h"
38 40
39#if defined(HAVE_LIBRADCLI) 41#if defined(HAVE_LIBRADCLI)
40#include <radcli/radcli.h> 42# include <radcli/radcli.h>
41#elif defined(HAVE_LIBFREERADIUS_CLIENT) 43#elif defined(HAVE_LIBFREERADIUS_CLIENT)
42#include <freeradius-client.h> 44# include <freeradius-client.h>
43#elif defined(HAVE_LIBRADIUSCLIENT_NG) 45#elif defined(HAVE_LIBRADIUSCLIENT_NG)
44#include <radiusclient-ng.h> 46# include <radiusclient-ng.h>
45#else 47#else
46#include <radiusclient.h> 48# include <radiusclient.h>
47#endif 49#endif
48 50
49static int process_arguments (int /*argc*/, char ** /*argv*/); 51typedef struct {
50static void print_help (void); 52 int errorcode;
51void print_usage (void); 53 check_radius_config config;
52 54} check_radius_config_wrapper;
53#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI) 55static check_radius_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
54#define my_rc_conf_str(a) rc_conf_str(rch,a) 56static void print_help(void);
55#if defined(HAVE_LIBRADCLI) 57void print_usage(void);
56#define my_rc_send_server(a,b) rc_send_server(rch,a,b,AUTH) 58
57#else 59#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
58#define my_rc_send_server(a,b) rc_send_server(rch,a,b) 60 defined(HAVE_LIBRADCLI)
59#endif 61# define my_rc_conf_str(a) rc_conf_str(rch, a)
60#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI) 62# 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) 63# define my_rc_send_server(a, b) rc_send_server(rch, a, b, AUTH)
64# else
65# define my_rc_send_server(a, b) rc_send_server(rch, a, b)
66# endif
67# if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI)
68# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, (a)->secret, e, f)
69# else
70# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, e, f)
71# endif
72# define my_rc_avpair_add(a, b, c, d) rc_avpair_add(rch, a, b, c, -1, d)
73# define my_rc_read_dictionary(a) rc_read_dictionary(rch, a)
62#else 74#else
63#define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(rch,a,b,c,d,e,f) 75# define my_rc_conf_str(a) rc_conf_str(a)
64#endif 76# 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) 77# 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) 78# define my_rc_avpair_add(a, b, c, d) rc_avpair_add(a, b, c, d)
67#else 79# 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 80#endif
74 81
75/* REJECT_RC is only defined in some version of radiusclient. It has 82/* 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 */ 83 * been reported from radiusclient-ng 0.5.6 on FreeBSD 7.2-RELEASE */
77#ifndef REJECT_RC 84#ifndef REJECT_RC
78#define REJECT_RC BADRESP_RC 85# define REJECT_RC BADRESP_RC
79#endif 86#endif
80 87
81static int my_rc_read_config(char * /*a*/); 88static int my_rc_read_config(char * /*a*/, rc_handle ** /*rch*/);
82
83#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
84static rc_handle *rch = NULL;
85#endif
86 89
87static char *server = NULL;
88static char *username = NULL;
89static char *password = NULL;
90static char *nasid = NULL;
91static char *nasipaddress = NULL;
92static char *expect = NULL;
93static char *config_file = NULL;
94static unsigned short port = PW_AUTH_UDP_PORT;
95static int retries = 1;
96static bool verbose = false; 90static bool verbose = false;
97 91
98/****************************************************************************** 92/******************************************************************************
@@ -148,149 +142,171 @@ Please note that all tags must be lowercase to use the DocBook XML DTD.
148-@@ 142-@@
149******************************************************************************/ 143******************************************************************************/
150 144
145int main(int argc, char **argv) {
146 setlocale(LC_ALL, "");
147 bindtextdomain(PACKAGE, LOCALEDIR);
148 textdomain(PACKAGE);
151 149
150 /* Parse extra opts if any */
151 argv = np_extra_opts(&argc, argv, progname);
152
153 check_radius_config_wrapper tmp_config = process_arguments(argc, argv);
154
155 if (tmp_config.errorcode == ERROR) {
156 usage4(_("Could not parse arguments"));
157 }
158
159 check_radius_config config = tmp_config.config;
160
161#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
162 defined(HAVE_LIBRADCLI)
163 rc_handle *rch = NULL;
164#endif
165
166 char *str = strdup("dictionary");
167 if ((config.config_file && my_rc_read_config(config.config_file, &rch)) ||
168 my_rc_read_dictionary(my_rc_conf_str(str))) {
169 die(STATE_UNKNOWN, _("Config file error\n"));
170 }
171
172 uint32_t service = PW_AUTHENTICATE_ONLY;
173
174 SEND_DATA data;
175 memset(&data, 0, sizeof(data));
176 if (!(my_rc_avpair_add(&data.send_pairs, PW_SERVICE_TYPE, &service, 0) &&
177 my_rc_avpair_add(&data.send_pairs, PW_USER_NAME, config.username, 0) &&
178 my_rc_avpair_add(&data.send_pairs, PW_USER_PASSWORD, config.password, 0))) {
179 die(STATE_UNKNOWN, _("Out of Memory?\n"));
180 }
181
182 if (config.nas_id != NULL) {
183 if (!(my_rc_avpair_add(&data.send_pairs, PW_NAS_IDENTIFIER, config.nas_id, 0))) {
184 die(STATE_UNKNOWN, _("Invalid NAS-Identifier\n"));
185 }
186 }
152 187
153int
154main (int argc, char **argv)
155{
156 struct sockaddr_storage ss;
157 char name[HOST_NAME_MAX]; 188 char name[HOST_NAME_MAX];
189 if (config.nas_ip_address == NULL) {
190 if (gethostname(name, sizeof(name)) != 0) {
191 die(STATE_UNKNOWN, _("gethostname() failed!\n"));
192 }
193 config.nas_ip_address = name;
194 }
195
196 struct sockaddr_storage radius_server_socket;
197 if (!dns_lookup(config.nas_ip_address, &radius_server_socket, AF_UNSPEC)) {
198 die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
199 }
200
201 uint32_t client_id = ntohl(((struct sockaddr_in *)&radius_server_socket)->sin_addr.s_addr);
202 if (my_rc_avpair_add(&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL) {
203 die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
204 }
205
206 my_rc_buildreq(&data, PW_ACCESS_REQUEST, config.server, config.port, (int)timeout_interval,
207 config.retries);
208
158#ifdef RC_BUFFER_LEN 209#ifdef RC_BUFFER_LEN
159 char msg[RC_BUFFER_LEN]; 210 char msg[RC_BUFFER_LEN];
160#else 211#else
161 char msg[BUFFER_LEN]; 212 char msg[BUFFER_LEN];
162#endif 213#endif
163 SEND_DATA data;
164 int result = STATE_UNKNOWN;
165 uint32_t client_id, service;
166 char *str;
167 214
168 setlocale (LC_ALL, ""); 215 int result = my_rc_send_server(&data, msg);
169 bindtextdomain (PACKAGE, LOCALEDIR); 216 rc_avpair_free(data.send_pairs);
170 textdomain (PACKAGE); 217 if (data.receive_pairs) {
171 218 rc_avpair_free(data.receive_pairs);
172 /* Parse extra opts if any */ 219 }
173 argv=np_extra_opts (&argc, argv, progname);
174 220
175 if (process_arguments (argc, argv) == ERROR) 221 if (result == TIMEOUT_RC) {
176 usage4 (_("Could not parse arguments")); 222 printf("Timeout\n");
223 exit(STATE_CRITICAL);
224 }
177 225
178 str = strdup ("dictionary"); 226 if (result == ERROR_RC) {
179 if ((config_file && my_rc_read_config (config_file)) || 227 printf(_("Auth Error\n"));
180 my_rc_read_dictionary (my_rc_conf_str (str))) 228 exit(STATE_CRITICAL);
181 die (STATE_UNKNOWN, _("Config file error\n")); 229 }
182 230
183 service = PW_AUTHENTICATE_ONLY; 231 if (result == REJECT_RC) {
232 printf(_("Auth Failed\n"));
233 exit(STATE_WARNING);
234 }
184 235
185 memset (&data, 0, sizeof(data)); 236 if (result == BADRESP_RC) {
186 if (!(my_rc_avpair_add (&data.send_pairs, PW_SERVICE_TYPE, &service, 0) && 237 printf(_("Bad Response\n"));
187 my_rc_avpair_add (&data.send_pairs, PW_USER_NAME, username, 0) && 238 exit(STATE_WARNING);
188 my_rc_avpair_add (&data.send_pairs, PW_USER_PASSWORD, password, 0) 239 }
189 ))
190 die (STATE_UNKNOWN, _("Out of Memory?\n"));
191 240
192 if (nasid != NULL) { 241 if (config.expect && !strstr(msg, config.expect)) {
193 if (!(my_rc_avpair_add (&data.send_pairs, PW_NAS_IDENTIFIER, nasid, 0))) 242 printf("%s\n", msg);
194 die (STATE_UNKNOWN, _("Invalid NAS-Identifier\n")); 243 exit(STATE_WARNING);
195 } 244 }
196 245
197 if (nasipaddress == NULL) { 246 if (result == OK_RC) {
198 if (gethostname (name, sizeof(name)) != 0) 247 printf(_("Auth OK\n"));
199 die (STATE_UNKNOWN, _("gethostname() failed!\n")); 248 exit(STATE_OK);
200 nasipaddress = name;
201 } 249 }
202 if (!dns_lookup (nasipaddress, &ss, AF_INET)) /* TODO: Support IPv6. */ 250
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); 251 (void)snprintf(msg, sizeof(msg), _("Unexpected result code %d"), result);
229 die (STATE_UNKNOWN, "%s\n", msg); 252 printf("%s\n", msg);
253 exit(STATE_UNKNOWN);
230} 254}
231 255
232
233
234/* process command-line arguments */ 256/* process command-line arguments */
235int 257check_radius_config_wrapper process_arguments(int argc, char **argv) {
236process_arguments (int argc, char **argv)
237{
238 int c;
239
240 int option = 0;
241 static struct option longopts[] = { 258 static struct option longopts[] = {
242 {"hostname", required_argument, 0, 'H'}, 259 {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'P'},
243 {"port", required_argument, 0, 'P'}, 260 {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'},
244 {"username", required_argument, 0, 'u'}, 261 {"nas-id", required_argument, 0, 'n'}, {"nas-ip-address", required_argument, 0, 'N'},
245 {"password", required_argument, 0, 'p'}, 262 {"filename", required_argument, 0, 'F'}, {"expect", required_argument, 0, 'e'},
246 {"nas-id", required_argument, 0, 'n'}, 263 {"retries", required_argument, 0, 'r'}, {"timeout", required_argument, 0, 't'},
247 {"nas-ip-address", required_argument, 0, 'N'}, 264 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
248 {"filename", required_argument, 0, 'F'}, 265 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
249 {"expect", required_argument, 0, 'e'}, 266
250 {"retries", required_argument, 0, 'r'}, 267 check_radius_config_wrapper result = {
251 {"timeout", required_argument, 0, 't'}, 268 .errorcode = OK,
252 {"verbose", no_argument, 0, 'v'}, 269 .config = check_radius_config_init(),
253 {"version", no_argument, 0, 'V'},
254 {"help", no_argument, 0, 'h'},
255 {0, 0, 0, 0}
256 }; 270 };
257 271
258 while (1) { 272 while (true) {
259 c = getopt_long (argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, 273 int option = 0;
260 &option); 274 int option_index = getopt_long(argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, &option);
261 275
262 if (c == -1 || c == EOF || c == 1) 276 if (option_index == -1 || option_index == EOF || option_index == 1) {
263 break; 277 break;
278 }
264 279
265 switch (c) { 280 switch (option_index) {
266 case '?': /* print short usage statement if args not parsable */ 281 case '?': /* print short usage statement if args not parsable */
267 usage5 (); 282 usage5();
268 case 'h': /* help */ 283 case 'h': /* help */
269 print_help (); 284 print_help();
270 exit (STATE_UNKNOWN); 285 exit(STATE_UNKNOWN);
271 case 'V': /* version */ 286 case 'V': /* version */
272 print_revision (progname, NP_VERSION); 287 print_revision(progname, NP_VERSION);
273 exit (STATE_UNKNOWN); 288 exit(STATE_UNKNOWN);
274 case 'v': /* verbose mode */ 289 case 'v': /* verbose mode */
275 verbose = true; 290 verbose = true;
276 break; 291 break;
277 case 'H': /* hostname */ 292 case 'H': /* hostname */
278 if (!is_host (optarg)) { 293 if (!is_host(optarg)) {
279 usage2 (_("Invalid hostname/address"), optarg); 294 usage2(_("Invalid hostname/address"), optarg);
280 } 295 }
281 server = optarg; 296 result.config.server = optarg;
282 break; 297 break;
283 case 'P': /* port */ 298 case 'P': /* port */
284 if (is_intnonneg (optarg)) 299 if (is_intnonneg(optarg)) {
285 port = (unsigned short)atoi (optarg); 300 result.config.port = (unsigned short)atoi(optarg);
286 else 301 } else {
287 usage4 (_("Port must be a positive integer")); 302 usage4(_("Port must be a positive integer"));
303 }
288 break; 304 break;
289 case 'u': /* username */ 305 case 'u': /* username */
290 username = optarg; 306 result.config.username = optarg;
291 break; 307 break;
292 case 'p': /* password */ 308 case 'p': /* password */
293 password = strdup(optarg); 309 result.config.password = strdup(optarg);
294 310
295 /* Delete the password from process list */ 311 /* Delete the password from process list */
296 while (*optarg != '\0') { 312 while (*optarg != '\0') {
@@ -298,119 +314,118 @@ process_arguments (int argc, char **argv)
298 optarg++; 314 optarg++;
299 } 315 }
300 break; 316 break;
301 case 'n': /* nas id */ 317 case 'n': /* nas id */
302 nasid = optarg; 318 result.config.nas_id = optarg;
303 break; 319 break;
304 case 'N': /* nas ip address */ 320 case 'N': /* nas ip address */
305 nasipaddress = optarg; 321 result.config.nas_ip_address = optarg;
306 break; 322 break;
307 case 'F': /* configuration file */ 323 case 'F': /* configuration file */
308 config_file = optarg; 324 result.config.config_file = optarg;
309 break; 325 break;
310 case 'e': /* expect */ 326 case 'e': /* expect */
311 expect = optarg; 327 result.config.expect = optarg;
312 break; 328 break;
313 case 'r': /* retries */ 329 case 'r': /* retries */
314 if (is_intpos (optarg)) 330 if (is_intpos(optarg)) {
315 retries = atoi (optarg); 331 result.config.retries = atoi(optarg);
316 else 332 } else {
317 usage4 (_("Number of retries must be a positive integer")); 333 usage4(_("Number of retries must be a positive integer"));
334 }
318 break; 335 break;
319 case 't': /* timeout */ 336 case 't': /* timeout */
320 if (is_intpos (optarg)) 337 if (is_intpos(optarg)) {
321 timeout_interval = (unsigned)atoi (optarg); 338 timeout_interval = (unsigned)atoi(optarg);
322 else 339 } else {
323 usage2 (_("Timeout interval must be a positive integer"), optarg); 340 usage2(_("Timeout interval must be a positive integer"), optarg);
341 }
324 break; 342 break;
325 } 343 }
326 } 344 }
327 345
328 if (server == NULL) 346 if (result.config.server == NULL) {
329 usage4 (_("Hostname was not supplied")); 347 usage4(_("Hostname was not supplied"));
330 if (username == NULL) 348 }
331 usage4 (_("User not specified")); 349 if (result.config.username == NULL) {
332 if (password == NULL) 350 usage4(_("User not specified"));
333 usage4 (_("Password not specified")); 351 }
334 if (config_file == NULL) 352 if (result.config.password == NULL) {
335 usage4 (_("Configuration file not specified")); 353 usage4(_("Password not specified"));
354 }
355 if (result.config.config_file == NULL) {
356 usage4(_("Configuration file not specified"));
357 }
336 358
337 return OK; 359 return result;
338} 360}
339 361
340 362void print_help(void) {
341
342void
343print_help (void)
344{
345 char *myport; 363 char *myport;
346 xasprintf (&myport, "%d", PW_AUTH_UDP_PORT); 364 xasprintf(&myport, "%d", PW_AUTH_UDP_PORT);
347 365
348 print_revision (progname, NP_VERSION); 366 print_revision(progname, NP_VERSION);
349 367
350 printf ("Copyright (c) 1999 Robert August Vincent II\n"); 368 printf("Copyright (c) 1999 Robert August Vincent II\n");
351 printf (COPYRIGHT, copyright, email); 369 printf(COPYRIGHT, copyright, email);
352 370
353 printf("%s\n", _("Tests to see if a RADIUS server is accepting connections.")); 371 printf("%s\n", _("Tests to see if a RADIUS server is accepting connections."));
354 372
355 printf ("\n\n"); 373 printf("\n\n");
356 374
357 print_usage (); 375 print_usage();
358 376
359 printf (UT_HELP_VRSN); 377 printf(UT_HELP_VRSN);
360 printf (UT_EXTRA_OPTS); 378 printf(UT_EXTRA_OPTS);
361 379
362 printf (UT_HOST_PORT, 'P', myport); 380 printf(UT_HOST_PORT, 'P', myport);
363 381
364 printf (" %s\n", "-u, --username=STRING"); 382 printf(" %s\n", "-u, --username=STRING");
365 printf (" %s\n", _("The user to authenticate")); 383 printf(" %s\n", _("The user to authenticate"));
366 printf (" %s\n", "-p, --password=STRING"); 384 printf(" %s\n", "-p, --password=STRING");
367 printf (" %s\n", _("Password for authentication (SECURITY RISK)")); 385 printf(" %s\n", _("Password for authentication (SECURITY RISK)"));
368 printf (" %s\n", "-n, --nas-id=STRING"); 386 printf(" %s\n", "-n, --nas-id=STRING");
369 printf (" %s\n", _("NAS identifier")); 387 printf(" %s\n", _("NAS identifier"));
370 printf (" %s\n", "-N, --nas-ip-address=STRING"); 388 printf(" %s\n", "-N, --nas-ip-address=STRING");
371 printf (" %s\n", _("NAS IP Address")); 389 printf(" %s\n", _("NAS IP Address"));
372 printf (" %s\n", "-F, --filename=STRING"); 390 printf(" %s\n", "-F, --filename=STRING");
373 printf (" %s\n", _("Configuration file")); 391 printf(" %s\n", _("Configuration file"));
374 printf (" %s\n", "-e, --expect=STRING"); 392 printf(" %s\n", "-e, --expect=STRING");
375 printf (" %s\n", _("Response string to expect from the server")); 393 printf(" %s\n", _("Response string to expect from the server"));
376 printf (" %s\n", "-r, --retries=INTEGER"); 394 printf(" %s\n", "-r, --retries=INTEGER");
377 printf (" %s\n", _("Number of times to retry a failed connection")); 395 printf(" %s\n", _("Number of times to retry a failed connection"));
378 396
379 printf (UT_CONN_TIMEOUT, timeout_interval); 397 printf(UT_CONN_TIMEOUT, timeout_interval);
380 398
381 printf ("\n"); 399 printf("\n");
382 printf ("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections.")); 400 printf("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections."));
383 printf ("%s\n", _("The server to test must be specified in the invocation, as well as a user")); 401 printf("%s\n", _("The server to test must be specified in the invocation, as well as a user"));
384 printf ("%s\n", _("name and password. A configuration file must be present. The format of")); 402 printf("%s\n", _("name and password. A configuration file must be present. The format of"));
385 printf ("%s\n", _("the configuration file is described in the radiusclient library sources.")); 403 printf("%s\n", _("the configuration file is described in the radiusclient library sources."));
386 printf ("%s\n", _("The password option presents a substantial security issue because the")); 404 printf("%s\n", _("The password option presents a substantial security issue because the"));
387 printf ("%s\n", _("password can possibly be determined by careful watching of the command line")); 405 printf("%s\n",
388 printf ("%s\n", _("in a process listing. This risk is exacerbated because the plugin will")); 406 _("password can possibly be determined by careful watching of the command line"));
389 printf ("%s\n", _("typically be executed at regular predictable intervals. Please be sure that")); 407 printf("%s\n", _("in a process listing. This risk is exacerbated because the plugin will"));
390 printf ("%s\n", _("the password used does not allow access to sensitive system resources.")); 408 printf("%s\n",
391 409 _("typically be executed at regular predictable intervals. Please be sure that"));
392 printf (UT_SUPPORT); 410 printf("%s\n", _("the password used does not allow access to sensitive system resources."));
411
412 printf(UT_SUPPORT);
393} 413}
394 414
395 415void print_usage(void) {
396 416 printf("%s\n", _("Usage:"));
397void 417 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\ 418 [-P port] [-t timeout] [-r retries] [-e expect]\n\
403 [-n nas-id] [-N nas-ip-addr]\n", progname); 419 [-n nas-id] [-N nas-ip-addr]\n",
420 progname);
404} 421}
405 422
406 423int my_rc_read_config(char *config_file_name, rc_handle **rch) {
407 424#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
408int my_rc_read_config(char * a) 425 defined(HAVE_LIBRADCLI)
409{ 426 *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; 427 return (rch == NULL) ? 1 : 0;
413#else 428#else
414 return rc_read_config(a); 429 return rc_read_config(config_file_name);
415#endif 430#endif
416} 431}
diff --git a/plugins/check_radius.d/config.h b/plugins/check_radius.d/config.h
new file mode 100644
index 00000000..b27d31e7
--- /dev/null
+++ b/plugins/check_radius.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#if defined(HAVE_LIBRADCLI)
6# include <radcli/radcli.h>
7#elif defined(HAVE_LIBFREERADIUS_CLIENT)
8# include <freeradius-client.h>
9#elif defined(HAVE_LIBRADIUSCLIENT_NG)
10# include <radiusclient-ng.h>
11#else
12# include <radiusclient.h>
13#endif
14
15typedef struct {
16 char *server;
17 char *username;
18 char *password;
19 char *config_file;
20 char *nas_id;
21 char *nas_ip_address;
22 int retries;
23 unsigned short port;
24
25 char *expect;
26} check_radius_config;
27
28check_radius_config check_radius_config_init() {
29 check_radius_config tmp = {
30 .server = NULL,
31 .username = NULL,
32 .password = NULL,
33 .config_file = NULL,
34 .nas_id = NULL,
35 .nas_ip_address = NULL,
36 .retries = 1,
37 .port = PW_AUTH_UDP_PORT,
38
39 .expect = NULL,
40 };
41 return tmp;
42}
diff --git a/plugins/check_real.c b/plugins/check_real.c
index 369a88b1..66d07f8f 100644
--- a/plugins/check_real.c
+++ b/plugins/check_real.c
@@ -28,6 +28,8 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
32#include <stdio.h>
31const char *progname = "check_real"; 33const char *progname = "check_real";
32const char *copyright = "2000-2024"; 34const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
@@ -35,27 +37,20 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 37#include "common.h"
36#include "netutils.h" 38#include "netutils.h"
37#include "utils.h" 39#include "utils.h"
38 40#include "check_real.d/config.h"
39enum {
40 PORT = 554
41};
42 41
43#define EXPECT "RTSP/1." 42#define EXPECT "RTSP/1."
44#define URL "" 43#define URL ""
45 44
46static int process_arguments(int, char **); 45typedef struct {
46 int errorcode;
47 check_real_config config;
48} check_real_config_wrapper;
49static check_real_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50
47static void print_help(void); 51static void print_help(void);
48void print_usage(void); 52void print_usage(void);
49 53
50static int server_port = PORT;
51static char *server_address;
52static char *host_name;
53static char *server_url = NULL;
54static char *server_expect;
55static int warning_time = 0;
56static bool check_warning_time = false;
57static int critical_time = 0;
58static bool check_critical_time = false;
59static bool verbose = false; 54static bool verbose = false;
60 55
61int main(int argc, char **argv) { 56int main(int argc, char **argv) {
@@ -66,8 +61,12 @@ int main(int argc, char **argv) {
66 /* Parse extra opts if any */ 61 /* Parse extra opts if any */
67 argv = np_extra_opts(&argc, argv, progname); 62 argv = np_extra_opts(&argc, argv, progname);
68 63
69 if (process_arguments(argc, argv) == ERROR) 64 check_real_config_wrapper tmp_config = process_arguments(argc, argv);
65 if (tmp_config.errorcode == ERROR) {
70 usage4(_("Could not parse arguments")); 66 usage4(_("Could not parse arguments"));
67 }
68
69 const check_real_config config = tmp_config.config;
71 70
72 /* initialize alarm signal handling */ 71 /* initialize alarm signal handling */
73 signal(SIGALRM, socket_timeout_alarm_handler); 72 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -78,38 +77,52 @@ int main(int argc, char **argv) {
78 77
79 /* try to connect to the host at the given port number */ 78 /* try to connect to the host at the given port number */
80 int socket; 79 int socket;
81 if (my_tcp_connect(server_address, server_port, &socket) != STATE_OK) 80 if (my_tcp_connect(config.server_address, config.server_port, &socket) != STATE_OK) {
82 die(STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), server_address, server_port); 81 die(STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), config.server_address,
82 config.server_port);
83 }
83 84
84 /* Part I - Server Check */ 85 /* Part I - Server Check */
85 86
86 /* send the OPTIONS request */ 87 /* send the OPTIONS request */
87 char buffer[MAX_INPUT_BUFFER]; 88 char buffer[MAX_INPUT_BUFFER];
88 sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", host_name, server_port); 89 sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", config.host_name, config.server_port);
89 int result = send(socket, buffer, strlen(buffer), 0); 90 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0);
91 if (sent_bytes == -1) {
92 die(STATE_CRITICAL, _("Sending options to %s failed\n"), config.host_name);
93 }
90 94
91 /* send the header sync */ 95 /* send the header sync */
92 sprintf(buffer, "CSeq: 1\r\n"); 96 sprintf(buffer, "CSeq: 1\r\n");
93 result = send(socket, buffer, strlen(buffer), 0); 97 sent_bytes = send(socket, buffer, strlen(buffer), 0);
98 if (sent_bytes == -1) {
99 die(STATE_CRITICAL, _("Sending header sync to %s failed\n"), config.host_name);
100 }
94 101
95 /* send a newline so the server knows we're done with the request */ 102 /* send a newline so the server knows we're done with the request */
96 sprintf(buffer, "\r\n"); 103 sprintf(buffer, "\r\n");
97 result = send(socket, buffer, strlen(buffer), 0); 104 sent_bytes = send(socket, buffer, strlen(buffer), 0);
105 if (sent_bytes == -1) {
106 die(STATE_CRITICAL, _("Sending newline to %s failed\n"), config.host_name);
107 }
98 108
99 /* watch for the REAL connection string */ 109 /* watch for the REAL connection string */
100 result = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); 110 ssize_t received_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
101 111
102 /* return a CRITICAL status if we couldn't read any data */ 112 /* return a CRITICAL status if we couldn't read any data */
103 if (result == -1) 113 if (received_bytes == -1) {
104 die(STATE_CRITICAL, _("No data received from %s\n"), host_name); 114 die(STATE_CRITICAL, _("No data received from %s\n"), config.host_name);
115 }
105 116
117 mp_state_enum result = STATE_OK;
106 char *status_line = NULL; 118 char *status_line = NULL;
107 /* make sure we find the response we are looking for */ 119 /* make sure we find the response we are looking for */
108 if (!strstr(buffer, server_expect)) { 120 if (!strstr(buffer, config.server_expect)) {
109 if (server_port == PORT) 121 if (config.server_port == PORT) {
110 printf("%s\n", _("Invalid REAL response received from host")); 122 printf("%s\n", _("Invalid REAL response received from host"));
111 else 123 } else {
112 printf(_("Invalid REAL response received from host on port %d\n"), server_port); 124 printf(_("Invalid REAL response received from host on port %d\n"), config.server_port);
125 }
113 } else { 126 } else {
114 /* else we got the REAL string, so check the return code */ 127 /* else we got the REAL string, so check the return code */
115 128
@@ -117,69 +130,81 @@ int main(int argc, char **argv) {
117 130
118 result = STATE_OK; 131 result = STATE_OK;
119 132
120 status_line = (char *)strtok(buffer, "\n"); 133 status_line = strtok(buffer, "\n");
121 134
122 if (strstr(status_line, "200")) 135 if (strstr(status_line, "200")) {
123 result = STATE_OK; 136 result = STATE_OK;
137 }
124 138
125 /* client errors result in a warning state */ 139 /* client errors result in a warning state */
126 else if (strstr(status_line, "400")) 140 else if (strstr(status_line, "400")) {
127 result = STATE_WARNING; 141 result = STATE_WARNING;
128 else if (strstr(status_line, "401")) 142 } else if (strstr(status_line, "401")) {
129 result = STATE_WARNING; 143 result = STATE_WARNING;
130 else if (strstr(status_line, "402")) 144 } else if (strstr(status_line, "402")) {
131 result = STATE_WARNING; 145 result = STATE_WARNING;
132 else if (strstr(status_line, "403")) 146 } else if (strstr(status_line, "403")) {
133 result = STATE_WARNING; 147 result = STATE_WARNING;
134 else if (strstr(status_line, "404")) 148 } else if (strstr(status_line, "404")) {
135 result = STATE_WARNING; 149 result = STATE_WARNING;
136 150 } else if (strstr(status_line, "500")) {
137 /* server errors result in a critical state */ 151 /* server errors result in a critical state */
138 else if (strstr(status_line, "500"))
139 result = STATE_CRITICAL; 152 result = STATE_CRITICAL;
140 else if (strstr(status_line, "501")) 153 } else if (strstr(status_line, "501")) {
141 result = STATE_CRITICAL; 154 result = STATE_CRITICAL;
142 else if (strstr(status_line, "502")) 155 } else if (strstr(status_line, "502")) {
143 result = STATE_CRITICAL; 156 result = STATE_CRITICAL;
144 else if (strstr(status_line, "503")) 157 } else if (strstr(status_line, "503")) {
145 result = STATE_CRITICAL; 158 result = STATE_CRITICAL;
146 159 } else {
147 else
148 result = STATE_UNKNOWN; 160 result = STATE_UNKNOWN;
161 }
149 } 162 }
150 163
151 /* Part II - Check stream exists and is ok */ 164 /* Part II - Check stream exists and is ok */
152 if ((result == STATE_OK) && (server_url != NULL)) { 165 if ((result == STATE_OK) && (config.server_url != NULL)) {
153 166
154 /* Part I - Server Check */ 167 /* Part I - Server Check */
155 168
156 /* send the DESCRIBE request */ 169 /* send the DESCRIBE request */
157 sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", host_name, server_port, server_url); 170 sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", config.host_name,
158 result = send(socket, buffer, strlen(buffer), 0); 171 config.server_port, config.server_url);
172
173 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0);
174 if (sent_bytes == -1) {
175 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name);
176 }
159 177
160 /* send the header sync */ 178 /* send the header sync */
161 sprintf(buffer, "CSeq: 2\r\n"); 179 sprintf(buffer, "CSeq: 2\r\n");
162 result = send(socket, buffer, strlen(buffer), 0); 180 sent_bytes = send(socket, buffer, strlen(buffer), 0);
181 if (sent_bytes == -1) {
182 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name);
183 }
163 184
164 /* send a newline so the server knows we're done with the request */ 185 /* send a newline so the server knows we're done with the request */
165 sprintf(buffer, "\r\n"); 186 sprintf(buffer, "\r\n");
166 result = send(socket, buffer, strlen(buffer), 0); 187 sent_bytes = send(socket, buffer, strlen(buffer), 0);
188 if (sent_bytes == -1) {
189 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name);
190 }
167 191
168 /* watch for the REAL connection string */ 192 /* watch for the REAL connection string */
169 result = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); 193 ssize_t recv_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
170 buffer[result] = '\0'; /* null terminate received buffer */ 194 if (recv_bytes == -1) {
171 195 /* return a CRITICAL status if we couldn't read any data */
172 /* return a CRITICAL status if we couldn't read any data */
173 if (result == -1) {
174 printf(_("No data received from host\n")); 196 printf(_("No data received from host\n"));
175 result = STATE_CRITICAL; 197 result = STATE_CRITICAL;
176 } else { 198 } else {
199 buffer[result] = '\0'; /* null terminate received buffer */
177 /* make sure we find the response we are looking for */ 200 /* make sure we find the response we are looking for */
178 if (!strstr(buffer, server_expect)) { 201 if (!strstr(buffer, config.server_expect)) {
179 if (server_port == PORT) 202 if (config.server_port == PORT) {
180 printf("%s\n", _("Invalid REAL response received from host")); 203 printf("%s\n", _("Invalid REAL response received from host"));
181 else 204 } else {
182 printf(_("Invalid REAL response received from host on port %d\n"), server_port); 205 printf(_("Invalid REAL response received from host on port %d\n"),
206 config.server_port);
207 }
183 } else { 208 } else {
184 209
185 /* else we got the REAL string, so check the return code */ 210 /* else we got the REAL string, so check the return code */
@@ -188,51 +213,57 @@ int main(int argc, char **argv) {
188 213
189 result = STATE_OK; 214 result = STATE_OK;
190 215
191 status_line = (char *)strtok(buffer, "\n"); 216 status_line = strtok(buffer, "\n");
192 217
193 if (strstr(status_line, "200")) 218 if (strstr(status_line, "200")) {
194 result = STATE_OK; 219 result = STATE_OK;
220 }
195 221
196 /* client errors result in a warning state */ 222 /* client errors result in a warning state */
197 else if (strstr(status_line, "400")) 223 else if (strstr(status_line, "400")) {
198 result = STATE_WARNING; 224 result = STATE_WARNING;
199 else if (strstr(status_line, "401")) 225 } else if (strstr(status_line, "401")) {
200 result = STATE_WARNING; 226 result = STATE_WARNING;
201 else if (strstr(status_line, "402")) 227 } else if (strstr(status_line, "402")) {
202 result = STATE_WARNING; 228 result = STATE_WARNING;
203 else if (strstr(status_line, "403")) 229 } else if (strstr(status_line, "403")) {
204 result = STATE_WARNING; 230 result = STATE_WARNING;
205 else if (strstr(status_line, "404")) 231 } else if (strstr(status_line, "404")) {
206 result = STATE_WARNING; 232 result = STATE_WARNING;
233 }
207 234
208 /* server errors result in a critical state */ 235 /* server errors result in a critical state */
209 else if (strstr(status_line, "500")) 236 else if (strstr(status_line, "500")) {
210 result = STATE_CRITICAL; 237 result = STATE_CRITICAL;
211 else if (strstr(status_line, "501")) 238 } else if (strstr(status_line, "501")) {
212 result = STATE_CRITICAL; 239 result = STATE_CRITICAL;
213 else if (strstr(status_line, "502")) 240 } else if (strstr(status_line, "502")) {
214 result = STATE_CRITICAL; 241 result = STATE_CRITICAL;
215 else if (strstr(status_line, "503")) 242 } else if (strstr(status_line, "503")) {
216 result = STATE_CRITICAL; 243 result = STATE_CRITICAL;
244 }
217 245
218 else 246 else {
219 result = STATE_UNKNOWN; 247 result = STATE_UNKNOWN;
248 }
220 } 249 }
221 } 250 }
222 } 251 }
223 252
224 /* Return results */ 253 /* Return results */
225 if (result == STATE_OK) { 254 if (result == STATE_OK) {
226 255 if (config.check_critical_time && (end_time - start_time) > config.critical_time) {
227 if (check_critical_time && (end_time - start_time) > critical_time)
228 result = STATE_CRITICAL; 256 result = STATE_CRITICAL;
229 else if (check_warning_time && (end_time - start_time) > warning_time) 257 } else if (config.check_warning_time && (end_time - start_time) > config.warning_time) {
230 result = STATE_WARNING; 258 result = STATE_WARNING;
259 }
231 260
232 /* Put some HTML in here to create a dynamic link */ 261 /* Put some HTML in here to create a dynamic link */
233 printf(_("REAL %s - %d second response time\n"), state_text(result), (int)(end_time - start_time)); 262 printf(_("REAL %s - %d second response time\n"), state_text(result),
234 } else 263 (int)(end_time - start_time));
264 } else {
235 printf("%s\n", status_line); 265 printf("%s\n", status_line);
266 }
236 267
237 /* close the connection */ 268 /* close the connection */
238 close(socket); 269 close(socket);
@@ -240,73 +271,83 @@ int main(int argc, char **argv) {
240 /* reset the alarm */ 271 /* reset the alarm */
241 alarm(0); 272 alarm(0);
242 273
243 return result; 274 exit(result);
244} 275}
245 276
246/* process command-line arguments */ 277/* process command-line arguments */
247int process_arguments(int argc, char **argv) { 278check_real_config_wrapper process_arguments(int argc, char **argv) {
248 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, {"IPaddress", required_argument, 0, 'I'}, 279 static struct option longopts[] = {
249 {"expect", required_argument, 0, 'e'}, {"url", required_argument, 0, 'u'}, 280 {"hostname", required_argument, 0, 'H'}, {"IPaddress", required_argument, 0, 'I'},
250 {"port", required_argument, 0, 'p'}, {"critical", required_argument, 0, 'c'}, 281 {"expect", required_argument, 0, 'e'}, {"url", required_argument, 0, 'u'},
251 {"warning", required_argument, 0, 'w'}, {"timeout", required_argument, 0, 't'}, 282 {"port", required_argument, 0, 'p'}, {"critical", required_argument, 0, 'c'},
252 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 283 {"warning", required_argument, 0, 'w'}, {"timeout", required_argument, 0, 't'},
253 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; 284 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
254 285 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
255 if (argc < 2) 286
256 return ERROR; 287 check_real_config_wrapper result = {
288 .errorcode = OK,
289 .config = check_real_config_init(),
290 };
291
292 if (argc < 2) {
293 result.errorcode = ERROR;
294 return result;
295 }
257 296
258 for (int i = 1; i < argc; i++) { 297 for (int i = 1; i < argc; i++) {
259 if (strcmp("-to", argv[i]) == 0) 298 if (strcmp("-to", argv[i]) == 0) {
260 strcpy(argv[i], "-t"); 299 strcpy(argv[i], "-t");
261 else if (strcmp("-wt", argv[i]) == 0) 300 } else if (strcmp("-wt", argv[i]) == 0) {
262 strcpy(argv[i], "-w"); 301 strcpy(argv[i], "-w");
263 else if (strcmp("-ct", argv[i]) == 0) 302 } else if (strcmp("-ct", argv[i]) == 0) {
264 strcpy(argv[i], "-c"); 303 strcpy(argv[i], "-c");
304 }
265 } 305 }
266 306
267 int option_char;
268 while (true) { 307 while (true) {
269 int option = 0; 308 int option = 0;
270 option_char = getopt_long(argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, &option); 309 int option_char = getopt_long(argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, &option);
271 310
272 if (option_char == -1 || option_char == EOF) 311 if (option_char == -1 || option_char == EOF) {
273 break; 312 break;
313 }
274 314
275 switch (option_char) { 315 switch (option_char) {
276 case 'I': /* hostname */ 316 case 'I': /* hostname */
277 case 'H': /* hostname */ 317 case 'H': /* hostname */
278 if (server_address) 318 if (result.config.server_address) {
279 break; 319 break;
280 else if (is_host(optarg)) 320 } else if (is_host(optarg)) {
281 server_address = optarg; 321 result.config.server_address = optarg;
282 else 322 } else {
283 usage2(_("Invalid hostname/address"), optarg); 323 usage2(_("Invalid hostname/address"), optarg);
324 }
284 break; 325 break;
285 case 'e': /* string to expect in response header */ 326 case 'e': /* string to expect in response header */
286 server_expect = optarg; 327 result.config.server_expect = optarg;
287 break; 328 break;
288 case 'u': /* server URL */ 329 case 'u': /* server URL */
289 server_url = optarg; 330 result.config.server_url = optarg;
290 break; 331 break;
291 case 'p': /* port */ 332 case 'p': /* port */
292 if (is_intpos(optarg)) { 333 if (is_intpos(optarg)) {
293 server_port = atoi(optarg); 334 result.config.server_port = atoi(optarg);
294 } else { 335 } else {
295 usage4(_("Port must be a positive integer")); 336 usage4(_("Port must be a positive integer"));
296 } 337 }
297 break; 338 break;
298 case 'w': /* warning time threshold */ 339 case 'w': /* warning time threshold */
299 if (is_intnonneg(optarg)) { 340 if (is_intnonneg(optarg)) {
300 warning_time = atoi(optarg); 341 result.config.warning_time = atoi(optarg);
301 check_warning_time = true; 342 result.config.check_warning_time = true;
302 } else { 343 } else {
303 usage4(_("Warning time must be a positive integer")); 344 usage4(_("Warning time must be a positive integer"));
304 } 345 }
305 break; 346 break;
306 case 'c': /* critical time threshold */ 347 case 'c': /* critical time threshold */
307 if (is_intnonneg(optarg)) { 348 if (is_intnonneg(optarg)) {
308 critical_time = atoi(optarg); 349 result.config.critical_time = atoi(optarg);
309 check_critical_time = true; 350 result.config.check_critical_time = true;
310 } else { 351 } else {
311 usage4(_("Critical time must be a positive integer")); 352 usage4(_("Critical time must be a positive integer"));
312 } 353 }
@@ -332,25 +373,28 @@ int process_arguments(int argc, char **argv) {
332 } 373 }
333 } 374 }
334 375
335 option_char = optind; 376 int option_char = optind;
336 if (server_address == NULL && argc > option_char) { 377 if (result.config.server_address == NULL && argc > option_char) {
337 if (is_host(argv[option_char])) { 378 if (is_host(argv[option_char])) {
338 server_address = argv[option_char++]; 379 result.config.server_address = argv[option_char++];
339 } else { 380 } else {
340 usage2(_("Invalid hostname/address"), argv[option_char]); 381 usage2(_("Invalid hostname/address"), argv[option_char]);
341 } 382 }
342 } 383 }
343 384
344 if (server_address == NULL) 385 if (result.config.server_address == NULL) {
345 usage4(_("You must provide a server to check")); 386 usage4(_("You must provide a server to check"));
387 }
346 388
347 if (host_name == NULL) 389 if (result.config.host_name == NULL) {
348 host_name = strdup(server_address); 390 result.config.host_name = strdup(result.config.server_address);
391 }
349 392
350 if (server_expect == NULL) 393 if (result.config.server_expect == NULL) {
351 server_expect = strdup(EXPECT); 394 result.config.server_expect = strdup(EXPECT);
395 }
352 396
353 return OK; 397 return result;
354} 398}
355 399
356void print_help(void) { 400void print_help(void) {
@@ -388,7 +432,8 @@ void print_help(void) {
388 printf("%s\n", _("This plugin will attempt to open an RTSP connection with the host.")); 432 printf("%s\n", _("This plugin will attempt to open an RTSP connection with the host."));
389 printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); 433 printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return"));
390 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful connects,")); 434 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful connects,"));
391 printf("%s\n", _("but incorrect response messages from the host result in STATE_WARNING return")); 435 printf("%s\n",
436 _("but incorrect response messages from the host result in STATE_WARNING return"));
392 printf("%s\n", _("values.")); 437 printf("%s\n", _("values."));
393 438
394 printf(UT_SUPPORT); 439 printf(UT_SUPPORT);
diff --git a/plugins/check_real.d/config.h b/plugins/check_real.d/config.h
new file mode 100644
index 00000000..c4663cf9
--- /dev/null
+++ b/plugins/check_real.d/config.h
@@ -0,0 +1,37 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PORT = 554
8};
9
10typedef struct {
11 char *server_address;
12 char *host_name;
13 int server_port;
14 char *server_url;
15
16 char *server_expect;
17 int warning_time;
18 bool check_warning_time;
19 int critical_time;
20 bool check_critical_time;
21} check_real_config;
22
23check_real_config check_real_config_init() {
24 check_real_config tmp = {
25 .server_address = NULL,
26 .host_name = NULL,
27 .server_port = PORT,
28 .server_url = NULL,
29
30 .server_expect = NULL,
31 .warning_time = 0,
32 .check_warning_time = false,
33 .critical_time = 0,
34 .check_critical_time = false,
35 };
36 return tmp;
37}
diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c
index e6369e63..83ad575c 100644
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
@@ -1,32 +1,32 @@
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-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_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 30
31const char *progname = "check_smtp"; 31const char *progname = "check_smtp";
32const char *copyright = "2000-2024"; 32const char *copyright = "2000-2024";
@@ -36,400 +36,415 @@ const char *email = "devel@monitoring-plugins.org";
36#include "netutils.h" 36#include "netutils.h"
37#include "utils.h" 37#include "utils.h"
38#include "base64.h" 38#include "base64.h"
39#include "regex.h"
39 40
40#include <ctype.h> 41#include <ctype.h>
42#include "check_smtp.d/config.h"
43#include "../lib/states.h"
44
45#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
46#define SMTP_HELO "HELO "
47#define SMTP_EHLO "EHLO "
48#define SMTP_LHLO "LHLO "
49#define SMTP_QUIT "QUIT\r\n"
50#define SMTP_STARTTLS "STARTTLS\r\n"
51#define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n"
41 52
53#define EHLO_SUPPORTS_STARTTLS 1
54
55typedef struct {
56 int errorcode;
57 check_smtp_config config;
58} check_smtp_config_wrapper;
59static check_smtp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
60
61int my_recv(check_smtp_config config, void *buf, int num, int socket_descriptor,
62 bool ssl_established) {
42#ifdef HAVE_SSL 63#ifdef HAVE_SSL
43static bool check_cert = false; 64 if ((config.use_starttls || config.use_ssl) && ssl_established) {
44static int days_till_exp_warn, days_till_exp_crit; 65 return np_net_ssl_read(buf, num);
45# define my_recv(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) 66 }
46# define my_send(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) 67 return (int)read(socket_descriptor, buf, (size_t)num);
47#else /* ifndef HAVE_SSL */ 68#else /* ifndef HAVE_SSL */
48# define my_recv(buf, len) read(sd, buf, len) 69 return read(socket_descriptor, buf, len)
49# define my_send(buf, len) send(sd, buf, len, 0)
50#endif 70#endif
71}
51 72
52enum { 73int my_send(check_smtp_config config, void *buf, int num, int socket_descriptor,
53 SMTP_PORT = 25, 74 bool ssl_established) {
54 SMTPS_PORT = 465 75#ifdef HAVE_SSL
55}; 76 if ((config.use_starttls || config.use_ssl) && ssl_established) {
56#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
57#define SMTP_EXPECT "220"
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"
64 77
65#define EHLO_SUPPORTS_STARTTLS 1 78 return np_net_ssl_write(buf, num);
79 }
80 return (int)send(socket_descriptor, buf, (size_t)num, 0);
81#else /* ifndef HAVE_SSL */
82 return send(socket_descriptor, buf, len, 0);
83#endif
84}
66 85
67static int process_arguments (int, char **); 86static void print_help(void);
68static int validate_arguments (void); 87void print_usage(void);
69static void print_help (void); 88static char *smtp_quit(check_smtp_config /*config*/, char /*buffer*/[MAX_INPUT_BUFFER],
70void print_usage (void); 89 int /*socket_descriptor*/, bool /*ssl_established*/);
71static void smtp_quit(void); 90static int recvline(char * /*buf*/, size_t /*bufsize*/, check_smtp_config /*config*/,
72static int recvline(char *, size_t); 91 int /*socket_descriptor*/, bool /*ssl_established*/);
73static int recvlines(char *, size_t); 92static int recvlines(check_smtp_config /*config*/, char * /*buf*/, size_t /*bufsize*/,
74static int my_close(void); 93 int /*socket_descriptor*/, bool /*ssl_established*/);
94static int my_close(int /*socket_descriptor*/);
75 95
76#include "regex.h"
77static regex_t preg;
78static regmatch_t pmatch[10];
79static char errbuf[MAX_INPUT_BUFFER];
80static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
81static int eflags = 0;
82static int errcode, excode;
83
84static int server_port = SMTP_PORT;
85static int server_port_option = 0;
86static char *server_address = NULL;
87static char *server_expect = NULL;
88static char *mail_command = NULL;
89static char *from_arg = NULL;
90static int send_mail_from=0;
91static int ncommands=0;
92static int command_size=0;
93static int nresponses=0;
94static int response_size=0;
95static char **commands = NULL;
96static char **responses = NULL;
97static char *authtype = NULL;
98static char *authuser = NULL;
99static char *authpass = NULL;
100static double warning_time = 0;
101static bool check_warning_time = false;
102static double critical_time = 0;
103static bool check_critical_time = false;
104static int verbose = 0; 96static int verbose = 0;
105static bool use_ssl = false;
106static bool use_starttls = false;
107static bool use_sni = false;
108static bool use_proxy_prefix = false;
109static bool use_ehlo = false;
110static bool use_lhlo = false;
111static bool ssl_established = false;
112static char *localhostname = NULL;
113static int sd;
114static char buffer[MAX_INPUT_BUFFER];
115enum {
116 TCP_PROTOCOL = 1,
117 UDP_PROTOCOL = 2,
118};
119static bool ignore_send_quit_failure = false;
120
121
122int
123main (int argc, char **argv)
124{
125 bool supports_tls = false;
126 int n = 0;
127 double elapsed_time;
128 long microsec;
129 int result = STATE_UNKNOWN;
130 char *cmd_str = NULL;
131 char *helocmd = NULL;
132 char *error_msg = "";
133 char *server_response = NULL;
134 struct timeval tv;
135 97
136 /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ 98int main(int argc, char **argv) {
137 (void) signal (SIGPIPE, SIG_IGN); 99 setlocale(LC_ALL, "");
138 100 bindtextdomain(PACKAGE, LOCALEDIR);
139 setlocale (LC_ALL, ""); 101 textdomain(PACKAGE);
140 bindtextdomain (PACKAGE, LOCALEDIR);
141 textdomain (PACKAGE);
142 102
143 /* Parse extra opts if any */ 103 /* Parse extra opts if any */
144 argv=np_extra_opts (&argc, argv, progname); 104 argv = np_extra_opts(&argc, argv, progname);
145 105
146 if (process_arguments (argc, argv) == ERROR) 106 check_smtp_config_wrapper tmp_config = process_arguments(argc, argv);
147 usage4 (_("Could not parse arguments")); 107
108 if (tmp_config.errorcode == ERROR) {
109 usage4(_("Could not parse arguments"));
110 }
111
112 const check_smtp_config config = tmp_config.config;
148 113
149 /* If localhostname not set on command line, use gethostname to set */ 114 /* If localhostname not set on command line, use gethostname to set */
150 if(! localhostname){ 115 char *localhostname = config.localhostname;
151 localhostname = malloc (HOST_MAX_BYTES); 116 if (!localhostname) {
152 if(!localhostname){ 117 localhostname = malloc(HOST_MAX_BYTES);
118 if (!localhostname) {
153 printf(_("malloc() failed!\n")); 119 printf(_("malloc() failed!\n"));
154 return STATE_CRITICAL; 120 exit(STATE_CRITICAL);
155 } 121 }
156 if(gethostname(localhostname, HOST_MAX_BYTES)){ 122 if (gethostname(localhostname, HOST_MAX_BYTES)) {
157 printf(_("gethostname() failed!\n")); 123 printf(_("gethostname() failed!\n"));
158 return STATE_CRITICAL; 124 exit(STATE_CRITICAL);
159 } 125 }
160 } 126 }
161 if(use_lhlo) 127
162 xasprintf (&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); 128 char *helocmd = NULL;
163 else if(use_ehlo) 129 if (config.use_lhlo) {
164 xasprintf (&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n"); 130 xasprintf(&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n");
165 else 131 } else if (config.use_ehlo) {
166 xasprintf (&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n"); 132 xasprintf(&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n");
167 133 } else {
168 if (verbose) 134 xasprintf(&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n");
135 }
136
137 if (verbose) {
169 printf("HELOCMD: %s", helocmd); 138 printf("HELOCMD: %s", helocmd);
139 }
170 140
141 char *mail_command = strdup("MAIL ");
142 char *cmd_str = NULL;
171 /* initialize the MAIL command with optional FROM command */ 143 /* initialize the MAIL command with optional FROM command */
172 xasprintf (&cmd_str, "%sFROM:<%s>%s", mail_command, from_arg, "\r\n"); 144 xasprintf(&cmd_str, "%sFROM:<%s>%s", mail_command, config.from_arg, "\r\n");
173 145
174 if (verbose && send_mail_from) 146 if (verbose && config.send_mail_from) {
175 printf ("FROM CMD: %s", cmd_str); 147 printf("FROM CMD: %s", cmd_str);
148 }
149
150 /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */
151 (void)signal(SIGPIPE, SIG_IGN);
176 152
177 /* initialize alarm signal handling */ 153 /* initialize alarm signal handling */
178 (void) signal (SIGALRM, socket_timeout_alarm_handler); 154 (void)signal(SIGALRM, socket_timeout_alarm_handler);
179 155
180 /* set socket timeout */ 156 /* set socket timeout */
181 (void) alarm (socket_timeout); 157 (void)alarm(socket_timeout);
182 158
159 struct timeval start_time;
183 /* start timer */ 160 /* start timer */
184 gettimeofday (&tv, NULL); 161 gettimeofday(&start_time, NULL);
185 162
163 int socket_descriptor = 0;
186 /* try to connect to the host at the given port number */ 164 /* try to connect to the host at the given port number */
187 result = my_tcp_connect (server_address, server_port, &sd); 165 mp_state_enum result =
166 my_tcp_connect(config.server_address, config.server_port, &socket_descriptor);
188 167
168 char *error_msg = "";
169 char buffer[MAX_INPUT_BUFFER];
170 bool ssl_established = false;
189 if (result == STATE_OK) { /* we connected */ 171 if (result == STATE_OK) { /* we connected */
190 /* If requested, send PROXY header */ 172 /* If requested, send PROXY header */
191 if (use_proxy_prefix) { 173 if (config.use_proxy_prefix) {
192 if (verbose) 174 if (verbose) {
193 printf ("Sending header %s\n", PROXY_PREFIX); 175 printf("Sending header %s\n", PROXY_PREFIX);
194 my_send(PROXY_PREFIX, strlen(PROXY_PREFIX)); 176 }
177 my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
195 } 178 }
196 179
197#ifdef HAVE_SSL 180#ifdef HAVE_SSL
198 if (use_ssl) { 181 if (config.use_ssl) {
199 result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); 182 result = np_net_ssl_init_with_hostname(socket_descriptor,
183 (config.use_sni ? config.server_address : NULL));
200 if (result != STATE_OK) { 184 if (result != STATE_OK) {
201 printf (_("CRITICAL - Cannot create SSL context.\n")); 185 printf(_("CRITICAL - Cannot create SSL context.\n"));
202 close(sd); 186 close(socket_descriptor);
203 np_net_ssl_cleanup(); 187 np_net_ssl_cleanup();
204 return STATE_CRITICAL; 188 exit(STATE_CRITICAL);
205 } else {
206 ssl_established = 1;
207 } 189 }
190 ssl_established = true;
208 } 191 }
209#endif 192#endif
210 193
211 /* watch for the SMTP connection string and */ 194 /* watch for the SMTP connection string and */
212 /* return a WARNING status if we couldn't read any data */ 195 /* return a WARNING status if we couldn't read any data */
213 if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { 196 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
214 printf (_("recv() failed\n")); 197 printf(_("recv() failed\n"));
215 return STATE_WARNING; 198 exit(STATE_WARNING);
216 } 199 }
217 200
201 char *server_response = NULL;
218 /* save connect return (220 hostname ..) for later use */ 202 /* save connect return (220 hostname ..) for later use */
219 xasprintf(&server_response, "%s", buffer); 203 xasprintf(&server_response, "%s", buffer);
220 204
221 /* send the HELO/EHLO command */ 205 /* send the HELO/EHLO command */
222 my_send(helocmd, strlen(helocmd)); 206 my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
223 207
224 /* allow for response to helo command to reach us */ 208 /* allow for response to helo command to reach us */
225 if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { 209 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
226 printf (_("recv() failed\n")); 210 printf(_("recv() failed\n"));
227 return STATE_WARNING; 211 exit(STATE_WARNING);
228 } else if(use_ehlo || use_lhlo){ 212 }
229 if(strstr(buffer, "250 STARTTLS") != NULL || 213
230 strstr(buffer, "250-STARTTLS") != NULL){ 214 bool supports_tls = false;
231 supports_tls=true; 215 if (config.use_ehlo || config.use_lhlo) {
216 if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
217 supports_tls = true;
232 } 218 }
233 } 219 }
234 220
235 if(use_starttls && ! supports_tls){ 221 if (config.use_starttls && !supports_tls) {
236 printf(_("WARNING - TLS not supported by server\n")); 222 printf(_("WARNING - TLS not supported by server\n"));
237 smtp_quit(); 223 smtp_quit(config, buffer, socket_descriptor, ssl_established);
238 return STATE_WARNING; 224 exit(STATE_WARNING);
239 } 225 }
240 226
241#ifdef HAVE_SSL 227#ifdef HAVE_SSL
242 if(use_starttls) { 228 if (config.use_starttls) {
243 /* send the STARTTLS command */ 229 /* send the STARTTLS command */
244 send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); 230 send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
245 231
246 recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */ 232 recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
247 if (!strstr (buffer, SMTP_EXPECT)) { 233 ssl_established); /* wait for it */
248 printf (_("Server does not support STARTTLS\n")); 234 if (!strstr(buffer, SMTP_EXPECT)) {
249 smtp_quit(); 235 printf(_("Server does not support STARTTLS\n"));
250 return STATE_UNKNOWN; 236 smtp_quit(config, buffer, socket_descriptor, ssl_established);
251 } 237 exit(STATE_UNKNOWN);
252 result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); 238 }
253 if(result != STATE_OK) { 239
254 printf (_("CRITICAL - Cannot create SSL context.\n")); 240 result = np_net_ssl_init_with_hostname(socket_descriptor,
255 close(sd); 241 (config.use_sni ? config.server_address : NULL));
256 np_net_ssl_cleanup(); 242 if (result != STATE_OK) {
257 return STATE_CRITICAL; 243 printf(_("CRITICAL - Cannot create SSL context.\n"));
258 } else { 244 close(socket_descriptor);
259 ssl_established = 1; 245 np_net_ssl_cleanup();
260 } 246 exit(STATE_CRITICAL);
261 247 }
262 /* 248
263 * Resend the EHLO command. 249 ssl_established = true;
264 * 250
265 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge 251 /*
266 * obtained from the server, such as the list of SMTP service 252 * Resend the EHLO command.
267 * extensions, which was not obtained from the TLS negotiation 253 *
268 * itself. The client SHOULD send an EHLO command as the first 254 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
269 * command after a successful TLS negotiation.'' For this 255 * obtained from the server, such as the list of SMTP service
270 * reason, some MTAs will not allow an AUTH LOGIN command before 256 * extensions, which was not obtained from the TLS negotiation
271 * we resent EHLO via TLS. 257 * itself. The client SHOULD send an EHLO command as the first
272 */ 258 * command after a successful TLS negotiation.'' For this
273 if (my_send(helocmd, strlen(helocmd)) <= 0) { 259 * reason, some MTAs will not allow an AUTH LOGIN command before
274 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS.")); 260 * we resent EHLO via TLS.
275 my_close(); 261 */
276 return STATE_UNKNOWN; 262 if (my_send(config, helocmd, strlen(helocmd), socket_descriptor, ssl_established) <=
277 } 263 0) {
278 if (verbose) 264 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
279 printf(_("sent %s"), helocmd); 265 my_close(socket_descriptor);
280 if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 266 exit(STATE_UNKNOWN);
281 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); 267 }
282 my_close(); 268
283 return STATE_UNKNOWN; 269 if (verbose) {
284 } 270 printf(_("sent %s"), helocmd);
285 if (verbose) { 271 }
286 printf("%s", buffer);
287 }
288 272
289# ifdef USE_OPENSSL 273 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <=
290 if ( check_cert ) { 274 0) {
291 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 275 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS."));
292 smtp_quit(); 276 my_close(socket_descriptor);
293 my_close(); 277 exit(STATE_UNKNOWN);
294 return result; 278 }
295 } 279
296# endif /* USE_OPENSSL */ 280 if (verbose) {
281 printf("%s", buffer);
282 }
283
284# ifdef USE_OPENSSL
285 if (config.check_cert) {
286 result =
287 np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit);
288 smtp_quit(config, buffer, socket_descriptor, ssl_established);
289 my_close(socket_descriptor);
290 exit(result);
291 }
292# endif /* USE_OPENSSL */
297 } 293 }
298#endif 294#endif
299 295
300 if (verbose) 296 if (verbose) {
301 printf ("%s", buffer); 297 printf("%s", buffer);
298 }
302 299
303 /* save buffer for later use */ 300 /* save buffer for later use */
304 xasprintf(&server_response, "%s%s", server_response, buffer); 301 xasprintf(&server_response, "%s%s", server_response, buffer);
305 /* strip the buffer of carriage returns */ 302 /* strip the buffer of carriage returns */
306 strip (server_response); 303 strip(server_response);
307 304
308 /* make sure we find the droids we are looking for */ 305 /* make sure we find the droids we are looking for */
309 if (!strstr (server_response, server_expect)) { 306 if (!strstr(server_response, config.server_expect)) {
310 if (server_port == SMTP_PORT) 307 if (config.server_port == SMTP_PORT) {
311 printf (_("Invalid SMTP response received from host: %s\n"), server_response); 308 printf(_("Invalid SMTP response received from host: %s\n"), server_response);
312 else 309 } else {
313 printf (_("Invalid SMTP response received from host on port %d: %s\n"), 310 printf(_("Invalid SMTP response received from host on port %d: %s\n"),
314 server_port, server_response); 311 config.server_port, server_response);
315 return STATE_WARNING; 312 }
313 exit(STATE_WARNING);
316 } 314 }
317 315
318 if (send_mail_from) { 316 if (config.send_mail_from) {
319 my_send(cmd_str, strlen(cmd_str)); 317 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
320 if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) 318 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
321 printf("%s", buffer); 319 1 &&
320 verbose) {
321 printf("%s", buffer);
322 }
322 } 323 }
323 324
324 n = 0; 325 int counter = 0;
325 while (n < ncommands) { 326 while (counter < config.ncommands) {
326 xasprintf (&cmd_str, "%s%s", commands[n], "\r\n"); 327 xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
327 my_send(cmd_str, strlen(cmd_str)); 328 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
328 if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) 329 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
330 1 &&
331 verbose) {
329 printf("%s", buffer); 332 printf("%s", buffer);
330 strip (buffer); 333 }
331 if (n < nresponses) { 334 strip(buffer);
332 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 335 if (counter < config.nresponses) {
333 errcode = regcomp (&preg, responses[n], cflags); 336 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
337 regex_t preg;
338 int errcode = regcomp(&preg, config.responses[counter], cflags);
339 char errbuf[MAX_INPUT_BUFFER];
334 if (errcode != 0) { 340 if (errcode != 0) {
335 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 341 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
336 printf (_("Could Not Compile Regular Expression")); 342 printf(_("Could Not Compile Regular Expression"));
337 return ERROR; 343 exit(STATE_UNKNOWN);
338 } 344 }
339 excode = regexec (&preg, buffer, 10, pmatch, eflags); 345
346 regmatch_t pmatch[10];
347 int eflags = 0;
348 int excode = regexec(&preg, buffer, 10, pmatch, eflags);
340 if (excode == 0) { 349 if (excode == 0) {
341 result = STATE_OK; 350 result = STATE_OK;
342 } 351 } else if (excode == REG_NOMATCH) {
343 else if (excode == REG_NOMATCH) {
344 result = STATE_WARNING; 352 result = STATE_WARNING;
345 printf (_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text (result), buffer, commands[n]); 353 printf(_("SMTP %s - Invalid response '%s' to command '%s'\n"),
346 } 354 state_text(result), buffer, config.commands[counter]);
347 else { 355 } else {
348 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER); 356 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
349 printf (_("Execute Error: %s\n"), errbuf); 357 printf(_("Execute Error: %s\n"), errbuf);
350 result = STATE_UNKNOWN; 358 result = STATE_UNKNOWN;
351 } 359 }
352 } 360 }
353 n++; 361 counter++;
354 } 362 }
355 363
356 if (authtype != NULL) { 364 if (config.authtype != NULL) {
357 if (strcmp (authtype, "LOGIN") == 0) { 365 if (strcmp(config.authtype, "LOGIN") == 0) {
358 char *abuf; 366 char *abuf;
359 int ret; 367 int ret;
360 do { 368 do {
361 if (authuser == NULL) { 369 if (config.authuser == NULL) {
362 result = STATE_CRITICAL; 370 result = STATE_CRITICAL;
363 xasprintf(&error_msg, _("no authuser specified, ")); 371 xasprintf(&error_msg, _("no authuser specified, "));
364 break; 372 break;
365 } 373 }
366 if (authpass == NULL) { 374 if (config.authpass == NULL) {
367 result = STATE_CRITICAL; 375 result = STATE_CRITICAL;
368 xasprintf(&error_msg, _("no authpass specified, ")); 376 xasprintf(&error_msg, _("no authpass specified, "));
369 break; 377 break;
370 } 378 }
371 379
372 /* send AUTH LOGIN */ 380 /* send AUTH LOGIN */
373 my_send(SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN)); 381 my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
374 if (verbose) 382 ssl_established);
375 printf (_("sent %s\n"), "AUTH LOGIN"); 383 if (verbose) {
384 printf(_("sent %s\n"), "AUTH LOGIN");
385 }
376 386
377 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 387 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
388 ssl_established)) <= 0) {
378 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, ")); 389 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
379 result = STATE_WARNING; 390 result = STATE_WARNING;
380 break; 391 break;
381 } 392 }
382 if (verbose) 393 if (verbose) {
383 printf (_("received %s\n"), buffer); 394 printf(_("received %s\n"), buffer);
395 }
384 396
385 if (strncmp (buffer, "334", 3) != 0) { 397 if (strncmp(buffer, "334", 3) != 0) {
386 result = STATE_CRITICAL; 398 result = STATE_CRITICAL;
387 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, ")); 399 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
388 break; 400 break;
389 } 401 }
390 402
391 /* encode authuser with base64 */ 403 /* encode authuser with base64 */
392 base64_encode_alloc (authuser, strlen(authuser), &abuf); 404 base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
393 xasprintf(&abuf, "%s\r\n", abuf); 405 xasprintf(&abuf, "%s\r\n", abuf);
394 my_send(abuf, strlen(abuf)); 406 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
395 if (verbose) 407 if (verbose) {
396 printf (_("sent %s\n"), abuf); 408 printf(_("sent %s\n"), abuf);
409 }
397 410
398 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 411 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
412 ssl_established)) <= 0) {
399 result = STATE_CRITICAL; 413 result = STATE_CRITICAL;
400 xasprintf(&error_msg, _("recv() failed after sending authuser, ")); 414 xasprintf(&error_msg, _("recv() failed after sending authuser, "));
401 break; 415 break;
402 } 416 }
403 if (verbose) { 417 if (verbose) {
404 printf (_("received %s\n"), buffer); 418 printf(_("received %s\n"), buffer);
405 } 419 }
406 if (strncmp (buffer, "334", 3) != 0) { 420 if (strncmp(buffer, "334", 3) != 0) {
407 result = STATE_CRITICAL; 421 result = STATE_CRITICAL;
408 xasprintf(&error_msg, _("invalid response received after authuser, ")); 422 xasprintf(&error_msg, _("invalid response received after authuser, "));
409 break; 423 break;
410 } 424 }
411 /* encode authpass with base64 */ 425 /* encode authpass with base64 */
412 base64_encode_alloc (authpass, strlen(authpass), &abuf); 426 base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
413 xasprintf(&abuf, "%s\r\n", abuf); 427 xasprintf(&abuf, "%s\r\n", abuf);
414 my_send(abuf, strlen(abuf)); 428 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
415 if (verbose) { 429 if (verbose) {
416 printf (_("sent %s\n"), abuf); 430 printf(_("sent %s\n"), abuf);
417 } 431 }
418 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 432 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
433 ssl_established)) <= 0) {
419 result = STATE_CRITICAL; 434 result = STATE_CRITICAL;
420 xasprintf(&error_msg, _("recv() failed after sending authpass, ")); 435 xasprintf(&error_msg, _("recv() failed after sending authpass, "));
421 break; 436 break;
422 } 437 }
423 if (verbose) { 438 if (verbose) {
424 printf (_("received %s\n"), buffer); 439 printf(_("received %s\n"), buffer);
425 } 440 }
426 if (strncmp (buffer, "235", 3) != 0) { 441 if (strncmp(buffer, "235", 3) != 0) {
427 result = STATE_CRITICAL; 442 result = STATE_CRITICAL;
428 xasprintf(&error_msg, _("invalid response received after authpass, ")); 443 xasprintf(&error_msg, _("invalid response received after authpass, "));
429 break; 444 break;
430 } 445 }
431 break; 446 break;
432 } while (0); 447 } while (false);
433 } else { 448 } else {
434 result = STATE_CRITICAL; 449 result = STATE_CRITICAL;
435 xasprintf(&error_msg, _("only authtype LOGIN is supported, ")); 450 xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
@@ -437,243 +452,249 @@ main (int argc, char **argv)
437 } 452 }
438 453
439 /* tell the server we're done */ 454 /* tell the server we're done */
440 smtp_quit(); 455 smtp_quit(config, buffer, socket_descriptor, ssl_established);
441 456
442 /* finally close the connection */ 457 /* finally close the connection */
443 close (sd); 458 close(socket_descriptor);
444 } 459 }
445 460
446 /* reset the alarm */ 461 /* reset the alarm */
447 alarm (0); 462 alarm(0);
448 463
449 microsec = deltime (tv); 464 long microsec = deltime(start_time);
450 elapsed_time = (double)microsec / 1.0e6; 465 double elapsed_time = (double)microsec / 1.0e6;
451 466
452 if (result == STATE_OK) { 467 if (result == STATE_OK) {
453 if (check_critical_time && elapsed_time > critical_time) 468 if (config.check_critical_time && elapsed_time > config.critical_time) {
454 result = STATE_CRITICAL; 469 result = STATE_CRITICAL;
455 else if (check_warning_time && elapsed_time > warning_time) 470 } else if (config.check_warning_time && elapsed_time > config.warning_time) {
456 result = STATE_WARNING; 471 result = STATE_WARNING;
472 }
457 } 473 }
458 474
459 printf (_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), 475 printf(_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), state_text(result), error_msg,
460 state_text (result), 476 elapsed_time, verbose ? ", " : "", verbose ? buffer : "",
461 error_msg, 477 fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time,
462 elapsed_time, 478 config.check_critical_time, config.critical_time, true, 0, false, 0));
463 verbose?", ":"", verbose?buffer:"",
464 fperfdata ("time", elapsed_time, "s",
465 (int)check_warning_time, warning_time,
466 (int)check_critical_time, critical_time,
467 true, 0, false, 0));
468 479
469 return result; 480 exit(result);
470} 481}
471 482
472
473
474/* process command-line arguments */ 483/* process command-line arguments */
475int 484check_smtp_config_wrapper process_arguments(int argc, char **argv) {
476process_arguments (int argc, char **argv)
477{
478 int c;
479 char* temp;
480
481 bool implicit_tls = false;
482
483 enum { 485 enum {
484 SNI_OPTION 486 SNI_OPTION = CHAR_MAX + 1
485 }; 487 };
486 488
487 int option = 0; 489 int option = 0;
488 static struct option longopts[] = { 490 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
489 {"hostname", required_argument, 0, 'H'}, 491 {"expect", required_argument, 0, 'e'},
490 {"expect", required_argument, 0, 'e'}, 492 {"critical", required_argument, 0, 'c'},
491 {"critical", required_argument, 0, 'c'}, 493 {"warning", required_argument, 0, 'w'},
492 {"warning", required_argument, 0, 'w'}, 494 {"timeout", required_argument, 0, 't'},
493 {"timeout", required_argument, 0, 't'}, 495 {"port", required_argument, 0, 'p'},
494 {"port", required_argument, 0, 'p'}, 496 {"from", required_argument, 0, 'f'},
495 {"from", required_argument, 0, 'f'}, 497 {"fqdn", required_argument, 0, 'F'},
496 {"fqdn", required_argument, 0, 'F'}, 498 {"authtype", required_argument, 0, 'A'},
497 {"authtype", required_argument, 0, 'A'}, 499 {"authuser", required_argument, 0, 'U'},
498 {"authuser", required_argument, 0, 'U'}, 500 {"authpass", required_argument, 0, 'P'},
499 {"authpass", required_argument, 0, 'P'}, 501 {"command", required_argument, 0, 'C'},
500 {"command", required_argument, 0, 'C'}, 502 {"response", required_argument, 0, 'R'},
501 {"response", required_argument, 0, 'R'}, 503 {"verbose", no_argument, 0, 'v'},
502 {"verbose", no_argument, 0, 'v'}, 504 {"version", no_argument, 0, 'V'},
503 {"version", no_argument, 0, 'V'}, 505 {"use-ipv4", no_argument, 0, '4'},
504 {"use-ipv4", no_argument, 0, '4'}, 506 {"use-ipv6", no_argument, 0, '6'},
505 {"use-ipv6", no_argument, 0, '6'}, 507 {"help", no_argument, 0, 'h'},
506 {"help", no_argument, 0, 'h'}, 508 {"lmtp", no_argument, 0, 'L'},
507 {"lmtp", no_argument, 0, 'L'}, 509 {"ssl", no_argument, 0, 's'},
508 {"ssl", no_argument, 0, 's'}, 510 {"tls", no_argument, 0, 's'},
509 {"tls", no_argument, 0, 's'}, 511 {"starttls", no_argument, 0, 'S'},
510 {"starttls",no_argument,0,'S'}, 512 {"sni", no_argument, 0, SNI_OPTION},
511 {"sni", no_argument, 0, SNI_OPTION}, 513 {"certificate", required_argument, 0, 'D'},
512 {"certificate",required_argument,0,'D'}, 514 {"ignore-quit-failure", no_argument, 0, 'q'},
513 {"ignore-quit-failure",no_argument,0,'q'}, 515 {"proxy", no_argument, 0, 'r'},
514 {"proxy",no_argument,0,'r'}, 516 {0, 0, 0, 0}};
515 {0, 0, 0, 0} 517
518 check_smtp_config_wrapper result = {
519 .config = check_smtp_config_init(),
520 .errorcode = OK,
516 }; 521 };
517 522
518 if (argc < 2) 523 if (argc < 2) {
519 return ERROR; 524 result.errorcode = ERROR;
525 return result;
526 }
520 527
521 for (c = 1; c < argc; c++) { 528 for (int index = 1; index < argc; index++) {
522 if (strcmp ("-to", argv[c]) == 0) 529 if (strcmp("-to", argv[index]) == 0) {
523 strcpy (argv[c], "-t"); 530 strcpy(argv[index], "-t");
524 else if (strcmp ("-wt", argv[c]) == 0) 531 } else if (strcmp("-wt", argv[index]) == 0) {
525 strcpy (argv[c], "-w"); 532 strcpy(argv[index], "-w");
526 else if (strcmp ("-ct", argv[c]) == 0) 533 } else if (strcmp("-ct", argv[index]) == 0) {
527 strcpy (argv[c], "-c"); 534 strcpy(argv[index], "-c");
535 }
528 } 536 }
529 537
530 while (1) { 538 int command_size = 0;
531 c = getopt_long (argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", 539 int response_size = 0;
532 longopts, &option); 540 bool implicit_tls = false;
541 int server_port_option = 0;
542 while (true) {
543 int opt_index =
544 getopt_long(argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", longopts, &option);
533 545
534 if (c == -1 || c == EOF) 546 if (opt_index == -1 || opt_index == EOF) {
535 break; 547 break;
548 }
536 549
537 switch (c) { 550 switch (opt_index) {
538 case 'H': /* hostname */ 551 case 'H': /* hostname */
539 if (is_host (optarg)) { 552 if (is_host(optarg)) {
540 server_address = optarg; 553 result.config.server_address = optarg;
541 } 554 } else {
542 else { 555 usage2(_("Invalid hostname/address"), optarg);
543 usage2 (_("Invalid hostname/address"), optarg);
544 } 556 }
545 break; 557 break;
546 case 'p': /* port */ 558 case 'p': /* port */
547 if (is_intpos (optarg)) 559 if (is_intpos(optarg)) {
548 server_port_option = atoi (optarg); 560 server_port_option = atoi(optarg);
549 else 561 } else {
550 usage4 (_("Port must be a positive integer")); 562 usage4(_("Port must be a positive integer"));
563 }
551 break; 564 break;
552 case 'F': 565 case 'F':
553 /* localhostname */ 566 /* localhostname */
554 localhostname = strdup(optarg); 567 result.config.localhostname = strdup(optarg);
555 break; 568 break;
556 case 'f': /* from argument */ 569 case 'f': /* from argument */
557 from_arg = optarg + strspn(optarg, "<"); 570 result.config.from_arg = optarg + strspn(optarg, "<");
558 from_arg = strndup(from_arg, strcspn(from_arg, ">")); 571 result.config.from_arg =
559 send_mail_from = 1; 572 strndup(result.config.from_arg, strcspn(result.config.from_arg, ">"));
573 result.config.send_mail_from = true;
560 break; 574 break;
561 case 'A': 575 case 'A':
562 authtype = optarg; 576 result.config.authtype = optarg;
563 use_ehlo = true; 577 result.config.use_ehlo = true;
564 break; 578 break;
565 case 'U': 579 case 'U':
566 authuser = optarg; 580 result.config.authuser = optarg;
567 break; 581 break;
568 case 'P': 582 case 'P':
569 authpass = optarg; 583 result.config.authpass = optarg;
570 break; 584 break;
571 case 'e': /* server expect string on 220 */ 585 case 'e': /* server expect string on 220 */
572 server_expect = optarg; 586 result.config.server_expect = optarg;
573 break; 587 break;
574 case 'C': /* commands */ 588 case 'C': /* commands */
575 if (ncommands >= command_size) { 589 if (result.config.ncommands >= command_size) {
576 command_size+=8; 590 command_size += 8;
577 commands = realloc (commands, sizeof(char *) * command_size); 591 result.config.commands =
578 if (commands == NULL) 592 realloc(result.config.commands, sizeof(char *) * command_size);
579 die (STATE_UNKNOWN, 593 if (result.config.commands == NULL) {
580 _("Could not realloc() units [%d]\n"), ncommands); 594 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
595 result.config.ncommands);
596 }
581 } 597 }
582 commands[ncommands] = (char *) malloc (sizeof(char) * 255); 598 result.config.commands[result.config.ncommands] = (char *)malloc(sizeof(char) * 255);
583 strncpy (commands[ncommands], optarg, 255); 599 strncpy(result.config.commands[result.config.ncommands], optarg, 255);
584 ncommands++; 600 result.config.ncommands++;
585 break; 601 break;
586 case 'R': /* server responses */ 602 case 'R': /* server responses */
587 if (nresponses >= response_size) { 603 if (result.config.nresponses >= response_size) {
588 response_size += 8; 604 response_size += 8;
589 responses = realloc (responses, sizeof(char *) * response_size); 605 result.config.responses =
590 if (responses == NULL) 606 realloc(result.config.responses, sizeof(char *) * response_size);
591 die (STATE_UNKNOWN, 607 if (result.config.responses == NULL) {
592 _("Could not realloc() units [%d]\n"), nresponses); 608 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
609 result.config.nresponses);
610 }
593 } 611 }
594 responses[nresponses] = (char *) malloc (sizeof(char) * 255); 612 result.config.responses[result.config.nresponses] = (char *)malloc(sizeof(char) * 255);
595 strncpy (responses[nresponses], optarg, 255); 613 strncpy(result.config.responses[result.config.nresponses], optarg, 255);
596 nresponses++; 614 result.config.nresponses++;
597 break; 615 break;
598 case 'c': /* critical time threshold */ 616 case 'c': /* critical time threshold */
599 if (!is_nonnegative (optarg)) 617 if (!is_nonnegative(optarg)) {
600 usage4 (_("Critical time must be a positive")); 618 usage4(_("Critical time must be a positive"));
601 else { 619 } else {
602 critical_time = strtod (optarg, NULL); 620 result.config.critical_time = strtod(optarg, NULL);
603 check_critical_time = true; 621 result.config.check_critical_time = true;
604 } 622 }
605 break; 623 break;
606 case 'w': /* warning time threshold */ 624 case 'w': /* warning time threshold */
607 if (!is_nonnegative (optarg)) 625 if (!is_nonnegative(optarg)) {
608 usage4 (_("Warning time must be a positive")); 626 usage4(_("Warning time must be a positive"));
609 else { 627 } else {
610 warning_time = strtod (optarg, NULL); 628 result.config.warning_time = strtod(optarg, NULL);
611 check_warning_time = true; 629 result.config.check_warning_time = true;
612 } 630 }
613 break; 631 break;
614 case 'v': /* verbose */ 632 case 'v': /* verbose */
615 verbose++; 633 verbose++;
616 break; 634 break;
617 case 'q': 635 case 'q':
618 ignore_send_quit_failure = true; /* ignore problem sending QUIT */ 636 result.config.ignore_send_quit_failure = true; /* ignore problem sending QUIT */
619 break; 637 break;
620 case 't': /* timeout */ 638 case 't': /* timeout */
621 if (is_intnonneg (optarg)) { 639 if (is_intnonneg(optarg)) {
622 socket_timeout = atoi (optarg); 640 socket_timeout = atoi(optarg);
623 } 641 } else {
624 else { 642 usage4(_("Timeout interval must be a positive integer"));
625 usage4 (_("Timeout interval must be a positive integer"));
626 } 643 }
627 break; 644 break;
628 case 'D': 645 case 'D': {
629 /* Check SSL cert validity */ 646 /* Check SSL cert validity */
630#ifdef USE_OPENSSL 647#ifdef USE_OPENSSL
631 if ((temp=strchr(optarg,','))!=NULL) { 648 char *temp;
632 *temp='\0'; 649 if ((temp = strchr(optarg, ',')) != NULL) {
633 if (!is_intnonneg (optarg)) 650 *temp = '\0';
634 usage2 ("Invalid certificate expiration period", optarg); 651 if (!is_intnonneg(optarg)) {
635 days_till_exp_warn = atoi(optarg); 652 usage2("Invalid certificate expiration period", optarg);
636 *temp=','; 653 }
637 temp++; 654 result.config.days_till_exp_warn = atoi(optarg);
638 if (!is_intnonneg (temp)) 655 *temp = ',';
639 usage2 (_("Invalid certificate expiration period"), temp); 656 temp++;
640 days_till_exp_crit = atoi (temp); 657 if (!is_intnonneg(temp)) {
641 } 658 usage2(_("Invalid certificate expiration period"), temp);
642 else { 659 }
643 days_till_exp_crit=0; 660 result.config.days_till_exp_crit = atoi(temp);
644 if (!is_intnonneg (optarg)) 661 } else {
645 usage2 ("Invalid certificate expiration period", optarg); 662 result.config.days_till_exp_crit = 0;
646 days_till_exp_warn = atoi (optarg); 663 if (!is_intnonneg(optarg)) {
647 } 664 usage2("Invalid certificate expiration period", optarg);
648 check_cert = true; 665 }
649 ignore_send_quit_failure = true; 666 result.config.days_till_exp_warn = atoi(optarg);
667 }
668 result.config.check_cert = true;
669 result.config.ignore_send_quit_failure = true;
650#else 670#else
651 usage (_("SSL support not available - install OpenSSL and recompile")); 671 usage(_("SSL support not available - install OpenSSL and recompile"));
652#endif 672#endif
653 implicit_tls = true; 673 implicit_tls = true;
654 // fallthrough 674 // fallthrough
655 case 's': 675 case 's':
656 /* ssl */ 676 /* ssl */
657 use_ssl = true; 677 result.config.use_ssl = true;
658 server_port = SMTPS_PORT; 678 result.config.server_port = SMTPS_PORT;
659 break; 679 break;
660 case 'S': 680 case 'S':
661 /* starttls */ 681 /* starttls */
662 use_starttls = true; 682 result.config.use_starttls = true;
663 use_ehlo = true; 683 result.config.use_ehlo = true;
664 break; 684 break;
685 }
665 case SNI_OPTION: 686 case SNI_OPTION:
666#ifdef HAVE_SSL 687#ifdef HAVE_SSL
667 use_sni = true; 688 result.config.use_sni = true;
668#else 689#else
669 usage (_("SSL support not available - install OpenSSL and recompile")); 690 usage(_("SSL support not available - install OpenSSL and recompile"));
670#endif 691#endif
671 break; 692 break;
672 case 'r': 693 case 'r':
673 use_proxy_prefix = true; 694 result.config.use_proxy_prefix = true;
674 break; 695 break;
675 case 'L': 696 case 'L':
676 use_lhlo = true; 697 result.config.use_lhlo = true;
677 break; 698 break;
678 case '4': 699 case '4':
679 address_family = AF_INET; 700 address_family = AF_INET;
@@ -682,102 +703,81 @@ process_arguments (int argc, char **argv)
682#ifdef USE_IPV6 703#ifdef USE_IPV6
683 address_family = AF_INET6; 704 address_family = AF_INET6;
684#else 705#else
685 usage4 (_("IPv6 support not available")); 706 usage4(_("IPv6 support not available"));
686#endif 707#endif
687 break; 708 break;
688 case 'V': /* version */ 709 case 'V': /* version */
689 print_revision (progname, NP_VERSION); 710 print_revision(progname, NP_VERSION);
690 exit (STATE_UNKNOWN); 711 exit(STATE_UNKNOWN);
691 case 'h': /* help */ 712 case 'h': /* help */
692 print_help (); 713 print_help();
693 exit (STATE_UNKNOWN); 714 exit(STATE_UNKNOWN);
694 case '?': /* help */ 715 case '?': /* help */
695 usage5 (); 716 usage5();
696 } 717 }
697 } 718 }
698 719
699 c = optind; 720 int c = optind;
700 if (server_address == NULL) { 721 if (result.config.server_address == NULL) {
701 if (argv[c]) { 722 if (argv[c]) {
702 if (is_host (argv[c])) 723 if (is_host(argv[c])) {
703 server_address = argv[c]; 724 result.config.server_address = argv[c];
704 else 725 } else {
705 usage2 (_("Invalid hostname/address"), argv[c]); 726 usage2(_("Invalid hostname/address"), argv[c]);
706 } 727 }
707 else { 728 } else {
708 xasprintf (&server_address, "127.0.0.1"); 729 result.config.server_address = strdup("localhost");
709 } 730 }
710 } 731 }
711 732
712 if (server_expect == NULL) 733 if (result.config.use_starttls && result.config.use_ssl) {
713 server_expect = strdup (SMTP_EXPECT);
714
715 if (mail_command == NULL)
716 mail_command = strdup("MAIL ");
717
718 if (from_arg==NULL)
719 from_arg = strdup(" ");
720
721 if (use_starttls && use_ssl) {
722 if (implicit_tls) { 734 if (implicit_tls) {
723 use_ssl = false; 735 result.config.use_ssl = false;
724 server_port = SMTP_PORT;
725 } else { 736 } else {
726 usage4 (_("Set either -s/--ssl/--tls or -S/--starttls")); 737 usage4(_("Set either -s/--ssl/--tls or -S/--starttls"));
727 } 738 }
728 } 739 }
729 740
730 if (server_port_option != 0) { 741 if (server_port_option != 0) {
731 server_port = server_port_option; 742 result.config.server_port = server_port_option;
732 } 743 }
733 744
734 return validate_arguments (); 745 return result;
735}
736
737
738
739int
740validate_arguments (void)
741{
742 return OK;
743} 746}
744 747
745 748char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int socket_descriptor,
746void 749 bool ssl_established) {
747smtp_quit(void) 750 int sent_bytes =
748{ 751 my_send(config, SMTP_QUIT, strlen(SMTP_QUIT), socket_descriptor, ssl_established);
749 int bytes; 752 if (sent_bytes < 0) {
750 int n; 753 if (config.ignore_send_quit_failure) {
751 754 if (verbose) {
752 n = my_send(SMTP_QUIT, strlen(SMTP_QUIT));
753 if(n < 0) {
754 if(ignore_send_quit_failure) {
755 if(verbose) {
756 printf(_("Connection closed by server before sending QUIT command\n")); 755 printf(_("Connection closed by server before sending QUIT command\n"));
757 } 756 }
758 return; 757 return buffer;
759 } 758 }
760 die (STATE_UNKNOWN, 759 die(STATE_UNKNOWN, _("Connection closed by server before sending QUIT command\n"));
761 _("Connection closed by server before sending QUIT command\n"));
762 } 760 }
763 761
764 if (verbose) 762 if (verbose) {
765 printf(_("sent %s\n"), "QUIT"); 763 printf(_("sent %s\n"), "QUIT");
764 }
766 765
767 /* read the response but don't care about problems */ 766 /* read the response but don't care about problems */
768 bytes = recvlines(buffer, MAX_INPUT_BUFFER); 767 int bytes = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established);
769 if (verbose) { 768 if (verbose) {
770 if (bytes < 0) 769 if (bytes < 0) {
771 printf(_("recv() failed after QUIT.")); 770 printf(_("recv() failed after QUIT."));
772 else if (bytes == 0) 771 } else if (bytes == 0) {
773 printf(_("Connection reset by peer.")); 772 printf(_("Connection reset by peer."));
774 else { 773 } else {
775 buffer[bytes] = '\0'; 774 buffer[bytes] = '\0';
776 printf(_("received %s\n"), buffer); 775 printf(_("received %s\n"), buffer);
777 } 776 }
778 } 777 }
779}
780 778
779 return buffer;
780}
781 781
782/* 782/*
783 * Receive one line, copy it into buf and nul-terminate it. Returns the 783 * Receive one line, copy it into buf and nul-terminate it. Returns the
@@ -788,24 +788,23 @@ smtp_quit(void)
788 * function which buffers the data, move that to netutils.c and change 788 * function which buffers the data, move that to netutils.c and change
789 * check_smtp and other plugins to use that. Also, remove (\r)\n. 789 * check_smtp and other plugins to use that. Also, remove (\r)\n.
790 */ 790 */
791int 791int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor,
792recvline(char *buf, size_t bufsize) 792 bool ssl_established) {
793{
794 int result; 793 int result;
795 unsigned i; 794 int counter;
796 795
797 for (i = result = 0; i < bufsize - 1; i++) { 796 for (counter = result = 0; counter < bufsize - 1; counter++) {
798 if ((result = my_recv(&buf[i], 1)) != 1) 797 if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) {
799 break; 798 break;
800 if (buf[i] == '\n') { 799 }
801 buf[++i] = '\0'; 800 if (buf[counter] == '\n') {
802 return i; 801 buf[++counter] = '\0';
802 return counter;
803 } 803 }
804 } 804 }
805 return (result == 1 || i == 0) ? -2 : result; /* -2 if out of space */ 805 return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */
806} 806}
807 807
808
809/* 808/*
810 * Receive one or more lines, copy them into buf and nul-terminate it. Returns 809 * Receive one or more lines, copy them into buf and nul-terminate it. Returns
811 * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on 810 * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on
@@ -820,117 +819,110 @@ recvline(char *buf, size_t bufsize)
820 * 819 *
821 * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n. 820 * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n.
822 */ 821 */
823int 822int recvlines(check_smtp_config config, char *buf, size_t bufsize, int socket_descriptor,
824recvlines(char *buf, size_t bufsize) 823 bool ssl_established) {
825{ 824 int result;
826 int result, i; 825 int counter;
827 826
828 for (i = 0; /* forever */; i += result) 827 for (counter = 0; /* forever */; counter += result) {
829 if (!((result = recvline(buf + i, bufsize - i)) > 3 && 828 if (!((result = recvline(buf + counter, bufsize - counter, config, socket_descriptor,
830 isdigit((int)buf[i]) && 829 ssl_established)) > 3 &&
831 isdigit((int)buf[i + 1]) && 830 isdigit((int)buf[counter]) && isdigit((int)buf[counter + 1]) &&
832 isdigit((int)buf[i + 2]) && 831 isdigit((int)buf[counter + 2]) && buf[counter + 3] == '-')) {
833 buf[i + 3] == '-'))
834 break; 832 break;
833 }
834 }
835 835
836 return (result <= 0) ? result : result + i; 836 return (result <= 0) ? result : result + counter;
837} 837}
838 838
839 839int my_close(int socket_descriptor) {
840int
841my_close (void)
842{
843 int result; 840 int result;
844 result = close(sd); 841 result = close(socket_descriptor);
845#ifdef HAVE_SSL 842#ifdef HAVE_SSL
846 np_net_ssl_cleanup(); 843 np_net_ssl_cleanup();
847#endif 844#endif
848 return result; 845 return result;
849} 846}
850 847
851 848void print_help(void) {
852void
853print_help (void)
854{
855 char *myport; 849 char *myport;
856 xasprintf (&myport, "%d", SMTP_PORT); 850 xasprintf(&myport, "%d", SMTP_PORT);
857 851
858 print_revision (progname, NP_VERSION); 852 print_revision(progname, NP_VERSION);
859 853
860 printf ("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n"); 854 printf("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n");
861 printf (COPYRIGHT, copyright, email); 855 printf(COPYRIGHT, copyright, email);
862 856
863 printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host.")); 857 printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host."));
864 858
865 printf ("\n\n"); 859 printf("\n\n");
866 860
867 print_usage (); 861 print_usage();
868 862
869 printf (UT_HELP_VRSN); 863 printf(UT_HELP_VRSN);
870 printf (UT_EXTRA_OPTS); 864 printf(UT_EXTRA_OPTS);
871 865
872 printf (UT_HOST_PORT, 'p', myport); 866 printf(UT_HOST_PORT, 'p', myport);
873 867
874 printf (UT_IPv46); 868 printf(UT_IPv46);
875 869
876 printf (" %s\n", "-e, --expect=STRING"); 870 printf(" %s\n", "-e, --expect=STRING");
877 printf (_(" String to expect in first line of server response (default: '%s')\n"), SMTP_EXPECT); 871 printf(_(" String to expect in first line of server response (default: '%s')\n"),
878 printf (" %s\n", "-C, --command=STRING"); 872 SMTP_EXPECT);
879 printf (" %s\n", _("SMTP command (may be used repeatedly)")); 873 printf(" %s\n", "-C, --command=STRING");
880 printf (" %s\n", "-R, --response=STRING"); 874 printf(" %s\n", _("SMTP command (may be used repeatedly)"));
881 printf (" %s\n", _("Expected response to command (may be used repeatedly)")); 875 printf(" %s\n", "-R, --response=STRING");
882 printf (" %s\n", "-f, --from=STRING"); 876 printf(" %s\n", _("Expected response to command (may be used repeatedly)"));
883 printf (" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")), 877 printf(" %s\n", "-f, --from=STRING");
884 printf (" %s\n", "-F, --fqdn=STRING"); 878 printf(" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")),
885 printf (" %s\n", _("FQDN used for HELO")); 879 printf(" %s\n", "-F, --fqdn=STRING");
886 printf (" %s\n", "-r, --proxy"); 880 printf(" %s\n", _("FQDN used for HELO"));
887 printf (" %s\n", _("Use PROXY protocol prefix for the connection.")); 881 printf(" %s\n", "-r, --proxy");
882 printf(" %s\n", _("Use PROXY protocol prefix for the connection."));
888#ifdef HAVE_SSL 883#ifdef HAVE_SSL
889 printf (" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); 884 printf(" %s\n", "-D, --certificate=INTEGER[,INTEGER]");
890 printf (" %s\n", _("Minimum number of days a certificate has to be valid.")); 885 printf(" %s\n", _("Minimum number of days a certificate has to be valid."));
891 printf (" %s\n", "-s, --ssl, --tls"); 886 printf(" %s\n", "-s, --ssl, --tls");
892 printf (" %s\n", _("Use SSL/TLS for the connection.")); 887 printf(" %s\n", _("Use SSL/TLS for the connection."));
893 printf (_(" Sets default port to %d.\n"), SMTPS_PORT); 888 printf(_(" Sets default port to %d.\n"), SMTPS_PORT);
894 printf (" %s\n", "-S, --starttls"); 889 printf(" %s\n", "-S, --starttls");
895 printf (" %s\n", _("Use STARTTLS for the connection.")); 890 printf(" %s\n", _("Use STARTTLS for the connection."));
896 printf (" %s\n", "--sni"); 891 printf(" %s\n", "--sni");
897 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 892 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
898#endif 893#endif
899 894
900 printf (" %s\n", "-A, --authtype=STRING"); 895 printf(" %s\n", "-A, --authtype=STRING");
901 printf (" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)")); 896 printf(" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)"));
902 printf (" %s\n", "-U, --authuser=STRING"); 897 printf(" %s\n", "-U, --authuser=STRING");
903 printf (" %s\n", _("SMTP AUTH username")); 898 printf(" %s\n", _("SMTP AUTH username"));
904 printf (" %s\n", "-P, --authpass=STRING"); 899 printf(" %s\n", "-P, --authpass=STRING");
905 printf (" %s\n", _("SMTP AUTH password")); 900 printf(" %s\n", _("SMTP AUTH password"));
906 printf (" %s\n", "-L, --lmtp"); 901 printf(" %s\n", "-L, --lmtp");
907 printf (" %s\n", _("Send LHLO instead of HELO/EHLO")); 902 printf(" %s\n", _("Send LHLO instead of HELO/EHLO"));
908 printf (" %s\n", "-q, --ignore-quit-failure"); 903 printf(" %s\n", "-q, --ignore-quit-failure");
909 printf (" %s\n", _("Ignore failure when sending QUIT command to server")); 904 printf(" %s\n", _("Ignore failure when sending QUIT command to server"));
910
911 printf (UT_WARN_CRIT);
912 905
913 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 906 printf(UT_WARN_CRIT);
914 907
915 printf (UT_VERBOSE); 908 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
909
910 printf(UT_VERBOSE);
916 911
917 printf("\n"); 912 printf("\n");
918 printf ("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); 913 printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return"));
919 printf ("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful")); 914 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful"));
920 printf ("%s\n", _("connects, but incorrect response messages from the host result in")); 915 printf("%s\n", _("connects, but incorrect response messages from the host result in"));
921 printf ("%s\n", _("STATE_WARNING return values.")); 916 printf("%s\n", _("STATE_WARNING return values."));
922 917
923 printf (UT_SUPPORT); 918 printf(UT_SUPPORT);
924} 919}
925 920
926 921void print_usage(void) {
927 922 printf("%s\n", _("Usage:"));
928void 923 printf("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n",
929print_usage (void) 924 progname);
930{ 925 printf("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n");
931 printf ("%s\n", _("Usage:")); 926 printf("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] "
932 printf ("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n", progname); 927 "[-v] \n");
933 printf ("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n");
934 printf ("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] [-v] \n");
935} 928}
936
diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h
new file mode 100644
index 00000000..0a6511ef
--- /dev/null
+++ b/plugins/check_smtp.d/config.h
@@ -0,0 +1,92 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <string.h>
6
7enum {
8 SMTP_PORT = 25,
9 SMTPS_PORT = 465
10};
11
12#define SMTP_EXPECT "220"
13
14typedef struct {
15 int server_port;
16 char *server_address;
17 char *localhostname;
18 char *server_expect;
19 bool ignore_send_quit_failure;
20
21 double warning_time;
22 bool check_warning_time;
23 double critical_time;
24 bool check_critical_time;
25 bool use_ehlo;
26 bool use_lhlo;
27
28 char *from_arg;
29 bool send_mail_from;
30
31 int ncommands;
32 char **commands;
33
34 int 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 bool check_cert;
44 int days_till_exp_warn;
45 int days_till_exp_crit;
46 bool use_ssl;
47 bool use_starttls;
48 bool use_sni;
49#endif
50} check_smtp_config;
51
52check_smtp_config check_smtp_config_init() {
53 check_smtp_config tmp = {
54 .server_port = SMTP_PORT,
55 .server_address = NULL,
56 .localhostname = NULL,
57
58 .server_expect = SMTP_EXPECT,
59 .ignore_send_quit_failure = false,
60
61 .warning_time = 0,
62 .check_warning_time = false,
63 .critical_time = 0,
64 .check_critical_time = false,
65 .use_ehlo = false,
66 .use_lhlo = false,
67
68 .from_arg = strdup(" "),
69 .send_mail_from = false,
70
71 .ncommands = 0,
72 .commands = NULL,
73
74 .nresponses = 0,
75 .responses = NULL,
76
77 .authtype = NULL,
78 .authuser = NULL,
79 .authpass = NULL,
80
81 .use_proxy_prefix = false,
82#ifdef HAVE_SSL
83 .check_cert = false,
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#endif
90 };
91 return tmp;
92}
diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c
index c1d8e2dd..a5a7afe8 100644
--- a/plugins/check_snmp.c
+++ b/plugins/check_snmp.c
@@ -32,716 +32,494 @@ const char *progname = "check_snmp";
32const char *copyright = "1999-2024"; 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 <bits/getopt_core.h>
45#define DEFAULT_AUTH_PROTOCOL "MD5" 45#include <bits/getopt_ext.h>
46#define DEFAULT_PRIV_PROTOCOL "DES" 46#include <strings.h>
47#define DEFAULT_DELIMITER "=" 47#include <stdint.h>
48#define DEFAULT_OUTPUT_DELIMITER " " 48
49#define DEFAULT_BUFFER_SIZE 100 49#include "check_snmp.d/config.h"
50 50#include <stdlib.h>
51#define mark(a) ((a) != 0 ? "*" : "") 51#include <arpa/inet.h>
52 52#include <net-snmp/library/parse.h>
53#define CHECK_UNDEF 0 53#include <net-snmp/net-snmp-config.h>
54#define CRIT_PRESENT 1 54#include <net-snmp/net-snmp-includes.h>
55#define CRIT_STRING 2 55#include <net-snmp/library/snmp.h>
56#define CRIT_REGEX 4 56#include <net-snmp/library/keytools.h>
57#define WARN_PRESENT 8 57#include <net-snmp/library/snmp_api.h>
58 58#include <net-snmp/session_api.h>
59#define OID_COUNT_STEP 8 59#include <net-snmp/definitions.h>
60 60#include <net-snmp/library/asn1.h>
61/* Longopts only arguments */ 61#include <net-snmp/mib_api.h>
62#define L_CALCULATE_RATE CHAR_MAX + 1 62#include <net-snmp/library/snmp_impl.h>
63#define L_RATE_MULTIPLIER CHAR_MAX + 2 63#include <string.h>
64#define L_INVERT_SEARCH CHAR_MAX + 3 64#include "../gl/regex.h"
65#define L_OFFSET CHAR_MAX + 4 65#include "../gl/base64.h"
66#define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 66#include <assert.h>
67 67
68/* Gobble to string - stop incrementing c when c[0] match one of the 68const char DEFAULT_COMMUNITY[] = "public";
69 * characters in s */ 69const char DEFAULT_MIBLIST[] = "ALL";
70#define GOBBLE_TOS(c, s) \ 70#define DEFAULT_AUTH_PROTOCOL "MD5"
71 while (c[0] != '\0' && strchr(s, c[0]) == NULL) { \ 71
72 c++; \ 72#ifdef HAVE_USM_DES_PRIV_PROTOCOL
73# define DEFAULT_PRIV_PROTOCOL "DES"
74#else
75# define DEFAULT_PRIV_PROTOCOL "AES"
76#endif
77
78typedef struct proces_arguments_wrapper {
79 int errorcode;
80 check_snmp_config config;
81} process_arguments_wrapper;
82
83static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
84static char *trim_whitespaces_and_check_quoting(char *str);
85static char *get_next_argument(char *str);
86void print_usage(void);
87void print_help(void);
88
89int verbose = 0;
90
91typedef struct {
92 int errorcode;
93 char *state_string;
94} gen_state_string_type;
95gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) {
96 char *encoded_string = NULL;
97 gen_state_string_type result = {.errorcode = OK, .state_string = NULL};
98
99 if (verbose > 1) {
100 printf("%s:\n", __FUNCTION__);
101 for (size_t i = 0; i < num_of_entries; i++) {
102 printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp));
103 switch (entries[i].type) {
104 case ASN_GAUGE:
105 printf("Type GAUGE\n");
106 break;
107 case ASN_TIMETICKS:
108 printf("Type TIMETICKS\n");
109 break;
110 case ASN_COUNTER:
111 printf("Type COUNTER\n");
112 break;
113 case ASN_UINTEGER:
114 printf("Type UINTEGER\n");
115 break;
116 case ASN_COUNTER64:
117 printf("Type COUNTER64\n");
118 break;
119 case ASN_FLOAT:
120 printf("Type FLOAT\n");
121 case ASN_DOUBLE:
122 printf("Type DOUBLE\n");
123 break;
124 case ASN_INTEGER:
125 printf("Type INTEGER\n");
126 break;
127 }
128
129 switch (entries[i].type) {
130 case ASN_GAUGE:
131 case ASN_TIMETICKS:
132 case ASN_COUNTER:
133 case ASN_UINTEGER:
134 case ASN_COUNTER64:
135 printf("Value %llu\n", entries[i].value.uIntVal);
136 break;
137 case ASN_FLOAT:
138 case ASN_DOUBLE:
139 printf("Value %f\n", entries[i].value.doubleVal);
140 break;
141 case ASN_INTEGER:
142 printf("Value %lld\n", entries[i].value.intVal);
143 break;
144 }
145 }
146 }
147
148 idx_t encoded = base64_encode_alloc((const char *)entries,
149 (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)),
150 &encoded_string);
151
152 if (encoded > 0 && encoded_string != NULL) {
153 // success
154 if (verbose > 1) {
155 printf("encoded string: %s\n", encoded_string);
156 printf("encoded string length: %lu\n", strlen(encoded_string));
157 }
158 result.state_string = encoded_string;
159 return result;
73 } 160 }
74/* Given c, keep track of backslashes (bk) and double-quotes (dq) 161 result.errorcode = ERROR;
75 * from c[0] */ 162 return result;
76#define COUNT_SEQ(c, bk, dq) \ 163}
77 switch (c[0]) { \ 164
78 case '\\': \ 165typedef struct {
79 if (bk) \ 166 int errorcode;
80 bk--; \ 167 check_snmp_state_entry *state;
81 else \ 168} recover_state_data_type;
82 bk++; \ 169recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) {
83 break; \ 170 recover_state_data_type result = {.errorcode = OK, .state = NULL};
84 case '"': \ 171
85 if (!dq) { \ 172 if (verbose > 1) {
86 dq++; \ 173 printf("%s:\n", __FUNCTION__);
87 } else if (!bk) { \ 174 printf("State string: %s\n", state_string);
88 dq--; \ 175 printf("State string length: %lu\n", state_string_length);
89 } else { \
90 bk--; \
91 } \
92 break; \
93 } 176 }
94 177
95static int process_arguments(int, char **); 178 idx_t outlen = 0;
96static int validate_arguments(void); 179 bool decoded =
97static char *thisarg(char *str); 180 base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen);
98static char *nextarg(char *str); 181
99void print_usage(void); 182 if (!decoded) {
100static void print_help(void); 183 if (verbose) {
101static char *multiply(char *str); 184 printf("Failed to decode state string\n");
102 185 }
103#include "regex.h" 186 // failure to decode
104static char regex_expect[MAX_INPUT_BUFFER] = ""; 187 result.errorcode = ERROR;
105static regex_t preg; 188 return result;
106static regmatch_t pmatch[10]; 189 }
107static char errbuf[MAX_INPUT_BUFFER] = ""; 190
108static char perfstr[MAX_INPUT_BUFFER] = "| "; 191 if (result.state == NULL) {
109static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 192 // Memory Error?
110static int eflags = 0; 193 result.errorcode = ERROR;
111static int errcode, excode; 194 return result;
112 195 }
113static char *server_address = NULL; 196
114static char *community = NULL; 197 if (verbose > 1) {
115static char **contextargs = NULL; 198 printf("Recovered %lu entries of size %lu\n",
116static char *context = NULL; 199 (size_t)outlen / sizeof(check_snmp_state_entry), outlen);
117static char **authpriv = NULL; 200
118static char *proto = NULL; 201 for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) {
119static char *seclevel = NULL; 202 printf("Entry timestamp %lu: %s", result.state[i].timestamp,
120static char *secname = NULL; 203 ctime(&result.state[i].timestamp));
121static char *authproto = NULL; 204 switch (result.state[i].type) {
122static char *privproto = NULL; 205 case ASN_GAUGE:
123static char *authpasswd = NULL; 206 printf("Type GAUGE\n");
124static char *privpasswd = NULL; 207 break;
125static int nulloid = STATE_UNKNOWN; 208 case ASN_TIMETICKS:
126static char **oids = NULL; 209 printf("Type TIMETICKS\n");
127static size_t oids_size = 0; 210 break;
128static char *label; 211 case ASN_COUNTER:
129static char *units; 212 printf("Type COUNTER\n");
130static char *port; 213 break;
131static char *snmpcmd; 214 case ASN_UINTEGER:
132static char string_value[MAX_INPUT_BUFFER] = ""; 215 printf("Type UINTEGER\n");
133static int invert_search = 0; 216 break;
134static char **labels = NULL; 217 case ASN_COUNTER64:
135static char **unitv = NULL; 218 printf("Type COUNTER64\n");
136static size_t nlabels = 0; 219 break;
137static size_t labels_size = OID_COUNT_STEP; 220 case ASN_FLOAT:
138static size_t nunits = 0; 221 printf("Type FLOAT\n");
139static size_t unitv_size = OID_COUNT_STEP; 222 case ASN_DOUBLE:
140static size_t numoids = 0; 223 printf("Type DOUBLE\n");
141static int numauthpriv = 0; 224 break;
142static int numcontext = 0; 225 case ASN_INTEGER:
143static int verbose = 0; 226 printf("Type INTEGER\n");
144static bool usesnmpgetnext = false; 227 break;
145static char *warning_thresholds = NULL; 228 }
146static char *critical_thresholds = NULL; 229
147static thresholds **thlds; 230 switch (result.state[i].type) {
148static size_t thlds_size = OID_COUNT_STEP; 231 case ASN_GAUGE:
149static double *response_value; 232 case ASN_TIMETICKS:
150static size_t response_size = OID_COUNT_STEP; 233 case ASN_COUNTER:
151static int retries = 0; 234 case ASN_UINTEGER:
152static int *eval_method; 235 case ASN_COUNTER64:
153static size_t eval_size = OID_COUNT_STEP; 236 printf("Value %llu\n", result.state[i].value.uIntVal);
154static char *delimiter; 237 break;
155static char *output_delim; 238 case ASN_FLOAT:
156static char *miblist = NULL; 239 case ASN_DOUBLE:
157static bool needmibs = false; 240 printf("Value %f\n", result.state[i].value.doubleVal);
158static int calculate_rate = 0; 241 break;
159static double offset = 0.0; 242 case ASN_INTEGER:
160static int rate_multiplier = 1; 243 printf("Value %lld\n", result.state[i].value.intVal);
161static state_data *previous_state; 244 break;
162static double *previous_value; 245 }
163static size_t previous_size = OID_COUNT_STEP; 246 }
164static int perf_labels = 1; 247 }
165static char *ip_version = ""; 248
166static double multiplier = 1.0; 249 return result;
167static char *fmtstr = "";
168static bool fmtstr_set = false;
169static char buffer[DEFAULT_BUFFER_SIZE];
170static bool ignore_mib_parsing_errors = false;
171
172static char *fix_snmp_range(char *th) {
173 double left;
174 double right;
175 char *colon;
176 char *ret;
177
178 if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0')
179 return th;
180
181 left = strtod(th, NULL);
182 right = strtod(colon + 1, NULL);
183 if (right >= left)
184 return th;
185
186 if ((ret = malloc(strlen(th) + 2)) == NULL)
187 die(STATE_UNKNOWN, _("Cannot malloc"));
188 *colon = '\0';
189 sprintf(ret, "@%s:%s", colon + 1, th);
190 free(th);
191 return ret;
192} 250}
193 251
194int main(int argc, char **argv) { 252int main(int argc, char **argv) {
195 int len;
196 int total_oids;
197 size_t line;
198 unsigned int bk_count = 0;
199 unsigned int dq_count = 0;
200 int iresult = STATE_UNKNOWN;
201 int result = STATE_UNKNOWN;
202 int return_code = 0;
203 int external_error = 0;
204 char **command_line = NULL;
205 char *cl_hidden_auth = NULL;
206 char *oidname = NULL;
207 char *response = NULL;
208 char *mult_resp = NULL;
209 char *outbuff;
210 char *ptr = NULL;
211 char *show = NULL;
212 char *th_warn = NULL;
213 char *th_crit = NULL;
214 char type[8] = "";
215 output chld_out;
216 output chld_err;
217 char *previous_string = NULL;
218 char *ap = NULL;
219 char *state_string = NULL;
220 size_t response_length;
221 size_t current_length;
222 size_t string_length;
223 char *temp_string = NULL;
224 char *quote_string = NULL;
225 time_t current_time;
226 double temp_double;
227 time_t duration;
228 char *conv = "12345678";
229 int is_counter = 0;
230
231 setlocale(LC_ALL, ""); 253 setlocale(LC_ALL, "");
232 bindtextdomain(PACKAGE, LOCALEDIR); 254 bindtextdomain(PACKAGE, LOCALEDIR);
233 textdomain(PACKAGE); 255 textdomain(PACKAGE);
234 256
235 labels = malloc(labels_size * sizeof(*labels));
236 unitv = malloc(unitv_size * sizeof(*unitv));
237 thlds = malloc(thlds_size * sizeof(*thlds));
238 response_value = malloc(response_size * sizeof(*response_value));
239 previous_value = malloc(previous_size * sizeof(*previous_value));
240 eval_method = calloc(eval_size, sizeof(*eval_method));
241 oids = calloc(oids_size, sizeof(char *));
242
243 label = strdup("SNMP");
244 units = strdup("");
245 port = strdup(DEFAULT_PORT);
246 outbuff = strdup("");
247 delimiter = strdup(" = ");
248 output_delim = strdup(DEFAULT_OUTPUT_DELIMITER);
249 timeout_interval = DEFAULT_SOCKET_TIMEOUT; 257 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
250 retries = DEFAULT_RETRIES;
251 258
252 np_init((char *)progname, argc, argv); 259 np_init((char *)progname, argc, argv);
253 260
261 state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv);
262
254 /* Parse extra opts if any */ 263 /* Parse extra opts if any */
255 argv = np_extra_opts(&argc, argv, progname); 264 argv = np_extra_opts(&argc, argv, progname);
256 265
257 np_set_args(argc, argv); 266 np_set_args(argc, argv);
258 267
259 time(&current_time); 268 // Initialize net-snmp before touching the session we are going to use
269 init_snmp("check_snmp");
260 270
261 if (process_arguments(argc, argv) == ERROR) 271 process_arguments_wrapper paw_tmp = process_arguments(argc, argv);
272 if (paw_tmp.errorcode == ERROR) {
262 usage4(_("Could not parse arguments")); 273 usage4(_("Could not parse arguments"));
263
264 if (calculate_rate) {
265 if (!strcmp(label, "SNMP"))
266 label = strdup("SNMP RATE");
267
268 size_t i = 0;
269
270 previous_state = np_state_read();
271 if (previous_state != NULL) {
272 /* Split colon separated values */
273 previous_string = strdup((char *)previous_state->data);
274 while ((ap = strsep(&previous_string, ":")) != NULL) {
275 if (verbose > 2)
276 printf("State for %zd=%s\n", i, ap);
277 while (i >= previous_size) {
278 previous_size += OID_COUNT_STEP;
279 previous_value = realloc(previous_value, previous_size * sizeof(*previous_value));
280 }
281 previous_value[i++] = strtod(ap, NULL);
282 }
283 }
284 } 274 }
285 275
286 /* Populate the thresholds */ 276 check_snmp_config config = paw_tmp.config;
287 th_warn = warning_thresholds;
288 th_crit = critical_thresholds;
289 for (size_t i = 0; i < numoids; i++) {
290 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
291 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
292 /* translate "2:1" to "@1:2" for backwards compatibility */
293 w = w ? fix_snmp_range(w) : NULL;
294 c = c ? fix_snmp_range(c) : NULL;
295
296 while (i >= thlds_size) {
297 thlds_size += OID_COUNT_STEP;
298 thlds = realloc(thlds, thlds_size * sizeof(*thlds));
299 }
300
301 /* Skip empty thresholds, while avoiding segfault */
302 set_thresholds(&thlds[i], w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL, c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
303 if (w) {
304 th_warn = strchr(th_warn, ',');
305 if (th_warn)
306 th_warn++;
307 free(w);
308 }
309 if (c) {
310 th_crit = strchr(th_crit, ',');
311 if (th_crit)
312 th_crit++;
313 free(c);
314 }
315 }
316 277
317 /* Create the command array to execute */ 278 if (config.output_format_is_set) {
318 if (usesnmpgetnext) { 279 mp_set_format(config.output_format);
319 snmpcmd = strdup(PATH_TO_SNMPGETNEXT);
320 } else {
321 snmpcmd = strdup(PATH_TO_SNMPGET);
322 } 280 }
323 281
324 /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ 282 /* Set signal handling and alarm */
325 283 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
326 unsigned index = 0; 284 usage4(_("Cannot catch SIGALRM"));
327 command_line = calloc(11 + numcontext + numauthpriv + 1 + numoids + 1, sizeof(char *));
328
329 command_line[index++] = snmpcmd;
330 command_line[index++] = strdup("-Le");
331 command_line[index++] = strdup("-t");
332 xasprintf(&command_line[index++], "%d", timeout_interval);
333 command_line[index++] = strdup("-r");
334 xasprintf(&command_line[index++], "%d", retries);
335 command_line[index++] = strdup("-m");
336 command_line[index++] = strdup(miblist);
337 command_line[index++] = "-v";
338 command_line[index++] = strdup(proto);
339
340 xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s", snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''",
341 proto);
342
343 if (ignore_mib_parsing_errors) {
344 command_line[index++] = "-Pe";
345 xasprintf(&cl_hidden_auth, "%s -Pe", cl_hidden_auth);
346 } 285 }
347 286
348 for (int i = 0; i < numcontext; i++) { 287 time_t current_time;
349 command_line[index++] = contextargs[i]; 288 time(&current_time);
350 }
351 289
352 for (int i = 0; i < numauthpriv; i++) { 290 if (verbose > 2) {
353 command_line[index++] = authpriv[i]; 291 printf("current time: %s (timestamp: %lu)\n", ctime(&current_time), current_time);
354 } 292 }
355 293
356 xasprintf(&command_line[index++], "%s:%s", server_address, port); 294 snmp_responces response = do_snmp_query(config.snmp_params);
357 295
358 xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", cl_hidden_auth, server_address, port); 296 mp_check overall = mp_check_init();
359 297
360 for (size_t i = 0; i < numoids; i++) { 298 if (response.errorcode == OK) {
361 command_line[index++] = oids[i]; 299 mp_subcheck sc_successfull_query = mp_subcheck_init();
362 xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]); 300 xasprintf(&sc_successfull_query.output, "SNMP query was successful");
301 sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK);
302 mp_add_subcheck_to_check(&overall, sc_successfull_query);
303 } else {
304 // Error treatment here, either partial or whole
305 mp_subcheck sc_failed_query = mp_subcheck_init();
306 xasprintf(&sc_failed_query.output, "SNMP query failed");
307 sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK);
308 mp_add_subcheck_to_check(&overall, sc_failed_query);
309 mp_exit(overall);
363 } 310 }
364 311
365 command_line[index++] = NULL; 312 check_snmp_state_entry *prev_state = NULL;
313 bool have_previous_state = false;
366 314
367 if (verbose) { 315 if (config.evaluation_params.calculate_rate) {
368 printf("%s\n", cl_hidden_auth); 316 state_data *previous_state = np_state_read(stateKey);
369 } 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);
370 325
371 /* Set signal handling and alarm */ 326 if (prev_state_wrapper.errorcode == OK) {
372 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 327 have_previous_state = true;
373 usage4(_("Cannot catch SIGALRM")); 328 prev_state = prev_state_wrapper.state;
374 } 329 } else {
375 alarm(timeout_interval * retries + 5); 330 have_previous_state = false;
376 331 prev_state = NULL;
377 /* Run the command */
378 return_code = cmd_run_array(command_line, &chld_out, &chld_err, 0);
379
380 /* disable alarm again */
381 alarm(0);
382
383 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
384 only return state unknown if return code is non zero or there is no stdout.
385 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
386 */
387 if (return_code != 0)
388 external_error = 1;
389 if (chld_out.lines == 0)
390 external_error = 1;
391 if (external_error) {
392 if (chld_err.lines > 0) {
393 printf(_("External command error: %s\n"), chld_err.line[0]);
394 for (size_t i = 1; i < chld_err.lines; i++) {
395 printf("%s\n", chld_err.line[i]);
396 } 332 }
397 } else {
398 printf(_("External command error with no output (return code: %d)\n"), return_code);
399 } 333 }
400 exit(STATE_UNKNOWN);
401 } 334 }
402 335
403 if (verbose) { 336 check_snmp_state_entry *new_state = NULL;
404 for (size_t i = 0; i < chld_out.lines; i++) { 337 if (config.evaluation_params.calculate_rate) {
405 printf("%s\n", chld_out.line[i]); 338 new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry));
339 if (new_state == NULL) {
340 die(STATE_UNKNOWN, "memory allocation failed");
406 } 341 }
407 } 342 }
408 343
409 line = 0; 344 // We got the the query results, now process them
410 total_oids = 0; 345 for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) {
411 for (size_t i = 0; line < chld_out.lines && i < numoids; line++, i++, total_oids++) { 346 if (verbose > 0) {
412 if (calculate_rate) 347 printf("loop_index: %zu\n", loop_index);
413 conv = "%.10g";
414 else
415 conv = "%.0f";
416
417 ptr = chld_out.line[line];
418 oidname = strpcpy(oidname, ptr, delimiter);
419 response = strstr(ptr, delimiter);
420 if (response == NULL)
421 break;
422
423 if (verbose > 2) {
424 printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i + 1, line + 1, oidname, response);
425 } 348 }
426 349
427 /* Clean up type array - Sol10 does not necessarily zero it out */ 350 check_snmp_state_entry previous_unit_state = {};
428 bzero(type, sizeof(type)); 351 if (config.evaluation_params.calculate_rate && have_previous_state) {
429 352 previous_unit_state = prev_state[loop_index];
430 is_counter = 0; 353 }
431 /* We strip out the datatype indicator for PHBs */
432 if (strstr(response, "Gauge: ")) {
433 show = multiply(strstr(response, "Gauge: ") + 7);
434 } else if (strstr(response, "Gauge32: ")) {
435 show = multiply(strstr(response, "Gauge32: ") + 9);
436 } else if (strstr(response, "Counter32: ")) {
437 show = strstr(response, "Counter32: ") + 11;
438 is_counter = 1;
439 if (!calculate_rate)
440 strcpy(type, "c");
441 } else if (strstr(response, "Counter64: ")) {
442 show = strstr(response, "Counter64: ") + 11;
443 is_counter = 1;
444 if (!calculate_rate)
445 strcpy(type, "c");
446 } else if (strstr(response, "INTEGER: ")) {
447 show = multiply(strstr(response, "INTEGER: ") + 9);
448
449 if (fmtstr_set) {
450 conv = fmtstr;
451 }
452 } else if (strstr(response, "OID: ")) {
453 show = strstr(response, "OID: ") + 5;
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
467 if (dq_count) { /* unfinished line */
468 /* copy show verbatim first */
469 if (!mult_resp)
470 mult_resp = strdup("");
471 xasprintf(&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
472 /* then strip out unmatched double-quote from single-line output */
473 if (show[0] == '"')
474 show++;
475
476 /* Keep reading until we match end of double-quoted string */
477 for (line++; line < chld_out.lines; line++) {
478 ptr = chld_out.line[line];
479 xasprintf(&mult_resp, "%s%s\n", mult_resp, ptr);
480
481 COUNT_SEQ(ptr, bk_count, dq_count)
482 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
483 ptr++;
484 GOBBLE_TOS(ptr, "\n\"\\")
485 COUNT_SEQ(ptr, bk_count, dq_count)
486 }
487 /* Break for loop before next line increment when done */
488 if (!dq_count)
489 break;
490 }
491 }
492
493 } else if (strstr(response, "Timeticks: ")) {
494 show = strstr(response, "Timeticks: ");
495 } else
496 show = response + 3;
497 354
498 iresult = STATE_DEPENDENT; 355 check_snmp_evaluation single_eval =
356 evaluate_single_unit(response.response_values[loop_index], config.evaluation_params,
357 config.snmp_params.test_units[loop_index], current_time,
358 previous_unit_state, have_previous_state);
499 359
500 /* Process this block for numeric comparisons */ 360 if (config.evaluation_params.calculate_rate &&
501 /* Make some special values,like Timeticks numeric only if a threshold is defined */ 361 mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) {
502 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { 362 new_state[loop_index] = single_eval.state;
503 if (verbose > 2) {
504 print_thresholds(" thresholds", thlds[i]);
505 }
506 ptr = strpbrk(show, "-0123456789");
507 if (ptr == NULL) {
508 if (nulloid == 3)
509 die(STATE_UNKNOWN, _("No valid data returned (%s)\n"), show);
510 else if (nulloid == 0)
511 die(STATE_OK, _("No valid data returned (%s)\n"), show);
512 else if (nulloid == 1)
513 die(STATE_WARNING, _("No valid data returned (%s)\n"), show);
514 else if (nulloid == 2)
515 die(STATE_CRITICAL, _("No valid data returned (%s)\n"), show);
516 }
517 while (i >= response_size) {
518 response_size += OID_COUNT_STEP;
519 response_value = realloc(response_value, response_size * sizeof(*response_value));
520 }
521 response_value[i] = strtod(ptr, NULL) + offset;
522
523 if (calculate_rate) {
524 if (previous_state != NULL) {
525 duration = current_time - previous_state->time;
526 if (duration <= 0)
527 die(STATE_UNKNOWN, _("Time duration between plugin calls is invalid"));
528 temp_double = response_value[i] - previous_value[i];
529 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
530 if (is_counter) {
531 if (temp_double < (double)0.0)
532 temp_double += (double)4294967296.0; /* 2^32 */
533 if (temp_double < (double)0.0)
534 temp_double += (double)18446744069414584320.0; /* 2^64-2^32 */
535 ;
536 }
537 /* Convert to per second, then use multiplier */
538 temp_double = temp_double / duration * rate_multiplier;
539 iresult = get_status(temp_double, thlds[i]);
540 xasprintf(&show, conv, temp_double);
541 }
542 } else {
543 iresult = get_status(response_value[i], thlds[i]);
544 xasprintf(&show, conv, response_value[i]);
545 }
546 } 363 }
547 364
548 /* Process this block for string matching */ 365 mp_add_subcheck_to_check(&overall, single_eval.sc);
549 else if (eval_size > i && eval_method[i] & CRIT_STRING) { 366 }
550 if (strcmp(show, string_value))
551 iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK;
552 else
553 iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL;
554 }
555 367
556 /* Process this block for regex matching */ 368 if (config.evaluation_params.calculate_rate) {
557 else if (eval_size > i && eval_method[i] & CRIT_REGEX) { 369 // store state
558 excode = regexec(&preg, response, 10, pmatch, eflags); 370 gen_state_string_type current_state_wrapper =
559 if (excode == 0) { 371 gen_state_string(new_state, config.snmp_params.num_of_test_units);
560 iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL;
561 } else if (excode != REG_NOMATCH) {
562 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
563 printf(_("Execute Error: %s\n"), errbuf);
564 exit(STATE_CRITICAL);
565 } else {
566 iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK;
567 }
568 }
569 372
570 /* Process this block for existence-nonexistence checks */ 373 if (current_state_wrapper.errorcode == OK) {
571 /* TV: Should this be outside of this else block? */ 374 np_state_write_string(stateKey, current_time, current_state_wrapper.state_string);
572 else { 375 } else {
573 if (eval_size > i && eval_method[i] & CRIT_PRESENT) 376 die(STATE_UNKNOWN, "failed to create state string");
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 } 377 }
378 }
379 mp_exit(overall);
380}
580 381
581 /* Result is the worst outcome of all the OIDs tested */ 382/* process command-line arguments */
582 result = max_state(result, iresult); 383static process_arguments_wrapper process_arguments(int argc, char **argv) {
583 384 enum {
584 /* Prepend a label for this OID if there is one */ 385 /* Longopts only arguments */
585 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) 386 invert_search_index = CHAR_MAX + 1,
586 xasprintf(&outbuff, "%s%s%s %s%s%s", outbuff, (i == 0) ? " " : output_delim, labels[i], mark(iresult), show, mark(iresult)); 387 offset_index,
587 else 388 ignore_mib_parsing_errors_index,
588 xasprintf(&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim, mark(iresult), show, mark(iresult)); 389 connection_prefix_index,
589 390 output_format_index,
590 /* Append a unit string for this OID if there is one */ 391 calculate_rate,
591 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL) 392 rate_multiplier
592 xasprintf(&outbuff, "%s %s", outbuff, unitv[i]); 393 };
593 394
594 /* Write perfdata with whatever can be parsed by strtod, if possible */ 395 static struct option longopts[] = {
595 ptr = NULL; 396 STD_LONG_OPTS,
596 strtod(show, &ptr); 397 {"community", required_argument, 0, 'C'},
597 if (ptr > show) { 398 {"oid", required_argument, 0, 'o'},
598 if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) 399 {"object", required_argument, 0, 'o'},
599 temp_string = labels[i]; 400 {"delimiter", required_argument, 0, 'd'},
600 else 401 {"nulloid", required_argument, 0, 'z'},
601 temp_string = oidname; 402 {"output-delimiter", required_argument, 0, 'D'},
602 if (strpbrk(temp_string, " ='\"") == NULL) { 403 {"string", required_argument, 0, 's'},
603 strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); 404 {"timeout", required_argument, 0, 't'},
604 } else { 405 {"regex", required_argument, 0, 'r'},
605 if (strpbrk(temp_string, "'") == NULL) { 406 {"ereg", required_argument, 0, 'r'},
606 quote_string = "'"; 407 {"eregi", required_argument, 0, 'R'},
607 } else { 408 {"label", required_argument, 0, 'l'},
608 quote_string = "\""; 409 {"units", required_argument, 0, 'u'},
609 } 410 {"port", required_argument, 0, 'p'},
610 strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); 411 {"retries", required_argument, 0, 'e'},
611 strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); 412 {"miblist", required_argument, 0, 'm'},
612 strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); 413 {"protocol", required_argument, 0, 'P'},
613 } 414 {"context", required_argument, 0, 'N'},
614 strncat(perfstr, "=", sizeof(perfstr) - strlen(perfstr) - 1); 415 {"seclevel", required_argument, 0, 'L'},
615 len = sizeof(perfstr) - strlen(perfstr) - 1; 416 {"secname", required_argument, 0, 'U'},
616 strncat(perfstr, show, len > ptr - show ? ptr - show : len); 417 {"authproto", required_argument, 0, 'a'},
418 {"privproto", required_argument, 0, 'x'},
419 {"authpasswd", required_argument, 0, 'A'},
420 {"privpasswd", required_argument, 0, 'X'},
421 {"next", no_argument, 0, 'n'},
422 {"offset", required_argument, 0, offset_index},
423 {"invert-search", no_argument, 0, invert_search_index},
424 {"perf-oids", no_argument, 0, 'O'},
425 {"ipv4", no_argument, 0, '4'},
426 {"ipv6", no_argument, 0, '6'},
427 {"multiplier", required_argument, 0, 'M'},
428 {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index},
429 {"connection-prefix", required_argument, 0, connection_prefix_index},
430 {"output-format", required_argument, 0, output_format_index},
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 }
617 441
618 if (strcmp(type, "") != 0) { 442 // Count number of OIDs here first
619 strncat(perfstr, type, sizeof(perfstr) - strlen(perfstr) - 1); 443 int option = 0;
620 } 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);
621 449
622 if (warning_thresholds) { 450 if (option_char == -1 || option_char == EOF) {
623 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 451 break;
624 if (thlds[i]->warning && thlds[i]->warning->text) 452 }
625 strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr) - strlen(perfstr) - 1);
626 }
627 453
628 if (critical_thresholds) { 454 switch (option_char) {
629 if (!warning_thresholds) 455 case 'o': {
630 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 456 // we are going to parse this again, so we work on a copy of that string
631 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 457 char *tmp_oids = strdup(optarg);
632 if (thlds[i]->critical && thlds[i]->critical->text) 458 if (tmp_oids == NULL) {
633 strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr) - strlen(perfstr) - 1); 459 die(STATE_UNKNOWN, "strdup failed");
634 } 460 }
635 461
636 strncat(perfstr, " ", sizeof(perfstr) - strlen(perfstr) - 1); 462 for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL;
637 } 463 ptr = strtok(NULL, ", "), oid_counter++) {
638 }
639
640 /* Save state data, as all data collected now */
641 if (calculate_rate) {
642 string_length = 1024;
643 state_string = malloc(string_length);
644 if (state_string == NULL)
645 die(STATE_UNKNOWN, _("Cannot malloc"));
646
647 current_length = 0;
648 for (int i = 0; i < total_oids; i++) {
649 xasprintf(&temp_string, "%.0f", response_value[i]);
650 if (temp_string == NULL)
651 die(STATE_UNKNOWN, _("Cannot asprintf()"));
652 response_length = strlen(temp_string);
653 if (current_length + response_length > string_length) {
654 string_length = current_length + 1024;
655 state_string = realloc(state_string, string_length);
656 if (state_string == NULL)
657 die(STATE_UNKNOWN, _("Cannot realloc()"));
658 } 464 }
659 strcpy(&state_string[current_length], temp_string); 465 break;
660 current_length = current_length + response_length;
661 state_string[current_length] = ':';
662 current_length++;
663 free(temp_string);
664 } 466 }
665 state_string[--current_length] = '\0'; 467 case '?': /* usage */
666 if (verbose > 2) 468 usage5();
667 printf("State string=%s\n", state_string); 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);
668 476
669 /* This is not strictly the same as time now, but any subtle variations will cancel out */ 477 default:
670 np_state_write_string(current_time, state_string); 478 continue;
671 if (previous_state == NULL) {
672 /* Or should this be highest state? */
673 die(STATE_OK, _("No previous data to calculate rate - assume okay"));
674 } 479 }
675 } 480 }
676 481
677 printf("%s %s -%s %s\n", label, state_text(result), outbuff, perfstr); 482 /* Check whether at least one OID was given */
678 if (mult_resp) 483 if (oid_counter == 0) {
679 printf("%s", mult_resp); 484 die(STATE_UNKNOWN, _("No OIDs specified\n"));
485 }
680 486
681 return result; 487 // Allocate space for test units
682} 488 check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit));
489 if (tmp == NULL) {
490 die(STATE_UNKNOWN, "Failed to calloc");
491 }
683 492
684/* process command-line arguments */ 493 for (size_t i = 0; i < oid_counter; i++) {
685int process_arguments(int argc, char **argv) { 494 tmp[i] = check_snmp_test_unit_init();
686 static struct option longopts[] = {STD_LONG_OPTS,
687 {"community", required_argument, 0, 'C'},
688 {"oid", required_argument, 0, 'o'},
689 {"object", required_argument, 0, 'o'},
690 {"delimiter", required_argument, 0, 'd'},
691 {"nulloid", required_argument, 0, 'z'},
692 {"output-delimiter", required_argument, 0, 'D'},
693 {"string", required_argument, 0, 's'},
694 {"timeout", required_argument, 0, 't'},
695 {"regex", required_argument, 0, 'r'},
696 {"ereg", required_argument, 0, 'r'},
697 {"eregi", required_argument, 0, 'R'},
698 {"label", required_argument, 0, 'l'},
699 {"units", required_argument, 0, 'u'},
700 {"port", required_argument, 0, 'p'},
701 {"retries", required_argument, 0, 'e'},
702 {"miblist", required_argument, 0, 'm'},
703 {"protocol", required_argument, 0, 'P'},
704 {"context", required_argument, 0, 'N'},
705 {"seclevel", required_argument, 0, 'L'},
706 {"secname", required_argument, 0, 'U'},
707 {"authproto", required_argument, 0, 'a'},
708 {"privproto", required_argument, 0, 'x'},
709 {"authpasswd", required_argument, 0, 'A'},
710 {"privpasswd", required_argument, 0, 'X'},
711 {"next", no_argument, 0, 'n'},
712 {"rate", no_argument, 0, L_CALCULATE_RATE},
713 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
714 {"offset", required_argument, 0, L_OFFSET},
715 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
716 {"perf-oids", no_argument, 0, 'O'},
717 {"ipv4", no_argument, 0, '4'},
718 {"ipv6", no_argument, 0, '6'},
719 {"multiplier", required_argument, 0, 'M'},
720 {"fmtstr", required_argument, 0, 'f'},
721 {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS},
722 {0, 0, 0, 0}};
723
724 if (argc < 2)
725 return ERROR;
726
727 /* reverse compatibility for very old non-POSIX usage forms */
728 for (int c = 1; c < argc; c++) {
729 if (strcmp("-to", argv[c]) == 0)
730 strcpy(argv[c], "-t");
731 if (strcmp("-wv", argv[c]) == 0)
732 strcpy(argv[c], "-w");
733 if (strcmp("-cv", argv[c]) == 0)
734 strcpy(argv[c], "-c");
735 } 495 }
736 496
737 size_t j = 0; 497 check_snmp_config config = check_snmp_config_init();
738 size_t jj = 0; 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
739 while (true) { 515 while (true) {
740 int option = 0; 516 int option_char = getopt_long(
741 int option_char = 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:", longopts, &option); 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);
742 519
743 if (option_char == -1 || option_char == EOF) 520 if (option_char == -1 || option_char == EOF) {
744 break; 521 break;
522 }
745 523
746 switch (option_char) { 524 switch (option_char) {
747 case '?': /* usage */ 525 case '?': /* usage */
@@ -758,64 +536,155 @@ int process_arguments(int argc, char **argv) {
758 536
759 /* Connection info */ 537 /* Connection info */
760 case 'C': /* group or community */ 538 case 'C': /* group or community */
761 community = optarg; 539 config.snmp_params.snmp_session.community = (unsigned char *)optarg;
540 config.snmp_params.snmp_session.community_len = strlen(optarg);
762 break; 541 break;
763 case 'H': /* Host or server */ 542 case 'H': /* Host or server */
764 server_address = optarg; 543 config.snmp_params.snmp_session.peername = optarg;
765 break; 544 break;
766 case 'p': /* TCP port number */ 545 case 'p': /*port number */
546 // Add port to "peername" below to not rely on argument order
767 port = optarg; 547 port = optarg;
768 break; 548 break;
769 case 'm': /* List of MIBS */ 549 case 'm': /* List of MIBS */
770 miblist = optarg; 550 miblist = optarg;
771 break; 551 break;
772 case 'n': /* usesnmpgetnext */ 552 case 'n': /* use_getnext instead of get */
773 usesnmpgetnext = true; 553 config.snmp_params.use_getnext = true;
774 break; 554 break;
775 case 'P': /* SNMP protocol version */ 555 case 'P': /* SNMP protocol version */
776 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
777 break; 567 break;
778 case 'N': /* SNMPv3 context */ 568 case 'N': /* SNMPv3 context name */
779 context = optarg; 569 config.snmp_params.snmp_session.contextName = optarg;
570 config.snmp_params.snmp_session.contextNameLen = strlen(optarg);
780 break; 571 break;
781 case 'L': /* security level */ 572 case 'L': /* security level */
782 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 }
783 break; 582 break;
784 case 'U': /* security username */ 583 case 'U': /* security username */
785 secname = optarg; 584 config.snmp_params.snmp_session.securityName = optarg;
585 config.snmp_params.snmp_session.securityNameLen = strlen(optarg);
786 break; 586 break;
787 case 'a': /* auth protocol */ 587 case 'a': /* auth protocol */
788 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 }
789 break; 617 break;
790 case 'x': /* priv protocol */ 618 case 'x': /* priv protocol */
791 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 }
792 break; 659 break;
793 case 'A': /* auth passwd */ 660 case 'A': /* auth passwd */
794 authpasswd = optarg; 661 authpasswd = (unsigned char *)optarg;
795 break; 662 break;
796 case 'X': /* priv passwd */ 663 case 'X': /* priv passwd */
797 privpasswd = optarg; 664 privpasswd = (unsigned char *)optarg;
665 break;
666 case 'e':
667 case 'E':
668 if (!is_integer(optarg)) {
669 usage2(_("Retries interval must be a positive integer"), optarg);
670 } else {
671 config.snmp_params.snmp_session.retries = atoi(optarg);
672 }
798 break; 673 break;
799 case 't': /* timeout period */ 674 case 't': /* timeout period */
800 if (!is_integer(optarg)) 675 if (!is_integer(optarg)) {
801 usage2(_("Timeout interval must be a positive integer"), optarg); 676 usage2(_("Timeout interval must be a positive integer"), optarg);
802 else 677 } else {
803 timeout_interval = atoi(optarg); 678 timeout_interval = (unsigned int)atoi(optarg);
679 }
804 break; 680 break;
805 681
806 /* Test parameters */ 682 /* Test parameters */
807 case 'c': /* critical threshold */ 683 case 'c': /* critical threshold */
808 critical_thresholds = optarg; 684 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true);
809 break; 685 break;
810 case 'w': /* warning threshold */ 686 case 'w': /* warning threshold */
811 warning_thresholds = optarg; 687 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false);
812 break;
813 case 'e': /* PRELIMINARY - may change */
814 case 'E': /* PRELIMINARY - may change */
815 if (!is_integer(optarg))
816 usage2(_("Retries interval must be a positive integer"), optarg);
817 else
818 retries = atoi(optarg);
819 break; 688 break;
820 case 'o': /* object identifier */ 689 case 'o': /* object identifier */
821 if (strspn(optarg, "0123456789.,") != strlen(optarg)) { 690 if (strspn(optarg, "0123456789.,") != strlen(optarg)) {
@@ -824,306 +693,292 @@ int process_arguments(int argc, char **argv) {
824 * 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,
825 * so we have to actually read the mib files 694 * so we have to actually read the mib files
826 */ 695 */
827 needmibs = true; 696 config.snmp_params.need_mibs = true;
828 }
829 for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) {
830 while (j >= oids_size) {
831 oids_size += OID_COUNT_STEP;
832 oids = realloc(oids, oids_size * sizeof(*oids));
833 }
834 oids[j] = strdup(ptr);
835 } 697 }
836 numoids = j; 698
837 if (option_char == 'E' || option_char == 'e') { 699 for (char *ptr = strtok(optarg, ", "); ptr != NULL;
838 jj++; 700 ptr = strtok(NULL, ", "), tmp_oid_counter++) {
839 while (j + 1 >= eval_size) { 701 config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr);
840 eval_size += OID_COUNT_STEP;
841 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
842 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
843 }
844 if (option_char == 'E')
845 eval_method[j + 1] |= WARN_PRESENT;
846 else if (option_char == 'e')
847 eval_method[j + 1] |= CRIT_PRESENT;
848 } 702 }
849 break; 703 break;
850 case 'z': /* Null OID Return Check */ 704 case 'z': /* Null OID Return Check */
851 if (!is_integer(optarg)) 705 if (!is_integer(optarg)) {
852 usage2(_("Exit status must be a positive integer"), optarg); 706 usage2(_("Exit status must be a positive integer"), optarg);
853 else 707 } else {
854 nulloid = atoi(optarg); 708 config.evaluation_params.nulloid_result = atoi(optarg);
709 }
855 break; 710 break;
856 case 's': /* string or substring */ 711 case 's': /* string or substring */
857 strncpy(string_value, optarg, sizeof(string_value) - 1); 712 strncpy(config.evaluation_params.string_cmp_value, optarg,
858 string_value[sizeof(string_value) - 1] = 0; 713 sizeof(config.evaluation_params.string_cmp_value) - 1);
859 while (jj >= eval_size) { 714 config.evaluation_params
860 eval_size += OID_COUNT_STEP; 715 .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0;
861 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 716 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true;
862 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
863 }
864 eval_method[jj++] = CRIT_STRING;
865 break; 717 break;
866 case 'R': /* regex */ 718 case 'R': /* regex */
867 cflags = REG_ICASE; 719 cflags = REG_ICASE;
868 // fall through 720 // fall through
869 case 'r': /* regex */ 721 case 'r': /* regex */
722 {
723 char regex_expect[MAX_INPUT_BUFFER] = "";
870 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 724 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
871 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); 725 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1);
872 regex_expect[sizeof(regex_expect) - 1] = 0; 726 regex_expect[sizeof(regex_expect) - 1] = 0;
873 errcode = regcomp(&preg, regex_expect, cflags); 727 int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags);
874 if (errcode != 0) { 728 if (errcode != 0) {
875 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 729 char errbuf[MAX_INPUT_BUFFER] = "";
876 printf(_("Could Not Compile Regular Expression")); 730 regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf,
877 return ERROR; 731 MAX_INPUT_BUFFER);
878 } 732 printf("Could Not Compile Regular Expression: %s", errbuf);
879 while (jj >= eval_size) { 733 process_arguments_wrapper result = {
880 eval_size += OID_COUNT_STEP; 734 .errorcode = ERROR,
881 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 735 };
882 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); 736 return result;
883 } 737 }
884 eval_method[jj++] = CRIT_REGEX; 738 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true;
885 break; 739 } break;
886
887 /* Format */
888 case 'd': /* delimiter */
889 delimiter = strscpy(delimiter, optarg);
890 break;
891 case 'D': /* output-delimiter */
892 output_delim = strscpy(output_delim, optarg);
893 break;
894 case 'l': /* label */ 740 case 'l': /* label */
895 nlabels++; 741 {
896 if (nlabels > labels_size) { 742 if (labels_counter >= config.snmp_params.num_of_test_units) {
897 labels_size += 8; 743 break;
898 labels = realloc(labels, labels_size * sizeof(*labels)); 744 }
899 if (labels == NULL) 745 char *ptr = trim_whitespaces_and_check_quoting(optarg);
900 die(STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels); 746 if (ptr[0] == '\'') {
747 config.snmp_params.test_units[labels_counter].label = ptr + 1;
748 } else {
749 config.snmp_params.test_units[labels_counter].label = ptr;
901 } 750 }
902 labels[nlabels - 1] = optarg; 751
903 char *ptr = thisarg(optarg); 752 while (ptr && (ptr = get_next_argument(ptr))) {
904 labels[nlabels - 1] = ptr; 753 labels_counter++;
905 if (ptr[0] == '\'') 754 ptr = trim_whitespaces_and_check_quoting(ptr);
906 labels[nlabels - 1] = ptr + 1; 755 if (ptr[0] == '\'') {
907 while (ptr && (ptr = nextarg(ptr))) { 756 config.snmp_params.test_units[labels_counter].label = ptr + 1;
908 nlabels++; 757 } else {
909 if (nlabels > labels_size) { 758 config.snmp_params.test_units[labels_counter].label = ptr;
910 labels_size += 8;
911 labels = realloc(labels, labels_size * sizeof(*labels));
912 if (labels == NULL)
913 die(STATE_UNKNOWN, _("Could not reallocate labels\n"));
914 } 759 }
915 ptr = thisarg(ptr);
916 if (ptr[0] == '\'')
917 labels[nlabels - 1] = ptr + 1;
918 else
919 labels[nlabels - 1] = ptr;
920 } 760 }
921 break; 761 labels_counter++;
762 } break;
922 case 'u': /* units */ 763 case 'u': /* units */
923 units = optarg; 764 {
924 nunits++; 765 if (unitv_counter >= config.snmp_params.num_of_test_units) {
925 if (nunits > unitv_size) { 766 break;
926 unitv_size += 8;
927 unitv = realloc(unitv, unitv_size * sizeof(*unitv));
928 if (unitv == NULL)
929 die(STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
930 } 767 }
931 unitv[nunits - 1] = optarg; 768 char *ptr = trim_whitespaces_and_check_quoting(optarg);
932 ptr = thisarg(optarg); 769 if (ptr[0] == '\'') {
933 unitv[nunits - 1] = ptr; 770 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
934 if (ptr[0] == '\'') 771 } else {
935 unitv[nunits - 1] = ptr + 1; 772 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
936 while (ptr && (ptr = nextarg(ptr))) { 773 }
937 if (nunits > unitv_size) { 774 while (ptr && (ptr = get_next_argument(ptr))) {
938 unitv_size += 8; 775 unitv_counter++;
939 unitv = realloc(unitv, unitv_size * sizeof(*unitv)); 776 ptr = trim_whitespaces_and_check_quoting(ptr);
940 if (units == NULL) 777 if (ptr[0] == '\'') {
941 die(STATE_UNKNOWN, _("Could not realloc() units\n")); 778 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
779 } else {
780 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
942 } 781 }
943 nunits++;
944 ptr = thisarg(ptr);
945 if (ptr[0] == '\'')
946 unitv[nunits - 1] = ptr + 1;
947 else
948 unitv[nunits - 1] = ptr;
949 } 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;
950 break; 788 break;
951 case L_CALCULATE_RATE: 789 case invert_search_index:
952 if (calculate_rate == 0) 790 config.evaluation_params.invert_search = false;
953 np_enable_state(NULL, 1);
954 calculate_rate = 1;
955 break;
956 case L_RATE_MULTIPLIER:
957 if (!is_integer(optarg) || ((rate_multiplier = atoi(optarg)) <= 0))
958 usage2(_("Rate multiplier must be a positive integer"), optarg);
959 break;
960 case L_OFFSET:
961 offset = strtod(optarg, NULL);
962 break;
963 case L_INVERT_SEARCH:
964 invert_search = 1;
965 break; 791 break;
966 case 'O': 792 case 'O':
967 perf_labels = 0; 793 config.evaluation_params.use_oid_as_perf_data_label = true;
968 break; 794 break;
969 case '4': 795 case '4':
796 // The default, do something here to be exclusive to -6 instead of doing nothing?
797 connection_prefix = "udp";
970 break; 798 break;
971 case '6': 799 case '6':
972 xasprintf(&ip_version, "udp6:"); 800 connection_prefix = "udp6";
973 if (verbose > 2) 801 break;
974 printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); 802 case connection_prefix_index:
803 connection_prefix = optarg;
975 break; 804 break;
976 case 'M': 805 case 'M':
977 if (strspn(optarg, "0123456789.,") == strlen(optarg)) { 806 if (strspn(optarg, "0123456789.,") == strlen(optarg)) {
978 multiplier = strtod(optarg, NULL); 807 config.evaluation_params.multiplier = strtod(optarg, NULL);
808 config.evaluation_params.multiplier_set = true;
979 } 809 }
980 break; 810 break;
981 case 'f': 811 case ignore_mib_parsing_errors_index:
982 if (multiplier != 1.0) { 812 config.snmp_params.ignore_mib_parsing_errors = true;
983 fmtstr = optarg; 813 break;
984 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);
985 } 835 }
986 break; 836 break;
987 case L_IGNORE_MIB_PARSING_ERRORS: 837 default:
988 ignore_mib_parsing_errors = true; 838 die(STATE_UNKNOWN, "Unknown option");
989 } 839 }
990 } 840 }
991 841
992 if (server_address == NULL) 842 if (config.snmp_params.snmp_session.peername == NULL) {
993 server_address = argv[optind]; 843 config.snmp_params.snmp_session.peername = argv[optind];
994 844 }
995 if (community == NULL)
996 community = strdup(DEFAULT_COMMUNITY);
997
998 return validate_arguments();
999}
1000
1001/******************************************************************************
1002
1003@@-
1004<sect3>
1005<title>validate_arguments</title>
1006
1007<para>&PROTO_validate_arguments;</para>
1008
1009<para>Checks to see if the default miblist needs to be loaded. Also verifies
1010the authentication and authorization combinations based on protocol version
1011selected.</para>
1012
1013<para></para>
1014
1015</sect3>
1016-@@
1017******************************************************************************/
1018 845
1019static int validate_arguments() { 846 // Build true peername here if necessary
1020 /* check whether to load locally installed MIBS (CPU/disk intensive) */ 847 if (connection_prefix != NULL) {
1021 if (miblist == NULL) { 848 // We got something in the connection prefix
1022 if (needmibs) { 849 if (strcasecmp(connection_prefix, "udp") == 0) {
1023 miblist = strdup(DEFAULT_MIBLIST); 850 // The default, do nothing
851 } else if (strcasecmp(connection_prefix, "tcp") == 0) {
852 // use tcp/ipv4
853 xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s",
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);
1024 } else { 880 } else {
1025 miblist = ""; /* don't read any mib files for numeric oids */ 881 // Don't know that prefix, die here
882 die(STATE_UNKNOWN, "Unknown connection prefix");
1026 } 883 }
1027 } 884 }
1028 885
1029 /* Check server_address is given */ 886 /* Check server_address is given */
1030 if (server_address == NULL) 887 if (config.snmp_params.snmp_session.peername == NULL) {
1031 die(STATE_UNKNOWN, _("No host specified\n")); 888 die(STATE_UNKNOWN, _("No host specified\n"));
889 }
1032 890
1033 /* Check oid is given */ 891 if (port != NULL) {
1034 if (numoids == 0) 892 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s",
1035 die(STATE_UNKNOWN, _("No OIDs specified\n")); 893 config.snmp_params.snmp_session.peername, port);
894 }
1036 895
1037 if (proto == NULL) 896 /* check whether to load locally installed MIBS (CPU/disk intensive) */
1038 xasprintf(&proto, DEFAULT_PROTOCOL); 897 if (miblist == NULL) {
1039 898 if (config.snmp_params.need_mibs) {
1040 if ((strcmp(proto, "1") == 0) || (strcmp(proto, "2c") == 0)) { /* snmpv1 or snmpv2c */ 899 setenv("MIBLS", DEFAULT_MIBLIST, 1);
1041 numauthpriv = 2; 900 } else {
1042 authpriv = calloc(numauthpriv, sizeof(char *)); 901 setenv("MIBLS", "NONE", 1);
1043 authpriv[0] = strdup("-c"); 902 miblist = ""; /* don't read any mib files for numeric oids */
1044 authpriv[1] = strdup(community);
1045 } else if (strcmp(proto, "3") == 0) { /* snmpv3 args */
1046 if (!(context == NULL)) {
1047 numcontext = 2;
1048 contextargs = calloc(numcontext, sizeof(char *));
1049 contextargs[0] = strdup("-n");
1050 contextargs[1] = strdup(context);
1051 } 903 }
904 } else {
905 // Blatantly stolen from snmplib/snmp_parse_args
906 setenv("MIBS", miblist, 1);
907 }
1052 908
1053 if (seclevel == NULL) 909 // Historical default is SNMP v2c
1054 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 }
1055 913
1056 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) {
1057 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); 926 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
927 }
1058 928
1059 if (strcmp(seclevel, "noAuthNoPriv") == 0) { 929 switch (config.snmp_params.snmp_session.securityLevel) {
1060 numauthpriv = 4; 930 case SNMP_SEC_LEVEL_AUTHPRIV: {
1061 authpriv = calloc(numauthpriv, sizeof(char *)); 931 if (authpasswd == NULL) {
1062 authpriv[0] = strdup("-l"); 932 die(STATE_UNKNOWN,
1063 authpriv[1] = strdup("noAuthNoPriv"); 933 "No authentication passphrase was given, but authorization was requested");
1064 authpriv[2] = strdup("-u");
1065 authpriv[3] = strdup(secname);
1066 } else {
1067 if (!((strcmp(seclevel, "authNoPriv") == 0) || (strcmp(seclevel, "authPriv") == 0))) {
1068 usage2(_("Invalid seclevel"), seclevel);
1069 } 934 }
1070 935 // auth and priv
1071 if (authproto == NULL) 936 int priv_key_generated = generate_Ku(
1072 xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); 937 config.snmp_params.snmp_session.securityPrivProto,
1073 938 (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd,
1074 if (authpasswd == NULL) 939 strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey,
1075 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); 940 &config.snmp_params.snmp_session.securityPrivKeyLen);
1076 941
1077 if (strcmp(seclevel, "authNoPriv") == 0) { 942 if (priv_key_generated != SNMPERR_SUCCESS) {
1078 numauthpriv = 8; 943 die(STATE_UNKNOWN, "Failed to generate privacy key");
1079 authpriv = calloc(numauthpriv, sizeof(char *));
1080 authpriv[0] = strdup("-l");
1081 authpriv[1] = strdup("authNoPriv");
1082 authpriv[2] = strdup("-a");
1083 authpriv[3] = strdup(authproto);
1084 authpriv[4] = strdup("-u");
1085 authpriv[5] = strdup(secname);
1086 authpriv[6] = strdup("-A");
1087 authpriv[7] = strdup(authpasswd);
1088 } else if (strcmp(seclevel, "authPriv") == 0) {
1089 if (privproto == NULL)
1090 xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
1091
1092 if (privpasswd == NULL)
1093 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
1094
1095 numauthpriv = 12;
1096 authpriv = calloc(numauthpriv, sizeof(char *));
1097 authpriv[0] = strdup("-l");
1098 authpriv[1] = strdup("authPriv");
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 authpriv[8] = strdup("-x");
1106 authpriv[9] = strdup(privproto);
1107 authpriv[10] = strdup("-X");
1108 authpriv[11] = strdup(privpasswd);
1109 } 944 }
1110 } 945 }
1111 946 // fall through
1112 } else { 947 case SNMP_SEC_LEVEL_AUTHNOPRIV: {
1113 usage2(_("Invalid SNMP version"), proto); 948 if (privpasswd == NULL) {
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 }
1114 } 965 }
1115 966
1116 return OK; 967 process_arguments_wrapper result = {
968 .config = config,
969 .errorcode = OK,
970 };
971 return result;
1117} 972}
1118 973
1119/* trim leading whitespace 974/* trim leading whitespace
1120 if there is a leading quote, make sure it balances */ 975 if there is a leading quote, make sure it balances */
1121 976char *trim_whitespaces_and_check_quoting(char *str) {
1122static char *thisarg(char *str) {
1123 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ 977 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
1124 if (str[0] == '\'') { /* handle SIMPLE quoted strings */ 978 if (str[0] == '\'') { /* handle SIMPLE quoted strings */
1125 if (strlen(str) == 1 || !strstr(str + 1, "'")) 979 if (strlen(str) == 1 || !strstr(str + 1, "'")) {
1126 die(STATE_UNKNOWN, _("Unbalanced quotes\n")); 980 die(STATE_UNKNOWN, _("Unbalanced quotes\n"));
981 }
1127 } 982 }
1128 return str; 983 return str;
1129} 984}
@@ -1132,23 +987,21 @@ static char *thisarg(char *str) {
1132 set the trailing quote to '\x0' 987 set the trailing quote to '\x0'
1133 if the string continues, advance beyond the comma */ 988 if the string continues, advance beyond the comma */
1134 989
1135static char *nextarg(char *str) { 990char *get_next_argument(char *str) {
1136 if (str[0] == '\'') { 991 if (str[0] == '\'') {
1137 str[0] = 0; 992 str[0] = 0;
1138 if (strlen(str) > 1) { 993 if (strlen(str) > 1) {
1139 str = strstr(str + 1, "'"); 994 str = strstr(str + 1, "'");
1140 return (++str); 995 return (++str);
1141 } else {
1142 return NULL;
1143 } 996 }
997 return NULL;
1144 } 998 }
1145 if (str[0] == ',') { 999 if (str[0] == ',') {
1146 str[0] = 0; 1000 str[0] = 0;
1147 if (strlen(str) > 1) { 1001 if (strlen(str) > 1) {
1148 return (++str); 1002 return (++str);
1149 } else {
1150 return NULL;
1151 } 1003 }
1004 return NULL;
1152 } 1005 }
1153 if ((str = strstr(str, ",")) && strlen(str) > 1) { 1006 if ((str = strstr(str, ",")) && strlen(str) > 1) {
1154 str[0] = 0; 1007 str[0] = 0;
@@ -1157,41 +1010,7 @@ static char *nextarg(char *str) {
1157 return NULL; 1010 return NULL;
1158} 1011}
1159 1012
1160/* multiply result (values 0 < n < 1 work as divider) */ 1013void print_help(void) {
1161static char *multiply(char *str) {
1162 if (multiplier == 1)
1163 return (str);
1164
1165 if (verbose > 2)
1166 printf(" multiply input: %s\n", str);
1167
1168 char *endptr;
1169 double val = strtod(str, &endptr);
1170 if ((val == 0.0) && (endptr == str)) {
1171 die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, str);
1172 }
1173
1174 if (verbose > 2)
1175 printf(" multiply extracted double: %f\n", val);
1176
1177 val *= multiplier;
1178 char *conv = "%f";
1179 if (fmtstr_set) {
1180 conv = fmtstr;
1181 }
1182 if (val == (int)val) {
1183 snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val);
1184 } else {
1185 if (verbose > 2)
1186 printf(" multiply using format: %s\n", conv);
1187 snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val);
1188 }
1189 if (verbose > 2)
1190 printf(" multiply result: %s\n", buffer);
1191 return buffer;
1192}
1193
1194static void print_help(void) {
1195 print_revision(progname, NP_VERSION); 1014 print_revision(progname, NP_VERSION);
1196 1015
1197 printf(COPYRIGHT, copyright, email); 1016 printf(COPYRIGHT, copyright, email);
@@ -1204,8 +1023,6 @@ static void print_help(void) {
1204 1023
1205 printf(UT_HELP_VRSN); 1024 printf(UT_HELP_VRSN);
1206 printf(UT_EXTRA_OPTS); 1025 printf(UT_EXTRA_OPTS);
1207 printf(UT_IPv46);
1208
1209 printf(UT_HOST_PORT, 'p', DEFAULT_PORT); 1026 printf(UT_HOST_PORT, 'p', DEFAULT_PORT);
1210 1027
1211 /* SNMP and Authentication Protocol */ 1028 /* SNMP and Authentication Protocol */
@@ -1217,13 +1034,15 @@ static void print_help(void) {
1217 printf(" %s\n", _("SNMPv3 context")); 1034 printf(" %s\n", _("SNMPv3 context"));
1218 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); 1035 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1219 printf(" %s\n", _("SNMPv3 securityLevel")); 1036 printf(" %s\n", _("SNMPv3 securityLevel"));
1220 printf(" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); 1037 printf(" %s\n", "-a, --authproto=[MD5|SHA]");
1221 printf(" %s\n", 1038 printf(" %s\n", _("SNMPv3 auth proto"));
1222 _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); 1039#ifdef HAVE_USM_DES_PRIV_PROTOCOL
1223 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")); 1040 printf(" %s\n", "-x, --privproto=[DES|AES]");
1224 printf(" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); 1041 printf(" %s\n", _("SNMPv3 priv proto (default DES)"));
1225 printf(" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools")); 1042#else
1226 printf(" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); 1043 printf(" %s\n", "-x, --privproto=[AES]");
1044 printf(" %s\n", _("SNMPv3 priv proto (default AES)"));
1045#endif
1227 1046
1228 /* Authentication Tokens*/ 1047 /* Authentication Tokens*/
1229 printf(" %s\n", "-C, --community=STRING"); 1048 printf(" %s\n", "-C, --community=STRING");
@@ -1235,15 +1054,18 @@ static void print_help(void) {
1235 printf(" %s\n", _("SNMPv3 authentication password")); 1054 printf(" %s\n", _("SNMPv3 authentication password"));
1236 printf(" %s\n", "-X, --privpasswd=PASSWORD"); 1055 printf(" %s\n", "-X, --privpasswd=PASSWORD");
1237 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");
1238 1061
1239 /* OID Stuff */ 1062 /* OID Stuff */
1240 printf(" %s\n", "-o, --oid=OID(s)"); 1063 printf(" %s\n", "-o, --oid=OID(s)");
1241 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"));
1242 printf(" %s\n", "-m, --miblist=STRING"); 1065 printf(" %s\n", "-m, --miblist=STRING");
1243 printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); 1066 printf(" %s\n",
1067 _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1244 printf(" %s\n", _("for symbolic OIDs.)")); 1068 printf(" %s\n", _("for symbolic OIDs.)"));
1245 printf(" %s\n", "-d, --delimiter=STRING");
1246 printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1247 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered")); 1069 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1248 printf(" %s\n", _("to be the data that should be used in the evaluation.")); 1070 printf(" %s\n", _("to be the data that should be used in the evaluation."));
1249 printf(" %s\n", "-z, --nulloid=#"); 1071 printf(" %s\n", "-z, --nulloid=#");
@@ -1260,10 +1082,6 @@ static void print_help(void) {
1260 printf(" %s\n", _("Warning threshold range(s)")); 1082 printf(" %s\n", _("Warning threshold range(s)"));
1261 printf(" %s\n", "-c, --critical=THRESHOLD(s)"); 1083 printf(" %s\n", "-c, --critical=THRESHOLD(s)");
1262 printf(" %s\n", _("Critical threshold range(s)")); 1084 printf(" %s\n", _("Critical threshold range(s)"));
1263 printf(" %s\n", "--rate");
1264 printf(" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1265 printf(" %s\n", "--rate-multiplier");
1266 printf(" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1267 printf(" %s\n", "--offset=OFFSET"); 1085 printf(" %s\n", "--offset=OFFSET");
1268 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); 1086 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1269 1087
@@ -1271,9 +1089,11 @@ static void print_help(void) {
1271 printf(" %s\n", "-s, --string=STRING"); 1089 printf(" %s\n", "-s, --string=STRING");
1272 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"));
1273 printf(" %s\n", "-r, --ereg=REGEX"); 1091 printf(" %s\n", "-r, --ereg=REGEX");
1274 printf(" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); 1092 printf(" %s\n",
1093 _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1275 printf(" %s\n", "-R, --eregi=REGEX"); 1094 printf(" %s\n", "-R, --eregi=REGEX");
1276 printf(" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); 1095 printf(" %s\n",
1096 _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1277 printf(" %s\n", "--invert-search"); 1097 printf(" %s\n", "--invert-search");
1278 printf(" %s\n", _("Invert search result (CRITICAL if found)")); 1098 printf(" %s\n", _("Invert search result (CRITICAL if found)"));
1279 1099
@@ -1282,53 +1102,46 @@ static void print_help(void) {
1282 printf(" %s\n", _("Prefix label for output from plugin")); 1102 printf(" %s\n", _("Prefix label for output from plugin"));
1283 printf(" %s\n", "-u, --units=STRING"); 1103 printf(" %s\n", "-u, --units=STRING");
1284 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.')."));
1285 printf(" %s\n", "-D, --output-delimiter=STRING");
1286 printf(" %s\n", _("Separates output on multiple OID requests"));
1287 printf(" %s\n", "-M, --multiplier=FLOAT"); 1105 printf(" %s\n", "-M, --multiplier=FLOAT");
1288 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); 1106 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1289 printf(" %s\n", "-f, --fmtstr=STRING"); 1107 printf(UT_OUTPUT_FORMAT);
1290 printf(" %s\n", _("C-style format string for float values (see option -M)"));
1291 1108
1292 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1109 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1293 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5")); 1110 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: "
1111 "timeout_interval * retries + 5"));
1294 printf(" %s\n", "-e, --retries=INTEGER"); 1112 printf(" %s\n", "-e, --retries=INTEGER");
1295 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES); 1113 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "),
1114 DEFAULT_RETRIES);
1296 1115
1297 printf(" %s\n", "-O, --perf-oids"); 1116 printf(" %s\n", "-O, --perf-oids");
1298 printf(" %s\n", _("Label performance data with OIDs instead of --label's")); 1117 printf(" %s\n", _("Label performance data with OIDs instead of --label's"));
1299 1118
1300 printf(" %s\n", "--ignore-mib-parsing-errors"); 1119 printf(" %s\n", "--ignore-mib-parsing-errors");
1301 printf(" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files")); 1120 printf(" %s\n", _("Do to not print errors encountered when parsing MIB files"));
1302 1121
1303 printf(UT_VERBOSE); 1122 printf(UT_VERBOSE);
1304 1123
1305 printf("\n"); 1124 printf("\n");
1306 printf("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package.")); 1125 printf("%s\n", _("This plugin relies (links against) on the NET-SNMP libraries."));
1307 printf("%s\n", _("if you don't have the package installed, you will need to download it from")); 1126 printf("%s\n",
1127 _("if you don't have the libraries installed, you will need to download them from"));
1308 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin.")); 1128 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1309 1129
1310 printf("\n"); 1130 printf("\n");
1311 printf("%s\n", _("Notes:")); 1131 printf("%s\n", _("Notes:"));
1312 printf(" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); 1132 printf(" %s\n",
1133 _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1313 printf(" %s\n", _("list (lists with internal spaces must be quoted).")); 1134 printf(" %s\n", _("list (lists with internal spaces must be quoted)."));
1314 1135
1315 printf(" -%s", UT_THRESHOLDS_NOTES); 1136 printf(" -%s", UT_THRESHOLDS_NOTES);
1316 1137
1317 printf(" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); 1138 printf(" %s\n",
1139 _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1318 printf(" %s\n", _("- Note that only one string and one regex may be checked at present")); 1140 printf(" %s\n", _("- Note that only one string and one regex may be checked at present"));
1319 printf(" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); 1141 printf(" %s\n",
1142 _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1320 printf(" %s\n", _("returned from the SNMP query is an unsigned integer.")); 1143 printf(" %s\n", _("returned from the SNMP query is an unsigned integer."));
1321 1144
1322 printf("\n");
1323 printf("%s\n", _("Rate Calculation:"));
1324 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1325 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1326 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1327 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1328 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1329 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1330 printf(" %s\n", _("changing the arguments will create a new state file."));
1331
1332 printf(UT_SUPPORT); 1145 printf(UT_SUPPORT);
1333} 1146}
1334 1147
@@ -1339,5 +1152,5 @@ void print_usage(void) {
1339 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); 1152 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1340 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n"); 1153 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n");
1341 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n"); 1154 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1342 printf("[-M multiplier [-f format]]\n"); 1155 printf("[-M multiplier]\n");
1343} 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..ecbfc5dd
--- /dev/null
+++ b/plugins/check_snmp.d/check_snmp_helpers.c
@@ -0,0 +1,934 @@
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 for (char *ptr = strtok(threshold_string, ", "); ptr != NULL;
40 ptr = strtok(NULL, ", "), tu_index++) {
41
42 if (tu_index > max_test_units) {
43 // More thresholds then values, just ignore them
44 return 0;
45 }
46
47 // edge case: maybe we got `,,` to skip a value
48 if (strlen(ptr) == 0) {
49 // no threshold given, do not set it then
50 continue;
51 }
52
53 mp_range_parsed tmp = mp_parse_range_string(ptr);
54 if (tmp.error != MP_PARSING_SUCCES) {
55 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr);
56 }
57
58 if (is_critical) {
59 test_units[tu_index].threshold.critical = tmp.range;
60 test_units[tu_index].threshold.critical_is_set = true;
61 } else {
62 test_units[tu_index].threshold.warning = tmp.range;
63 test_units[tu_index].threshold.warning_is_set = true;
64 }
65 }
66
67 } else {
68 // Single value
69 // only valid for the first test unit
70 mp_range_parsed tmp = mp_parse_range_string(threshold_string);
71 if (tmp.error != MP_PARSING_SUCCES) {
72 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string);
73 }
74
75 if (is_critical) {
76 test_units[0].threshold.critical = tmp.range;
77 test_units[0].threshold.critical_is_set = true;
78 } else {
79 test_units[0].threshold.warning = tmp.range;
80 test_units[0].threshold.warning_is_set = true;
81 }
82 }
83
84 return 0;
85}
86
87const int DEFAULT_PROTOCOL = SNMP_VERSION_1;
88const char DEFAULT_OUTPUT_DELIMITER[] = " ";
89
90const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192;
91
92check_snmp_config check_snmp_config_init() {
93 check_snmp_config tmp = {
94 .snmp_params =
95 {
96 .use_getnext = false,
97
98 .ignore_mib_parsing_errors = false,
99 .need_mibs = false,
100
101 .test_units = NULL,
102 .num_of_test_units = 0,
103 },
104
105 .evaluation_params =
106 {
107 .nulloid_result = STATE_UNKNOWN, // state to return if no result for query
108
109 .invert_search = true,
110 .regex_cmp_value = {},
111 .string_cmp_value = "",
112
113 .multiplier = 1.0,
114 .multiplier_set = false,
115 .offset = 0,
116 .offset_set = false,
117
118 .use_oid_as_perf_data_label = false,
119
120 .calculate_rate = false,
121 .rate_multiplier = 1,
122 },
123 };
124
125 snmp_sess_init(&tmp.snmp_params.snmp_session);
126
127 tmp.snmp_params.snmp_session.retries = DEFAULT_RETRIES;
128 tmp.snmp_params.snmp_session.version = DEFAULT_SNMP_VERSION;
129 tmp.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
130 tmp.snmp_params.snmp_session.community = (unsigned char *)"public";
131 tmp.snmp_params.snmp_session.community_len = strlen("public");
132 return tmp;
133}
134
135snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) {
136 if (parameters.ignore_mib_parsing_errors) {
137 char *opt_toggle_res = snmp_mib_toggle_options("e");
138 if (opt_toggle_res != NULL) {
139 die(STATE_UNKNOWN, "Unable to disable MIB parsing errors");
140 }
141 }
142
143 struct snmp_pdu *pdu = NULL;
144 if (parameters.use_getnext) {
145 pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
146 } else {
147 pdu = snmp_pdu_create(SNMP_MSG_GET);
148 }
149
150 for (size_t i = 0; i < parameters.num_of_test_units; i++) {
151 assert(parameters.test_units[i].oid != NULL);
152 if (verbose > 0) {
153 printf("OID %zu to parse: %s\n", i, parameters.test_units[i].oid);
154 }
155
156 oid tmp_OID[MAX_OID_LEN];
157 size_t tmp_OID_len = MAX_OID_LEN;
158 if (snmp_parse_oid(parameters.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) {
159 // success
160 snmp_add_null_var(pdu, tmp_OID, tmp_OID_len);
161 } else {
162 // failed
163 snmp_perror("Parsing failure");
164 die(STATE_UNKNOWN, "Failed to parse OID\n");
165 }
166 }
167
168 const int timeout_safety_tolerance = 5;
169 alarm((timeout_interval * (unsigned int)parameters.snmp_session.retries) +
170 timeout_safety_tolerance);
171
172 struct snmp_session *active_session = snmp_open(&parameters.snmp_session);
173 if (active_session == NULL) {
174 int pcliberr = 0;
175 int psnmperr = 0;
176 char *pperrstring = NULL;
177 snmp_error(&parameters.snmp_session, &pcliberr, &psnmperr, &pperrstring);
178 die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring);
179 }
180
181 struct snmp_pdu *response = NULL;
182 int snmp_query_status = snmp_synch_response(active_session, pdu, &response);
183
184 if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) {
185 int pcliberr = 0;
186 int psnmperr = 0;
187 char *pperrstring = NULL;
188 snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring);
189
190 if (psnmperr == SNMPERR_TIMEOUT) {
191 // We exit with critical here for some historical reason
192 die(STATE_CRITICAL, "SNMP query ran into a timeout\n");
193 }
194 die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring);
195 }
196
197 snmp_close(active_session);
198
199 /* disable alarm again */
200 alarm(0);
201
202 snmp_responces result = {
203 .errorcode = OK,
204 .response_values = calloc(parameters.num_of_test_units, sizeof(response_value)),
205 };
206
207 if (result.response_values == NULL) {
208 result.errorcode = ERROR;
209 return result;
210 }
211
212 // We got the the query results, now process them
213 size_t loop_index = 0;
214 for (netsnmp_variable_list *vars = response->variables; vars;
215 vars = vars->next_variable, loop_index++) {
216
217 for (size_t jdx = 0; jdx < vars->name_length; jdx++) {
218 result.response_values[loop_index].oid[jdx] = vars->name[jdx];
219 }
220 result.response_values[loop_index].oid_length = vars->name_length;
221
222 switch (vars->type) {
223 case ASN_OCTET_STR: {
224 result.response_values[loop_index].string_response = strdup((char *)vars->val.string);
225 result.response_values[loop_index].type = vars->type;
226 if (verbose) {
227 printf("Debug: Got a string as response: %s\n", vars->val.string);
228 }
229 }
230 continue;
231 case ASN_OPAQUE:
232 if (verbose) {
233 printf("Debug: Got OPAQUE\n");
234 }
235 break;
236 /* Numerical values */
237 case ASN_COUNTER64: {
238 if (verbose) {
239 printf("Debug: Got counter64\n");
240 }
241 struct counter64 tmp = *(vars->val.counter64);
242 uint64_t counter = (tmp.high << 32) + tmp.low;
243 result.response_values[loop_index].value.uIntVal = counter;
244 result.response_values[loop_index].type = vars->type;
245 } break;
246 case ASN_GAUGE: // same as ASN_UNSIGNED
247 case ASN_TIMETICKS:
248 case ASN_COUNTER:
249 case ASN_UINTEGER: {
250 if (verbose) {
251 printf("Debug: Got a Integer like\n");
252 }
253 result.response_values[loop_index].value.uIntVal = (unsigned long)*(vars->val.integer);
254 result.response_values[loop_index].type = vars->type;
255 } break;
256 case ASN_INTEGER: {
257 if (verbose) {
258 printf("Debug: Got a Integer\n");
259 }
260 result.response_values[loop_index].value.intVal = *(vars->val.integer);
261 result.response_values[loop_index].type = vars->type;
262 } break;
263 case ASN_FLOAT: {
264 if (verbose) {
265 printf("Debug: Got a float\n");
266 }
267 result.response_values[loop_index].value.doubleVal = *(vars->val.floatVal);
268 result.response_values[loop_index].type = vars->type;
269 } break;
270 case ASN_DOUBLE: {
271 if (verbose) {
272 printf("Debug: Got a double\n");
273 }
274 result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal);
275 result.response_values[loop_index].type = vars->type;
276 } break;
277 case ASN_IPADDRESS:
278 if (verbose) {
279 printf("Debug: Got an IP address\n");
280 }
281 result.response_values[loop_index].type = vars->type;
282
283 // TODO: print address here, state always ok? or regex match?
284 break;
285 default:
286 if (verbose) {
287 printf("Debug: Got a unmatched result type: %hhu\n", vars->type);
288 }
289 // TODO: Error here?
290 break;
291 }
292 }
293
294 return result;
295}
296
297check_snmp_evaluation evaluate_single_unit(response_value response,
298 check_snmp_evaluation_parameters eval_params,
299 check_snmp_test_unit test_unit, time_t query_timestamp,
300 check_snmp_state_entry prev_state,
301 bool have_previous_state) {
302 mp_subcheck sc_oid_test = mp_subcheck_init();
303
304 if ((test_unit.label != NULL) && (strcmp(test_unit.label, "") != 0)) {
305 xasprintf(&sc_oid_test.output, "%s - ", test_unit.label);
306 } else {
307 sc_oid_test.output = strdup("");
308 }
309
310 char oid_string[(MAX_OID_LEN * 2) + 1] = {};
311
312 int oid_string_result =
313 snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, response.oid, response.oid_length);
314 if (oid_string_result <= 0) {
315 // TODO error here
316 die(STATE_UNKNOWN, "snprint_objid failed\n");
317 }
318
319 xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string);
320 sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK);
321
322 if (verbose > 2) {
323 printf("Processing oid %s\n", oid_string);
324 }
325
326 bool got_a_numerical_value = false;
327 mp_perfdata_value pd_result_val = {0};
328
329 check_snmp_state_entry result_state = {
330 .timestamp = query_timestamp,
331 .oid_length = response.oid_length,
332 .type = response.type,
333 };
334
335 for (size_t i = 0; i < response.oid_length; i++) {
336 result_state.oid[i] = response.oid[i];
337 }
338
339 if (have_previous_state) {
340 if (query_timestamp == prev_state.timestamp) {
341 // somehow we have the same timestamp again, that can't be good
342 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_UNKNOWN);
343 xasprintf(&sc_oid_test.output, "Time duration between plugin calls is invalid");
344
345 check_snmp_evaluation result = {
346 .sc = sc_oid_test,
347 .state = result_state,
348 };
349
350 return result;
351 }
352 }
353 // compute rate time difference
354 double timeDiff = 0;
355 if (have_previous_state) {
356 if (verbose) {
357 printf("Previous timestamp: %s", ctime(&prev_state.timestamp));
358 printf("Current timestamp: %s", ctime(&query_timestamp));
359 }
360 timeDiff = difftime(query_timestamp, prev_state.timestamp) / eval_params.rate_multiplier;
361 }
362
363 mp_perfdata pd_num_val = {};
364
365 switch (response.type) {
366 case ASN_OCTET_STR: {
367 char *tmp = response.string_response;
368 if (strchr(tmp, '"') != NULL) {
369 // got double quote in the string
370 if (strchr(tmp, '\'') != NULL) {
371 // got single quote in the string too
372 // dont quote that at all to avoid even more confusion
373 xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp);
374 } else {
375 // quote with single quotes
376 xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp);
377 }
378 } else {
379 // quote with double quotes
380 xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp);
381 }
382
383 if (strlen(tmp) == 0) {
384 sc_oid_test = mp_set_subcheck_state(sc_oid_test, eval_params.nulloid_result);
385 }
386
387 // String matching test
388 if ((test_unit.eval_mthd.crit_string)) {
389 if (strcmp(tmp, eval_params.string_cmp_value)) {
390 sc_oid_test = mp_set_subcheck_state(
391 sc_oid_test, (eval_params.invert_search) ? STATE_CRITICAL : STATE_OK);
392 } else {
393 sc_oid_test = mp_set_subcheck_state(
394 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
395 }
396 } else if (test_unit.eval_mthd.crit_regex) {
397 const size_t nmatch = eval_params.regex_cmp_value.re_nsub + 1;
398 regmatch_t pmatch[nmatch];
399 memset(pmatch, '\0', sizeof(regmatch_t) * nmatch);
400
401 int excode = regexec(&eval_params.regex_cmp_value, tmp, nmatch, pmatch, 0);
402 if (excode == 0) {
403 sc_oid_test = mp_set_subcheck_state(
404 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
405 } else if (excode != REG_NOMATCH) {
406 char errbuf[MAX_INPUT_BUFFER] = "";
407 regerror(excode, &eval_params.regex_cmp_value, errbuf, MAX_INPUT_BUFFER);
408 printf(_("Execute Error: %s\n"), errbuf);
409 exit(STATE_CRITICAL);
410 } else { // REG_NOMATCH
411 sc_oid_test = mp_set_subcheck_state(
412 sc_oid_test, eval_params.invert_search ? STATE_CRITICAL : STATE_OK);
413 }
414 }
415 } break;
416 case ASN_COUNTER64:
417 got_a_numerical_value = true;
418
419 result_state.value.uIntVal = response.value.uIntVal;
420 result_state.type = response.type;
421
422 // TODO: perfdata unit counter
423 if (eval_params.calculate_rate && have_previous_state) {
424 if (prev_state.value.uIntVal > response.value.uIntVal) {
425 // overflow
426 unsigned long long tmp =
427 (UINT64_MAX - prev_state.value.uIntVal) + response.value.uIntVal;
428
429 tmp /= timeDiff;
430 pd_result_val = mp_create_pd_value(tmp);
431 } else {
432 pd_result_val = mp_create_pd_value(
433 (response.value.uIntVal - prev_state.value.uIntVal) / timeDiff);
434 }
435 } else {
436 // It's only a counter if we cont compute rate
437 pd_num_val.uom = "c";
438 pd_result_val = mp_create_pd_value(response.value.uIntVal);
439 }
440 break;
441 case ASN_GAUGE: // same as ASN_UNSIGNED
442 case ASN_TIMETICKS:
443 case ASN_COUNTER:
444 case ASN_UINTEGER: {
445 got_a_numerical_value = true;
446 long long treated_value = (long long)response.value.uIntVal;
447
448 if (eval_params.multiplier_set || eval_params.offset_set) {
449 double processed = 0;
450 if (eval_params.offset_set) {
451 processed += eval_params.offset;
452 }
453
454 if (eval_params.multiplier_set) {
455 processed = processed * eval_params.multiplier;
456 }
457
458 treated_value = lround(processed);
459 }
460
461 result_state.value.intVal = treated_value;
462
463 if (eval_params.calculate_rate && have_previous_state) {
464 if (verbose > 2) {
465 printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__,
466 prev_state.value.intVal);
467 printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__,
468 treated_value);
469 }
470 double rate = (treated_value - prev_state.value.intVal) / timeDiff;
471 pd_result_val = mp_create_pd_value(rate);
472 } else {
473 pd_result_val = mp_create_pd_value(treated_value);
474
475 if (response.type == ASN_COUNTER) {
476 pd_num_val.uom = "c";
477 }
478 }
479
480 } break;
481 case ASN_INTEGER: {
482 if (eval_params.multiplier_set || eval_params.offset_set) {
483 double processed = 0;
484 if (eval_params.multiplier_set) {
485 processed = (double)response.value.intVal * eval_params.multiplier;
486 }
487
488 if (eval_params.offset_set) {
489 processed += eval_params.offset;
490 }
491
492 result_state.value.doubleVal = processed;
493
494 if (eval_params.calculate_rate && have_previous_state) {
495 pd_result_val =
496 mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff);
497 } else {
498 pd_result_val = mp_create_pd_value(processed);
499 }
500 } else {
501 result_state.value.intVal = response.value.intVal;
502
503 if (eval_params.calculate_rate && have_previous_state) {
504 pd_result_val = mp_create_pd_value(
505 (response.value.intVal - prev_state.value.intVal) / timeDiff);
506 } else {
507 pd_result_val = mp_create_pd_value(response.value.intVal);
508 }
509 }
510
511 got_a_numerical_value = true;
512 } break;
513 case ASN_FLOAT: // fallthrough
514 case ASN_DOUBLE: {
515 got_a_numerical_value = true;
516 double tmp = response.value.doubleVal;
517 if (eval_params.offset_set) {
518 tmp += eval_params.offset;
519 }
520
521 if (eval_params.multiplier_set) {
522 tmp *= eval_params.multiplier;
523 }
524
525 if (eval_params.calculate_rate && have_previous_state) {
526 pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff);
527 } else {
528 pd_result_val = mp_create_pd_value(tmp);
529 }
530 got_a_numerical_value = true;
531
532 result_state.value.doubleVal = tmp;
533 } break;
534 case ASN_IPADDRESS:
535 // TODO
536 break;
537 }
538
539 if (got_a_numerical_value) {
540 if (eval_params.use_oid_as_perf_data_label) {
541 // Use oid for perdata label
542 pd_num_val.label = strdup(oid_string);
543 // TODO strdup error checking
544 } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) {
545 pd_num_val.label = strdup(test_unit.label);
546 } else {
547 pd_num_val.label = strdup(test_unit.oid);
548 }
549
550 if (!(eval_params.calculate_rate && !have_previous_state)) {
551 // some kind of numerical value
552 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
553 pd_num_val.uom = test_unit.unit_value;
554 }
555
556 pd_num_val.value = pd_result_val;
557
558 xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output,
559 pd_value_to_string(pd_result_val));
560
561 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
562 xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value);
563 }
564
565 if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) {
566 pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold);
567 mp_state_enum tmp_state = mp_get_pd_status(pd_num_val);
568
569 if (tmp_state == STATE_WARNING) {
570 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING);
571 xasprintf(&sc_oid_test.output, "%s - number violates warning threshold",
572 sc_oid_test.output);
573 } else if (tmp_state == STATE_CRITICAL) {
574 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL);
575 xasprintf(&sc_oid_test.output, "%s - number violates critical threshold",
576 sc_oid_test.output);
577 }
578 }
579
580 mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val);
581 } else {
582 // should calculate rate, but there is no previous state, so first run
583 // exit with ok now
584 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK);
585 xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay",
586 sc_oid_test.output);
587 }
588 }
589
590 check_snmp_evaluation result = {
591 .sc = sc_oid_test,
592 .state = result_state,
593 };
594
595 return result;
596}
597
598char *_np_state_generate_key(int argc, char **argv);
599
600/*
601 * If time=NULL, use current time. Create state file, with state format
602 * version, default text. Writes version, time, and data. Avoid locking
603 * problems - use mv to write and then swap. Possible loss of state data if
604 * two things writing to same key at same time.
605 * Will die with UNKNOWN if errors
606 */
607void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) {
608 time_t current_time;
609 if (timestamp == 0) {
610 time(&current_time);
611 } else {
612 current_time = timestamp;
613 }
614
615 int result = 0;
616
617 /* If file doesn't currently exist, create directories */
618 if (access(stateKey._filename, F_OK) != 0) {
619 char *directories = NULL;
620 result = asprintf(&directories, "%s", stateKey._filename);
621 if (result < 0) {
622 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
623 }
624
625 for (char *p = directories + 1; *p; p++) {
626 if (*p == '/') {
627 *p = '\0';
628 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
629 /* Can't free this! Otherwise error message is wrong! */
630 /* np_free(directories); */
631 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
632 }
633 *p = '/';
634 }
635 }
636
637 if (directories) {
638 free(directories);
639 }
640 }
641
642 char *temp_file = NULL;
643 result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename);
644 if (result < 0) {
645 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
646 }
647
648 int temp_file_desc = 0;
649 if ((temp_file_desc = mkstemp(temp_file)) == -1) {
650 if (temp_file) {
651 free(temp_file);
652 }
653 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
654 }
655
656 FILE *temp_file_pointer = fdopen(temp_file_desc, "w");
657 if (temp_file_pointer == NULL) {
658 close(temp_file_desc);
659 unlink(temp_file);
660 if (temp_file) {
661 free(temp_file);
662 }
663 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
664 }
665
666 fprintf(temp_file_pointer, "# NP State file\n");
667 fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION);
668 fprintf(temp_file_pointer, "%d\n", stateKey.data_version);
669 fprintf(temp_file_pointer, "%lu\n", current_time);
670 fprintf(temp_file_pointer, "%s\n", stringToStore);
671
672 fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP);
673
674 fflush(temp_file_pointer);
675
676 result = fclose(temp_file_pointer);
677
678 fsync(temp_file_desc);
679
680 if (result != 0) {
681 unlink(temp_file);
682 if (temp_file) {
683 free(temp_file);
684 }
685 die(STATE_UNKNOWN, _("Error writing temp file"));
686 }
687
688 if (rename(temp_file, stateKey._filename) != 0) {
689 unlink(temp_file);
690 if (temp_file) {
691 free(temp_file);
692 }
693 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
694 }
695
696 if (temp_file) {
697 free(temp_file);
698 }
699}
700
701/*
702 * Read the state file
703 */
704bool _np_state_read_file(FILE *state_file, state_key stateKey) {
705 time_t current_time;
706 time(&current_time);
707
708 /* Note: This introduces a limit of 8192 bytes in the string data */
709 char *line = (char *)calloc(1, 8192);
710 if (line == NULL) {
711 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
712 }
713
714 bool status = false;
715 enum {
716 STATE_FILE_VERSION,
717 STATE_DATA_VERSION,
718 STATE_DATA_TIME,
719 STATE_DATA_TEXT,
720 STATE_DATA_END
721 } expected = STATE_FILE_VERSION;
722
723 int failure = 0;
724 while (!failure && (fgets(line, 8192, state_file)) != NULL) {
725 size_t pos = strlen(line);
726 if (line[pos - 1] == '\n') {
727 line[pos - 1] = '\0';
728 }
729
730 if (line[0] == '#') {
731 continue;
732 }
733
734 switch (expected) {
735 case STATE_FILE_VERSION: {
736 int i = atoi(line);
737 if (i != NP_STATE_FORMAT_VERSION) {
738 failure++;
739 } else {
740 expected = STATE_DATA_VERSION;
741 }
742 } break;
743 case STATE_DATA_VERSION: {
744 int i = atoi(line);
745 if (i != stateKey.data_version) {
746 failure++;
747 } else {
748 expected = STATE_DATA_TIME;
749 }
750 } break;
751 case STATE_DATA_TIME: {
752 /* If time > now, error */
753 time_t data_time = strtoul(line, NULL, 10);
754 if (data_time > current_time) {
755 failure++;
756 } else {
757 stateKey.state_data->time = data_time;
758 expected = STATE_DATA_TEXT;
759 }
760 } break;
761 case STATE_DATA_TEXT:
762 stateKey.state_data->data = strdup(line);
763 if (stateKey.state_data->data == NULL) {
764 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
765 }
766 stateKey.state_data->length = strlen(line);
767 expected = STATE_DATA_END;
768 status = true;
769 break;
770 case STATE_DATA_END:;
771 }
772 }
773
774 if (line) {
775 free(line);
776 }
777 return status;
778}
779/*
780 * Will return NULL if no data is available (first run). If key currently
781 * exists, read data. If state file format version is not expected, return
782 * as if no data. Get state data version number and compares to expected.
783 * If numerically lower, then return as no previous state. die with UNKNOWN
784 * if exceptional error.
785 */
786state_data *np_state_read(state_key stateKey) {
787 /* Open file. If this fails, no previous state found */
788 FILE *statefile = fopen(stateKey._filename, "r");
789 state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data));
790 if (statefile != NULL) {
791
792 if (this_state_data == NULL) {
793 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
794 }
795
796 this_state_data->data = NULL;
797 stateKey.state_data = this_state_data;
798
799 if (_np_state_read_file(statefile, stateKey)) {
800 this_state_data->errorcode = OK;
801 } else {
802 this_state_data->errorcode = ERROR;
803 }
804
805 fclose(statefile);
806 } else {
807 // Failed to open state file
808 this_state_data->errorcode = ERROR;
809 }
810
811 return stateKey.state_data;
812}
813
814/*
815 * Internal function. Returns either:
816 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
817 * statically compiled shared state directory
818 */
819char *_np_state_calculate_location_prefix(void) {
820 char *env_dir;
821
822 /* Do not allow passing MP_STATE_PATH in setuid plugins
823 * for security reasons */
824 if (!mp_suid()) {
825 env_dir = getenv("MP_STATE_PATH");
826 if (env_dir && env_dir[0] != '\0') {
827 return env_dir;
828 }
829 /* This is the former ENV, for backward-compatibility */
830 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
831 if (env_dir && env_dir[0] != '\0') {
832 return env_dir;
833 }
834 }
835
836 return NP_STATE_DIR_PREFIX;
837}
838
839/*
840 * Initiatializer for state routines.
841 * Sets variables. Generates filename. Returns np_state_key. die with
842 * UNKNOWN if exception
843 */
844state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc,
845 char **argv) {
846 state_key *this_state = (state_key *)calloc(1, sizeof(state_key));
847 if (this_state == NULL) {
848 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
849 }
850
851 char *temp_keyname = NULL;
852 if (keyname == NULL) {
853 temp_keyname = _np_state_generate_key(argc, argv);
854 } else {
855 temp_keyname = strdup(keyname);
856 if (temp_keyname == NULL) {
857 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
858 }
859 }
860
861 /* Die if invalid characters used for keyname */
862 char *tmp_char = temp_keyname;
863 while (*tmp_char != '\0') {
864 if (!(isalnum(*tmp_char) || *tmp_char == '_')) {
865 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
866 }
867 tmp_char++;
868 }
869 this_state->name = temp_keyname;
870 this_state->plugin_name = plugin_name;
871 this_state->data_version = expected_data_version;
872 this_state->state_data = NULL;
873
874 /* Calculate filename */
875 char *temp_filename = NULL;
876 int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(),
877 (unsigned long)geteuid(), plugin_name, this_state->name);
878 if (error < 0) {
879 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
880 }
881
882 this_state->_filename = temp_filename;
883
884 return *this_state;
885}
886
887/*
888 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
889 * hopefully a unique key per service/plugin invocation. Use the extra-opts
890 * parse of argv, so that uniqueness in parameters are reflected there.
891 */
892char *_np_state_generate_key(int argc, char **argv) {
893 unsigned char result[256];
894
895#ifdef USE_OPENSSL
896 /*
897 * This code path is chosen if openssl is available (which should be the most common
898 * scenario). Alternatively, the gnulib implementation/
899 *
900 */
901 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
902
903 EVP_DigestInit(ctx, EVP_sha256());
904
905 for (int i = 0; i < argc; i++) {
906 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
907 }
908
909 EVP_DigestFinal(ctx, result, NULL);
910#else
911
912 struct sha256_ctx ctx;
913
914 for (int i = 0; i < this_monitoring_plugin->argc; i++) {
915 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
916 }
917
918 sha256_finish_ctx(&ctx, result);
919#endif // FOUNDOPENSSL
920
921 char keyname[41];
922 for (int i = 0; i < 20; ++i) {
923 sprintf(&keyname[2 * i], "%02x", result[i]);
924 }
925
926 keyname[40] = '\0';
927
928 char *keyname_copy = strdup(keyname);
929 if (keyname_copy == NULL) {
930 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
931 }
932
933 return keyname_copy;
934}
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..0f7780b1
--- /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, char *plugin_name, int argc,
70 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 9d0d7cde..f6c8d551 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -57,7 +57,8 @@ static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*
57static void print_help(void); 57static void print_help(void);
58void print_usage(void); 58void print_usage(void);
59 59
60static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version, char *remote_protocol); 60static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version,
61 char *remote_protocol);
61 62
62int main(int argc, char **argv) { 63int main(int argc, char **argv) {
63 setlocale(LC_ALL, ""); 64 setlocale(LC_ALL, "");
@@ -85,7 +86,8 @@ int main(int argc, char **argv) {
85 alarm(socket_timeout); 86 alarm(socket_timeout);
86 87
87 /* ssh_connect exits if error is found */ 88 /* ssh_connect exits if error is found */
88 ssh_connect(&overall, config.server_name, config.port, config.remote_version, config.remote_protocol); 89 ssh_connect(&overall, config.server_name, config.port, config.remote_version,
90 config.remote_protocol);
89 91
90 alarm(0); 92 alarm(0);
91 93
@@ -96,19 +98,20 @@ int main(int argc, char **argv) {
96 98
97/* process command-line arguments */ 99/* process command-line arguments */
98process_arguments_wrapper process_arguments(int argc, char **argv) { 100process_arguments_wrapper process_arguments(int argc, char **argv) {
99 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 101 static struct option longopts[] = {
100 {"version", no_argument, 0, 'V'}, 102 {"help", no_argument, 0, 'h'},
101 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 103 {"version", no_argument, 0, 'V'},
102 {"hostname", required_argument, 0, 'H'}, 104 {"host", required_argument, 0, 'H'}, /* backward compatibility */
103 {"port", required_argument, 0, 'p'}, 105 {"hostname", required_argument, 0, 'H'},
104 {"use-ipv4", no_argument, 0, '4'}, 106 {"port", required_argument, 0, 'p'},
105 {"use-ipv6", no_argument, 0, '6'}, 107 {"use-ipv4", no_argument, 0, '4'},
106 {"timeout", required_argument, 0, 't'}, 108 {"use-ipv6", no_argument, 0, '6'},
107 {"verbose", no_argument, 0, 'v'}, 109 {"timeout", required_argument, 0, 't'},
108 {"remote-version", required_argument, 0, 'r'}, 110 {"verbose", no_argument, 0, 'v'},
109 {"remote-protocol", required_argument, 0, 'P'}, 111 {"remote-version", required_argument, 0, 'r'},
110 {"output-format", required_argument, 0, output_format_index}, 112 {"remote-protocol", required_argument, 0, 'P'},
111 {0, 0, 0, 0}}; 113 {"output-format", required_argument, 0, output_format_index},
114 {0, 0, 0, 0}};
112 115
113 process_arguments_wrapper result = { 116 process_arguments_wrapper result = {
114 .config = check_ssh_config_init(), 117 .config = check_ssh_config_init(),
@@ -228,7 +231,8 @@ process_arguments_wrapper process_arguments(int argc, char **argv) {
228 * 231 *
229 *-----------------------------------------------------------------------*/ 232 *-----------------------------------------------------------------------*/
230 233
231int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version, char *desired_remote_protocol) { 234int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version,
235 char *desired_remote_protocol) {
232 struct timeval tv; 236 struct timeval tv;
233 gettimeofday(&tv, NULL); 237 gettimeofday(&tv, NULL);
234 238
@@ -238,32 +242,34 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
238 mp_subcheck connection_sc = mp_subcheck_init(); 242 mp_subcheck connection_sc = mp_subcheck_init();
239 if (result != STATE_OK) { 243 if (result != STATE_OK) {
240 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); 244 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
241 xasprintf(&connection_sc.output, "Failed to establish TCP connection to Host %s and Port %d", haddr, hport); 245 xasprintf(&connection_sc.output,
246 "Failed to establish TCP connection to Host %s and Port %d", haddr, hport);
242 mp_add_subcheck_to_check(overall, connection_sc); 247 mp_add_subcheck_to_check(overall, connection_sc);
243 return result; 248 return result;
244 } 249 }
245 250
246 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char)); 251 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char));
247 char *buffer = NULL; 252 char *buffer = NULL;
248 size_t recv_ret = 0; 253 ssize_t recv_ret = 0;
249 char *version_control_string = NULL; 254 char *version_control_string = NULL;
250 size_t byte_offset = 0; 255 size_t byte_offset = 0;
251 while ((version_control_string == NULL) && 256 while ((version_control_string == NULL) &&
252 (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset), 0) > 0)) { 257 (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset),
258 0) > 0)) {
253 259
254 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/ 260 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/
255 byte_offset = 0; 261 byte_offset = 0;
256 262
257 char *index = NULL; 263 char *index = NULL;
258 unsigned long len = 0;
259 while ((index = strchr(output + byte_offset, '\n')) != NULL) { 264 while ((index = strchr(output + byte_offset, '\n')) != NULL) {
260 /*Partition the buffer so that this line is a separate string, 265 /*Partition the buffer so that this line is a separate string,
261 * by replacing the newline with NUL*/ 266 * by replacing the newline with NUL*/
262 output[(index - output)] = '\0'; 267 output[(index - output)] = '\0';
263 len = strlen(output + byte_offset); 268 size_t len = strlen(output + byte_offset);
264 269
265 if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { 270 if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) {
266 /*if the string starts with SSH-, this _should_ be a valid version control string*/ 271 /*if the string starts with SSH-, this _should_ be a valid version control
272 * string*/
267 version_control_string = output + byte_offset; 273 version_control_string = output + byte_offset;
268 break; 274 break;
269 } 275 }
@@ -273,21 +279,23 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
273 } 279 }
274 280
275 if (version_control_string == NULL) { 281 if (version_control_string == NULL) {
276 /* move unconsumed data to beginning of buffer, null rest */ 282 /* move unconsumed data to beginning of buffer */
277 memmove((void *)output, (void *)(output + byte_offset + 1), BUFF_SZ - len + 1); 283 memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset);
278 memset(output + byte_offset + 1, 0, BUFF_SZ - byte_offset + 1);
279 284
280 /*start reading from end of current line chunk on next recv*/ 285 /*start reading from end of current line chunk on next recv*/
281 byte_offset = strlen(output); 286 byte_offset = strlen(output);
287
288 /* NUL the rest of the buffer */
289 memset(output + byte_offset, 0, BUFF_SZ - byte_offset);
282 } 290 }
283 } else { 291 } else {
284 byte_offset += recv_ret; 292 byte_offset += (size_t)recv_ret;
285 } 293 }
286 } 294 }
287 295
288 if (recv_ret < 0) { 296 if (recv_ret < 0) {
289 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); 297 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
290 xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - %s", strerror(errno)); 298 xasprintf(&connection_sc.output, "%s - %s", "SSH CRITICAL - ", strerror(errno));
291 mp_add_subcheck_to_check(overall, connection_sc); 299 mp_add_subcheck_to_check(overall, connection_sc);
292 return OK; 300 return OK;
293 } 301 }
@@ -333,7 +341,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
333 * "1.x" (e.g., "1.5" or "1.3")." 341 * "1.x" (e.g., "1.5" or "1.3")."
334 * - RFC 4253:5 342 * - RFC 4253:5
335 */ 343 */
336 char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ 344 char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") +
345 1; /* (+1 for the '-' separating protoversion from softwareversion) */
337 346
338 /* If there's a space in the version string, whatever's after the space is a comment 347 /* If there's a space in the version string, whatever's after the space is a comment
339 * (which is NOT part of the server name/version)*/ 348 * (which is NOT part of the server name/version)*/
@@ -345,13 +354,15 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
345 mp_subcheck protocol_validity_sc = mp_subcheck_init(); 354 mp_subcheck protocol_validity_sc = mp_subcheck_init();
346 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { 355 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) {
347 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL); 356 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL);
348 xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s", version_control_string); 357 xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s",
358 version_control_string);
349 mp_add_subcheck_to_check(overall, protocol_validity_sc); 359 mp_add_subcheck_to_check(overall, protocol_validity_sc);
350 return OK; 360 return OK;
351 } 361 }
352 362
353 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK); 363 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK);
354 xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s", version_control_string); 364 xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s",
365 version_control_string);
355 mp_add_subcheck_to_check(overall, protocol_validity_sc); 366 mp_add_subcheck_to_check(overall, protocol_validity_sc);
356 367
357 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0; 368 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0;
@@ -366,8 +377,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
366 if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) { 377 if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) {
367 mp_subcheck remote_version_sc = mp_subcheck_init(); 378 mp_subcheck remote_version_sc = mp_subcheck_init();
368 remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL); 379 remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL);
369 xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"), ssh_server, ssh_proto, 380 xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"),
370 desired_remote_version); 381 ssh_server, ssh_proto, desired_remote_version);
371 close(socket); 382 close(socket);
372 mp_add_subcheck_to_check(overall, remote_version_sc); 383 mp_add_subcheck_to_check(overall, remote_version_sc);
373 return OK; 384 return OK;
@@ -385,11 +396,13 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
385 396
386 if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) { 397 if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) {
387 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL); 398 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL);
388 xasprintf(&protocol_version_sc.output, _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server, ssh_proto, 399 xasprintf(&protocol_version_sc.output,
389 desired_remote_protocol); 400 _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server,
401 ssh_proto, desired_remote_protocol);
390 } else { 402 } else {
391 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK); 403 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK);
392 xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)", ssh_server, ssh_proto); 404 xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)",
405 ssh_server, ssh_proto);
393 } 406 }
394 407
395 mp_add_subcheck_to_check(overall, protocol_version_sc); 408 mp_add_subcheck_to_check(overall, protocol_version_sc);
@@ -422,7 +435,8 @@ void print_help(void) {
422 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 435 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
423 436
424 printf(" %s\n", "-r, --remote-version=STRING"); 437 printf(" %s\n", "-r, --remote-version=STRING");
425 printf(" %s\n", _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); 438 printf(" %s\n",
439 _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)"));
426 440
427 printf(" %s\n", "-P, --remote-protocol=STRING"); 441 printf(" %s\n", "-P, --remote-protocol=STRING");
428 printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); 442 printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)"));
@@ -435,5 +449,6 @@ void print_help(void) {
435 449
436void print_usage(void) { 450void print_usage(void) {
437 printf("%s\n", _("Usage:")); 451 printf("%s\n", _("Usage:"));
438 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n", progname); 452 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n",
453 progname);
439} 454}
diff --git a/plugins/check_swap.c b/plugins/check_swap.c
index cb95949a..dbf53a00 100644
--- a/plugins/check_swap.c
+++ b/plugins/check_swap.c
@@ -90,6 +90,14 @@ int main(int argc, char **argv) {
90 exit(STATE_UNKNOWN); 90 exit(STATE_UNKNOWN);
91 } 91 }
92 92
93 if (verbose) {
94 printf("Swap retrieval result:\n"
95 "\tFree: %llu\n"
96 "\tUsed: %llu\n"
97 "\tTotal: %llu\n",
98 data.metrics.free, data.metrics.used, data.metrics.total);
99 }
100
93 double percent_used; 101 double percent_used;
94 mp_check overall = mp_check_init(); 102 mp_check overall = mp_check_init();
95 if (config.output_format_is_set) { 103 if (config.output_format_is_set) {
@@ -164,7 +172,8 @@ int main(int argc, char **argv) {
164 } 172 }
165 173
166 if (config.warn_is_set) { 174 if (config.warn_is_set) {
167 if ((config.warn.is_percentage && (percent_used >= (100 - (double)config.warn.value))) || config.warn.value >= data.metrics.free) { 175 if ((config.warn.is_percentage && (percent_used >= (100 - (double)config.warn.value))) ||
176 config.warn.value >= data.metrics.free) {
168 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING); 177 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
169 } 178 }
170 } 179 }
@@ -174,13 +183,14 @@ int main(int argc, char **argv) {
174 } 183 }
175 184
176 if (config.crit_is_set) { 185 if (config.crit_is_set) {
177 if ((config.crit.is_percentage && (percent_used >= (100 - (double)config.crit.value))) || config.crit.value >= data.metrics.free) { 186 if ((config.crit.is_percentage && (percent_used >= (100 - (double)config.crit.value))) ||
187 config.crit.value >= data.metrics.free) {
178 sc1 = mp_set_subcheck_state(sc1, STATE_CRITICAL); 188 sc1 = mp_set_subcheck_state(sc1, STATE_CRITICAL);
179 } 189 }
180 } 190 }
181 191
182 xasprintf(&sc1.output, _("%g%% free (%lluMiB out of %lluMiB)"), (100 - percent_used), data.metrics.free >> 20, 192 xasprintf(&sc1.output, _("%g%% free (%lluMiB out of %lluMiB)"), (100 - percent_used),
183 data.metrics.total >> 20); 193 data.metrics.free >> 20, data.metrics.total >> 20);
184 194
185 overall.summary = "Swap"; 195 overall.summary = "Swap";
186 mp_add_subcheck_to_check(&overall, sc1); 196 mp_add_subcheck_to_check(&overall, sc1);
@@ -193,7 +203,9 @@ int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
193 return config.no_swap_state; 203 return config.no_swap_state;
194 } 204 }
195 205
196 uint64_t free_swap = (uint64_t)(free_swap_mb * (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */ 206 uint64_t free_swap =
207 (uint64_t)(free_swap_mb *
208 (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */
197 209
198 if (!config.crit.is_percentage && config.crit.value >= free_swap) { 210 if (!config.crit.is_percentage && config.crit.value >= free_swap) {
199 return STATE_CRITICAL; 211 return STATE_CRITICAL;
@@ -202,13 +214,16 @@ int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
202 return STATE_WARNING; 214 return STATE_WARNING;
203 } 215 }
204 216
205 uint64_t usage_percentage = (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT; 217 uint64_t usage_percentage =
218 (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT;
206 219
207 if (config.crit.is_percentage && config.crit.value != 0 && usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) { 220 if (config.crit.is_percentage && config.crit.value != 0 &&
221 usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) {
208 return STATE_CRITICAL; 222 return STATE_CRITICAL;
209 } 223 }
210 224
211 if (config.warn.is_percentage && config.warn.value != 0 && usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) { 225 if (config.warn.is_percentage && config.warn.value != 0 &&
226 usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) {
212 return STATE_WARNING; 227 return STATE_WARNING;
213 } 228 }
214 229
diff --git a/plugins/check_swap.d/check_swap.h b/plugins/check_swap.d/check_swap.h
index 1000fc9e..8d3c7fcf 100644
--- a/plugins/check_swap.d/check_swap.h
+++ b/plugins/check_swap.d/check_swap.h
@@ -1,7 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include "../common.h" 3#include "../common.h"
4#include "output.h" 4#include "../../lib/output.h"
5#include "../../lib/states.h"
5 6
6#ifndef SWAP_CONVERSION 7#ifndef SWAP_CONVERSION
7# define SWAP_CONVERSION 1 8# define SWAP_CONVERSION 1
@@ -26,7 +27,7 @@ typedef struct {
26 27
27typedef struct { 28typedef struct {
28 bool allswaps; 29 bool allswaps;
29 int no_swap_state; 30 mp_state_enum no_swap_state;
30 bool warn_is_set; 31 bool warn_is_set;
31 check_swap_threshold warn; 32 check_swap_threshold warn;
32 bool crit_is_set; 33 bool crit_is_set;
@@ -42,6 +43,7 @@ swap_config swap_config_init(void);
42 43
43swap_result get_swap_data(swap_config config); 44swap_result get_swap_data(swap_config config);
44swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]); 45swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]);
45swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]); 46swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[],
47 const char swap_format[]);
46swap_result getSwapFromSwapctl_BSD(swap_config config); 48swap_result getSwapFromSwapctl_BSD(swap_config config);
47swap_result getSwapFromSwap_SRV4(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
index 180d5037..5b654197 100644
--- a/plugins/check_swap.d/swap.c
+++ b/plugins/check_swap.d/swap.c
@@ -68,7 +68,7 @@ swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
68 FILE *meminfo_file_ptr; 68 FILE *meminfo_file_ptr;
69 meminfo_file_ptr = fopen(proc_meminfo, "r"); 69 meminfo_file_ptr = fopen(proc_meminfo, "r");
70 70
71 swap_result result = {0}; 71 swap_result result = {};
72 result.errorcode = STATE_UNKNOWN; 72 result.errorcode = STATE_UNKNOWN;
73 73
74 if (meminfo_file_ptr == NULL) { 74 if (meminfo_file_ptr == NULL) {
@@ -78,90 +78,81 @@ swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
78 return result; 78 return result;
79 } 79 }
80 80
81 uint64_t swap_total = 0; 81 unsigned long swap_total = 0;
82 uint64_t swap_used = 0; 82 unsigned long swap_used = 0;
83 uint64_t swap_free = 0; 83 unsigned long swap_free = 0;
84 84
85 bool found_total = false; 85 bool found_total = false;
86 bool found_used = false;
87 bool found_free = false; 86 bool found_free = false;
88 87
89 char input_buffer[MAX_INPUT_BUFFER]; 88 char input_buffer[MAX_INPUT_BUFFER];
90 char str[32]; 89 char str[32];
91 90
92 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) { 91 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) {
93 uint64_t tmp_KB = 0;
94 92
95 /* 93 /*
96 * The following sscanf call looks for a line looking like: "Swap: 123 94 * The following sscanf call looks for a line looking like: "Swap: 123
97 * 123 123" On which kind of system this format exists, I can not say, 95 * 123 123" which exists on NetBSD (at least),
98 * but I wanted to document this for people who are not adapt with 96 * The unit should be Bytes
99 * sscanf anymore, like me
100 * Also the units used here are unclear and probably wrong
101 */ 97 */
102 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, &swap_free) == 3) { 98 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used,
103 99 &swap_free) == 3) {
104 result.metrics.total += swap_total;
105 result.metrics.used += swap_used;
106 result.metrics.free += swap_free;
107
108 found_total = true; 100 found_total = true;
109 found_free = true; 101 found_free = true;
110 found_used = true;
111
112 // Set error 102 // Set error
113 result.errorcode = STATE_OK; 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 }
114 125
115 /* 126 /* I think this part is always in Kb, so convert to bytes */
116 * The following sscanf call looks for lines looking like: 127 if (strcmp("Total", str) == 0) {
117 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least 128 swap_total = tmp_KB * 1000;
118 * on Debian Linux with a 5.* kernel 129 found_total = true;
119 */ 130 } else if (strcmp("Free", str) == 0) {
120 } else { 131 swap_free += tmp_KB * 1000;
121 int sscanf_result = sscanf(input_buffer, 132 found_free = true;
122 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu " 133 } else if (strcmp("Cached", str) == 0) {
123 "%*[k]%*[B]", 134 swap_free += tmp_KB * 1000;
124 str, &tmp_KB);
125
126 if (sscanf_result == 2) {
127
128 if (verbose >= 3) {
129 printf("Got %s with %lu\n", str, tmp_KB);
130 }
131
132 /* I think this part is always in Kb, so convert to bytes */
133 if (strcmp("Total", str) == 0) {
134 swap_total = tmp_KB * 1000;
135 found_total = true;
136 } else if (strcmp("Free", str) == 0) {
137 swap_free = swap_free + tmp_KB * 1000;
138 found_free = true;
139 found_used = true; // No explicit used metric available
140 } else if (strcmp("Cached", str) == 0) {
141 swap_free = swap_free + tmp_KB * 1000;
142 found_free = true;
143 found_used = true; // No explicit used metric available
144 }
145
146 result.errorcode = STATE_OK;
147 } 135 }
136
137 result.errorcode = STATE_OK;
148 } 138 }
149 } 139 }
150 140
151 fclose(meminfo_file_ptr); 141 fclose(meminfo_file_ptr);
152 142
153 result.metrics.total = swap_total; 143 result.metrics.total = swap_total;
154 result.metrics.used = swap_total - swap_free;
155 result.metrics.free = swap_free; 144 result.metrics.free = swap_free;
145 result.metrics.used = swap_total - swap_free;
156 146
157 if (!found_free || !found_total || !found_used) { 147 if (!found_free || !found_total) {
158 result.errorcode = STATE_UNKNOWN; 148 result.errorcode = STATE_UNKNOWN;
159 } 149 }
160 150
161 return result; 151 return result;
162} 152}
163 153
164swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]) { 154swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[],
155 const char swap_format[]) {
165 swap_result result = {0}; 156 swap_result result = {0};
166 157
167 char *temp_buffer; 158 char *temp_buffer;
@@ -224,7 +215,8 @@ swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[]
224 used_swap_mb = total_swap_mb - free_swap_mb; 215 used_swap_mb = total_swap_mb - free_swap_mb;
225 216
226 if (verbose >= 3) { 217 if (verbose >= 3) {
227 printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb); 218 printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb,
219 free_swap_mb);
228 } 220 }
229 } else { 221 } else {
230 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 222 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
@@ -297,8 +289,14 @@ struct swapent {
297}; 289};
298 290
299#else 291#else
292
293// Includes for NetBSD
294# include <unistd.h>
295# include <sys/swap.h>
296
300# define bsd_swapctl swapctl 297# define bsd_swapctl swapctl
301#endif 298
299#endif // CHECK_SWAP_SWAPCTL_BSD
302 300
303swap_result getSwapFromSwapctl_BSD(swap_config config) { 301swap_result getSwapFromSwapctl_BSD(swap_config config) {
304 /* get the number of active swap devices */ 302 /* get the number of active swap devices */
@@ -322,8 +320,8 @@ swap_result getSwapFromSwapctl_BSD(swap_config config) {
322 unsigned long long used_swap_mb = 0; 320 unsigned long long used_swap_mb = 0;
323 321
324 for (int i = 0; i < nswaps; i++) { 322 for (int i = 0; i < nswaps; i++) {
325 dsktotal_mb = (float)ent[i].se_nblks / (float)config.conversion_factor; 323 dsktotal_mb = (double)ent[i].se_nblks / (double)config.conversion_factor;
326 dskused_mb = (float)ent[i].se_inuse / (float)config.conversion_factor; 324 dskused_mb = (double)ent[i].se_inuse / (double)config.conversion_factor;
327 dskfree_mb = (dsktotal_mb - dskused_mb); 325 dskfree_mb = (dsktotal_mb - dskused_mb);
328 326
329 if (config.allswaps && dsktotal_mb > 0) { 327 if (config.allswaps && dsktotal_mb > 0) {
@@ -404,7 +402,8 @@ swap_result getSwapFromSwap_SRV4(swap_config config) {
404 } 402 }
405 403
406 /* initialize swap table + entries */ 404 /* initialize swap table + entries */
407 swaptbl_t *tbl = (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps)); 405 swaptbl_t *tbl =
406 (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
408 407
409 if (tbl == NULL) { 408 if (tbl == NULL) {
410 die(STATE_UNKNOWN, _("malloc() failed!\n")); 409 die(STATE_UNKNOWN, _("malloc() failed!\n"));
@@ -439,7 +438,8 @@ swap_result getSwapFromSwap_SRV4(swap_config config) {
439 dskused_mb = (dsktotal_mb - dskfree_mb); 438 dskused_mb = (dsktotal_mb - dskfree_mb);
440 439
441 if (verbose >= 3) { 440 if (verbose >= 3) {
442 printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb); 441 printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb,
442 dskused_mb);
443 } 443 }
444 444
445 if (config.allswaps && dsktotal_mb > 0) { 445 if (config.allswaps && dsktotal_mb > 0) {
diff --git a/plugins/check_tcp.c b/plugins/check_tcp.c
index 49ad096c..09806373 100644
--- a/plugins/check_tcp.c
+++ b/plugins/check_tcp.c
@@ -3,7 +3,7 @@
3 * Monitoring check_tcp plugin 3 * Monitoring check_tcp plugin
4 * 4 *
5 * License: GPL 5 * License: GPL
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2025 Monitoring Plugins Development Team
7 * 7 *
8 * Description: 8 * Description:
9 * 9 *
@@ -29,74 +29,64 @@
29 29
30/* progname "check_tcp" changes depending on symlink called */ 30/* progname "check_tcp" changes depending on symlink called */
31char *progname; 31char *progname;
32const char *copyright = "1999-2024"; 32const char *copyright = "1999-2025";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "./common.h"
36#include "netutils.h" 36#include "./netutils.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_tcp.h" 38#include "./check_tcp.d/config.h"
39#include "output.h"
40#include "states.h"
39 41
42#include <sys/types.h>
40#include <ctype.h> 43#include <ctype.h>
41#include <sys/select.h> 44#include <sys/select.h>
42 45
46ssize_t my_recv(int socket_descriptor, char *buf, size_t len, bool use_tls) {
43#ifdef HAVE_SSL 47#ifdef HAVE_SSL
44static bool check_cert = false; 48 if (use_tls) {
45static int days_till_exp_warn, days_till_exp_crit; 49 return np_net_ssl_read(buf, (int)len);
46# define my_recv(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) 50 }
47# define my_send(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) 51#endif
48#else 52 return read(socket_descriptor, buf, len);
49# define my_recv(buf, len) read(sd, buf, len) 53}
50# define my_send(buf, len) send(sd, buf, len, 0) 54
55ssize_t my_send(int socket_descriptor, char *buf, size_t len, bool use_tls) {
56#ifdef HAVE_SSL
57 if (use_tls) {
58 return np_net_ssl_write(buf, (int)len);
59 }
51#endif 60#endif
61 return write(socket_descriptor, buf, len);
62}
52 63
53/* int my_recv(char *, size_t); */ 64typedef struct {
54static int process_arguments(int /*argc*/, char ** /*argv*/); 65 int errorcode;
55static void print_help(void); 66 check_tcp_config config;
67} check_tcp_config_wrapper;
68static check_tcp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/,
69 check_tcp_config /*config*/);
70void print_help(const char *service);
56void print_usage(void); 71void print_usage(void);
57 72
58#define EXPECT server_expect[0] 73int verbosity = 0;
59static char *SERVICE = "TCP";
60static char *SEND = NULL;
61static char *QUIT = NULL;
62static int PROTOCOL = IPPROTO_TCP; /* most common is default */
63static int PORT = 0;
64static int READ_TIMEOUT = 2;
65
66static int server_port = 0;
67static char *server_address = NULL;
68static bool host_specified = false;
69static char *server_send = NULL;
70static char *server_quit = NULL;
71static char **server_expect;
72static size_t server_expect_count = 0;
73static ssize_t maxbytes = 0;
74static char **warn_codes = NULL;
75static size_t warn_codes_count = 0;
76static char **crit_codes = NULL;
77static size_t crit_codes_count = 0;
78static unsigned int delay = 0;
79static double warning_time = 0;
80static double critical_time = 0;
81static double elapsed_time = 0;
82static long microsec;
83static int sd = 0;
84#define MAXBUF 1024
85static char buffer[MAXBUF];
86static int expect_mismatch_state = STATE_WARNING;
87static int match_flags = NP_MATCH_EXACT;
88 74
89#ifdef HAVE_SSL 75static const int READ_TIMEOUT = 2;
90static char *sni = NULL; 76
91static bool sni_specified = false; 77const int MAXBUF = 1024;
92#endif
93 78
94#define FLAG_SSL 0x01 79const int DEFAULT_FTP_PORT = 21;
95#define FLAG_VERBOSE 0x02 80const int DEFAULT_POP_PORT = 110;
96#define FLAG_TIME_WARN 0x04 81const int DEFAULT_SPOP_PORT = 995;
97#define FLAG_TIME_CRIT 0x08 82const int DEFAULT_SMTP_PORT = 25;
98#define FLAG_HIDE_OUTPUT 0x10 83const int DEFAULT_SSMTP_PORT = 465;
99static size_t flags; 84const int DEFAULT_IMAP_PORT = 143;
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;
100 90
101int main(int argc, char **argv) { 91int main(int argc, char **argv) {
102 setlocale(LC_ALL, ""); 92 setlocale(LC_ALL, "");
@@ -105,353 +95,482 @@ int main(int argc, char **argv) {
105 95
106 /* determine program- and service-name quickly */ 96 /* determine program- and service-name quickly */
107 progname = strrchr(argv[0], '/'); 97 progname = strrchr(argv[0], '/');
108 if (progname != NULL) 98 if (progname != NULL) {
109 progname++; 99 progname++;
110 else 100 } else {
111 progname = argv[0]; 101 progname = argv[0];
102 }
103
104 // Initialize config here with values from above,
105 // might be changed by on disk config or cli commands
106 check_tcp_config config = check_tcp_config_init();
112 107
113 size_t prog_name_len = strlen(progname); 108 size_t prog_name_len = strlen(progname);
114 if (prog_name_len > 6 && !memcmp(progname, "check_", 6)) { 109 const size_t prefix_length = strlen("check_");
115 SERVICE = strdup(progname + 6); 110
116 for (size_t i = 0; i < prog_name_len - 6; i++) 111 if (prog_name_len <= prefix_length) {
117 SERVICE[i] = toupper(SERVICE[i]); 112 die(STATE_UNKNOWN, _("Weird progname"));
113 }
114
115 if (!memcmp(progname, "check_", prefix_length)) {
116 config.service = strdup(progname + prefix_length);
117 if (config.service == NULL) {
118 die(STATE_UNKNOWN, _("Allocation failed"));
119 }
120
121 for (size_t i = 0; i < prog_name_len - prefix_length; i++) {
122 config.service[i] = toupper(config.service[i]);
123 }
118 } 124 }
119 125
120 /* set up a reasonable buffer at first (will be realloc()'ed if 126 /* set up a reasonable buffer at first (will be realloc()'ed if
121 * user specifies other options) */ 127 * user specifies other options) */
122 server_expect = calloc(2, sizeof(char *)); 128 config.server_expect = calloc(2, sizeof(char *));
129
130 if (config.server_expect == NULL) {
131 die(STATE_UNKNOWN, _("Allocation failed"));
132 }
123 133
124 /* determine defaults for this service's protocol */ 134 /* determine defaults for this service's protocol */
125 if (!strncmp(SERVICE, "UDP", 3)) { 135 if (!strncmp(config.service, "UDP", strlen("UDP"))) {
126 PROTOCOL = IPPROTO_UDP; 136 config.protocol = IPPROTO_UDP;
127 } else if (!strncmp(SERVICE, "FTP", 3)) { 137 } else if (!strncmp(config.service, "FTP", strlen("FTP"))) {
128 EXPECT = "220"; 138 config.server_expect[0] = "220";
129 QUIT = "QUIT\r\n"; 139 config.quit = "QUIT\r\n";
130 PORT = 21; 140 config.server_port = DEFAULT_FTP_PORT;
131 } else if (!strncmp(SERVICE, "POP", 3) || !strncmp(SERVICE, "POP3", 4)) { 141 } else if (!strncmp(config.service, "POP", strlen("POP")) ||
132 EXPECT = "+OK"; 142 !strncmp(config.service, "POP3", strlen("POP3"))) {
133 QUIT = "QUIT\r\n"; 143 config.server_expect[0] = "+OK";
134 PORT = 110; 144 config.quit = "QUIT\r\n";
135 } else if (!strncmp(SERVICE, "SMTP", 4)) { 145 config.server_port = DEFAULT_POP_PORT;
136 EXPECT = "220"; 146 } else if (!strncmp(config.service, "SMTP", strlen("SMTP"))) {
137 QUIT = "QUIT\r\n"; 147 config.server_expect[0] = "220";
138 PORT = 25; 148 config.quit = "QUIT\r\n";
139 } else if (!strncmp(SERVICE, "IMAP", 4)) { 149 config.server_port = DEFAULT_SMTP_PORT;
140 EXPECT = "* OK"; 150 } else if (!strncmp(config.service, "IMAP", strlen("IMAP"))) {
141 QUIT = "a1 LOGOUT\r\n"; 151 config.server_expect[0] = "* OK";
142 PORT = 143; 152 config.quit = "a1 LOGOUT\r\n";
153 config.server_port = DEFAULT_IMAP_PORT;
143 } 154 }
144#ifdef HAVE_SSL 155#ifdef HAVE_SSL
145 else if (!strncmp(SERVICE, "SIMAP", 5)) { 156 else if (!strncmp(config.service, "SIMAP", strlen("SIMAP"))) {
146 EXPECT = "* OK"; 157 config.server_expect[0] = "* OK";
147 QUIT = "a1 LOGOUT\r\n"; 158 config.quit = "a1 LOGOUT\r\n";
148 flags |= FLAG_SSL; 159 config.use_tls = true;
149 PORT = 993; 160 config.server_port = DEFAULT_SIMAP_PORT;
150 } else if (!strncmp(SERVICE, "SPOP", 4)) { 161 } else if (!strncmp(config.service, "SPOP", strlen("SPOP"))) {
151 EXPECT = "+OK"; 162 config.server_expect[0] = "+OK";
152 QUIT = "QUIT\r\n"; 163 config.quit = "QUIT\r\n";
153 flags |= FLAG_SSL; 164 config.use_tls = true;
154 PORT = 995; 165 config.server_port = DEFAULT_SPOP_PORT;
155 } else if (!strncmp(SERVICE, "SSMTP", 5)) { 166 } else if (!strncmp(config.service, "SSMTP", strlen("SSMTP"))) {
156 EXPECT = "220"; 167 config.server_expect[0] = "220";
157 QUIT = "QUIT\r\n"; 168 config.quit = "QUIT\r\n";
158 flags |= FLAG_SSL; 169 config.use_tls = true;
159 PORT = 465; 170 config.server_port = DEFAULT_SSMTP_PORT;
160 } else if (!strncmp(SERVICE, "JABBER", 6)) { 171 } else if (!strncmp(config.service, "JABBER", strlen("JABBER"))) {
161 SEND = "<stream:stream to=\'host\' xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'>\n"; 172 config.send = "<stream:stream to=\'host\' xmlns=\'jabber:client\' "
162 EXPECT = "<?xml version=\'1.0\'"; 173 "xmlns:stream=\'http://etherx.jabber.org/streams\'>\n";
163 QUIT = "</stream:stream>\n"; 174 config.server_expect[0] = "<?xml version=\'1.0\'";
164 flags |= FLAG_HIDE_OUTPUT; 175 config.quit = "</stream:stream>\n";
165 PORT = 5222; 176 config.hide_output = true;
166 } else if (!strncmp(SERVICE, "NNTPS", 5)) { 177 config.server_port = DEFAULT_XMPP_C2S_PORT;
167 server_expect_count = 2; 178 } else if (!strncmp(config.service, "NNTPS", strlen("NNTPS"))) {
168 server_expect[0] = "200"; 179 config.server_expect_count = 2;
169 server_expect[1] = "201"; 180 config.server_expect[0] = "200";
170 QUIT = "QUIT\r\n"; 181 config.server_expect[1] = "201";
171 flags |= FLAG_SSL; 182 config.quit = "QUIT\r\n";
172 PORT = 563; 183 config.use_tls = true;
184 config.server_port = DEFAULT_NNTPS_PORT;
173 } 185 }
174#endif 186#endif
175 else if (!strncmp(SERVICE, "NNTP", 4)) { 187 else if (!strncmp(config.service, "NNTP", strlen("NNTP"))) {
176 server_expect_count = 2; 188 config.server_expect_count = 2;
177 server_expect = malloc(sizeof(char *) * server_expect_count); 189 char **tmp = realloc(config.server_expect, config.server_expect_count * sizeof(char *));
178 server_expect[0] = strdup("200"); 190 if (tmp == NULL) {
179 server_expect[1] = strdup("201"); 191 free(config.server_expect);
180 QUIT = "QUIT\r\n"; 192 die(STATE_UNKNOWN, _("Allocation failed"));
181 PORT = 119; 193 }
182 } else if (!strncmp(SERVICE, "CLAMD", 5)) { 194 config.server_expect = tmp;
183 SEND = "PING"; 195
184 EXPECT = "PONG"; 196 config.server_expect[0] = strdup("200");
185 QUIT = NULL; 197 config.server_expect[1] = strdup("201");
186 PORT = 3310; 198 config.quit = "QUIT\r\n";
199 config.server_port = DEFAULT_NNTP_PORT;
200 } else if (!strncmp(config.service, "CLAMD", strlen("CLAMD"))) {
201 config.send = "PING";
202 config.server_expect[0] = "PONG";
203 config.quit = NULL;
204 config.server_port = DEFAULT_CLAMD_PORT;
187 } 205 }
188 /* fallthrough check, so it's supposed to use reverse matching */ 206 /* fallthrough check, so it's supposed to use reverse matching */
189 else if (strcmp(SERVICE, "TCP")) 207 else if (strcmp(config.service, "TCP")) {
190 usage(_("CRITICAL - Generic check_tcp called with unknown service\n")); 208 usage(_("CRITICAL - Generic check_tcp called with unknown service\n"));
191 209 }
192 server_address = "127.0.0.1";
193 server_port = PORT;
194 server_send = SEND;
195 server_quit = QUIT;
196 char *status = NULL;
197 210
198 /* Parse extra opts if any */ 211 /* Parse extra opts if any */
199 argv = np_extra_opts(&argc, argv, progname); 212 argv = np_extra_opts(&argc, argv, progname);
200 213
201 if (process_arguments(argc, argv) == ERROR) 214 check_tcp_config_wrapper paw = process_arguments(argc, argv, config);
215 if (paw.errorcode == ERROR) {
202 usage4(_("Could not parse arguments")); 216 usage4(_("Could not parse arguments"));
217 }
218
219 config = paw.config;
203 220
204 if (flags & FLAG_VERBOSE) { 221 if (verbosity > 0) {
205 printf("Using service %s\n", SERVICE); 222 printf("Using service %s\n", config.service);
206 printf("Port: %d\n", server_port); 223 printf("Port: %d\n", config.server_port);
207 printf("flags: 0x%x\n", (int)flags);
208 } 224 }
209 225
210 if (EXPECT && !server_expect_count) 226 if ((config.server_expect_count == 0) && config.server_expect[0]) {
211 server_expect_count++; 227 config.server_expect_count++;
228 }
212 229
213 if (PROTOCOL == IPPROTO_UDP && !(server_expect_count && server_send)) { 230 if (config.protocol == IPPROTO_UDP && !(config.server_expect_count && config.send)) {
214 usage(_("With UDP checks, a send/expect string must be specified.")); 231 usage(_("With UDP checks, a send/expect string must be specified."));
215 } 232 }
216 233
234 // Initialize check stuff before setting timers
235 mp_check overall = mp_check_init();
236 if (config.output_format_set) {
237 mp_set_format(config.output_format);
238 }
239
217 /* set up the timer */ 240 /* set up the timer */
218 signal(SIGALRM, socket_timeout_alarm_handler); 241 signal(SIGALRM, socket_timeout_alarm_handler);
219 alarm(socket_timeout); 242 alarm(socket_timeout);
220 243
221 /* try to connect to the host at the given port number */ 244 /* try to connect to the host at the given port number */
222 struct timeval tv; 245 struct timeval start_time;
223 gettimeofday(&tv, NULL); 246 gettimeofday(&start_time, NULL);
224 247
225 int result = STATE_UNKNOWN; 248 int socket_descriptor = 0;
226 result = np_net_connect(server_address, server_port, &sd, PROTOCOL); 249 mp_subcheck inital_connect_result = mp_subcheck_init();
227 if (result == STATE_CRITICAL) 250
228 return econn_refuse_state; 251 // Try initial connection
252 if (np_net_connect(config.server_address, config.server_port, &socket_descriptor,
253 config.protocol) == STATE_CRITICAL) {
254 // Early exit here, we got connection refused
255 inital_connect_result =
256 mp_set_subcheck_state(inital_connect_result, config.econn_refuse_state);
257 xasprintf(&inital_connect_result.output, "Connection to %s on port %i was REFUSED",
258 config.server_address, config.server_port);
259 mp_add_subcheck_to_check(&overall, inital_connect_result);
260 mp_exit(overall);
261 } else {
262 inital_connect_result = mp_set_subcheck_state(inital_connect_result, STATE_OK);
263 xasprintf(&inital_connect_result.output, "Connection to %s on port %i was a SUCCESS",
264 config.server_address, config.server_port);
265 mp_add_subcheck_to_check(&overall, inital_connect_result);
266 }
229 267
230#ifdef HAVE_SSL 268#ifdef HAVE_SSL
231 if (flags & FLAG_SSL) { 269 if (config.use_tls) {
232 result = np_net_ssl_init_with_hostname(sd, (sni_specified ? sni : NULL)); 270 mp_subcheck tls_connection_result = mp_subcheck_init();
233 if (result == STATE_OK && check_cert) { 271 mp_state_enum result = np_net_ssl_init_with_hostname(
234 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 272 socket_descriptor, (config.sni_specified ? config.sni : NULL));
273 tls_connection_result = mp_set_subcheck_default_state(tls_connection_result, result);
274
275 if (result == STATE_OK) {
276 xasprintf(&tls_connection_result.output, "TLS connection succeeded");
277
278 if (config.check_cert) {
279 result =
280 np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit);
281
282 mp_subcheck tls_certificate_lifetime_result = mp_subcheck_init();
283 tls_certificate_lifetime_result =
284 mp_set_subcheck_state(tls_certificate_lifetime_result, result);
285
286 if (result == STATE_OK) {
287 xasprintf(&tls_certificate_lifetime_result.output,
288 "Certificate lifetime is within thresholds");
289 } else if (result == STATE_WARNING) {
290 xasprintf(&tls_certificate_lifetime_result.output,
291 "Certificate lifetime is violating warning threshold (%i)",
292 config.days_till_exp_warn);
293 } else if (result == STATE_CRITICAL) {
294 xasprintf(&tls_certificate_lifetime_result.output,
295 "Certificate lifetime is violating critical threshold (%i)",
296 config.days_till_exp_crit);
297 } else {
298 xasprintf(&tls_certificate_lifetime_result.output,
299 "Certificate lifetime is somehow unknown");
300 }
301
302 mp_add_subcheck_to_subcheck(&tls_connection_result,
303 tls_certificate_lifetime_result);
304 }
305
306 mp_add_subcheck_to_check(&overall, tls_connection_result);
307 } else {
308 xasprintf(&tls_connection_result.output, "TLS connection failed");
309 mp_add_subcheck_to_check(&overall, tls_connection_result);
310
311 if (socket_descriptor) {
312 close(socket_descriptor);
313 }
314 np_net_ssl_cleanup();
315
316 mp_exit(overall);
235 } 317 }
236 } 318 }
237 if (result != STATE_OK) {
238 if (sd)
239 close(sd);
240 np_net_ssl_cleanup();
241 return result;
242 }
243#endif /* HAVE_SSL */ 319#endif /* HAVE_SSL */
244 320
245 if (server_send != NULL) { /* Something to send? */ 321 if (config.send != NULL) { /* Something to send? */
246 my_send(server_send, strlen(server_send)); 322 my_send(socket_descriptor, config.send, strlen(config.send), config.use_tls);
247 } 323 }
248 324
249 if (delay > 0) { 325 if (config.delay > 0) {
250 tv.tv_sec += delay; 326 start_time.tv_sec += config.delay;
251 sleep(delay); 327 sleep(config.delay);
252 } 328 }
253 329
254 if (flags & FLAG_VERBOSE) { 330 if (verbosity > 0) {
255 if (server_send) { 331 if (config.send) {
256 printf("Send string: %s\n", server_send); 332 printf("Send string: %s\n", config.send);
333 }
334 if (config.quit) {
335 printf("Quit string: %s\n", config.quit);
257 } 336 }
258 if (server_quit) { 337 printf("server_expect_count: %d\n", (int)config.server_expect_count);
259 printf("Quit string: %s\n", server_quit); 338 for (size_t i = 0; i < config.server_expect_count; i++) {
339 printf("\t%zd: %s\n", i, config.server_expect[i]);
260 } 340 }
261 printf("server_expect_count: %d\n", (int)server_expect_count);
262 for (size_t i = 0; i < server_expect_count; i++)
263 printf("\t%zd: %s\n", i, server_expect[i]);
264 } 341 }
265 342
266 /* if(len) later on, we know we have a non-NULL response */ 343 /* if(len) later on, we know we have a non-NULL response */
267 ssize_t len = 0; 344 ssize_t len = 0;
345 char *received_buffer = NULL;
346 enum np_match_result match = NP_MATCH_NONE;
347 mp_subcheck expected_data_result = mp_subcheck_init();
268 348
269 int match = -1; 349 if (config.server_expect_count) {
270 struct timeval timeout;
271 fd_set rfds;
272 FD_ZERO(&rfds);
273 if (server_expect_count) {
274 ssize_t received = 0; 350 ssize_t received = 0;
351 char buffer[MAXBUF];
275 352
276 /* watch for the expect string */ 353 /* watch for the expect string */
277 while ((received = my_recv(buffer, sizeof(buffer))) > 0) { 354 while ((received = my_recv(socket_descriptor, buffer, sizeof(buffer), config.use_tls)) >
278 status = realloc(status, len + received + 1); 355 0) {
279 memcpy(&status[len], buffer, received); 356 received_buffer = realloc(received_buffer, len + received + 1);
357
358 if (received_buffer == NULL) {
359 die(STATE_UNKNOWN, _("Allocation failed"));
360 }
361
362 memcpy(&received_buffer[len], buffer, received);
280 len += received; 363 len += received;
281 status[len] = '\0'; 364 received_buffer[len] = '\0';
282 365
283 /* stop reading if user-forced */ 366 /* stop reading if user-forced */
284 if (maxbytes && len >= maxbytes) 367 if (config.maxbytes && len >= config.maxbytes) {
285 break; 368 break;
369 }
286 370
287 if ((match = np_expect_match(status, server_expect, server_expect_count, match_flags)) != NP_MATCH_RETRY) 371 if ((match = np_expect_match(received_buffer, config.server_expect,
372 config.server_expect_count, config.match_flags)) !=
373 NP_MATCH_RETRY) {
288 break; 374 break;
375 }
376
377 fd_set rfds;
378 FD_ZERO(&rfds);
379 FD_SET(socket_descriptor, &rfds);
289 380
290 /* some protocols wait for further input, so make sure we don't wait forever */ 381 /* some protocols wait for further input, so make sure we don't wait forever */
291 FD_SET(sd, &rfds); 382 struct timeval timeout;
292 timeout.tv_sec = READ_TIMEOUT; 383 timeout.tv_sec = READ_TIMEOUT;
293 timeout.tv_usec = 0; 384 timeout.tv_usec = 0;
294 if (select(sd + 1, &rfds, NULL, NULL, &timeout) <= 0) 385
386 if (select(socket_descriptor + 1, &rfds, NULL, NULL, &timeout) <= 0) {
295 break; 387 break;
388 }
296 } 389 }
297 390
298 if (match == NP_MATCH_RETRY) 391 if (match == NP_MATCH_RETRY) {
299 match = NP_MATCH_FAILURE; 392 match = NP_MATCH_FAILURE;
393 }
300 394
301 /* no data when expected, so return critical */ 395 /* no data when expected, so return critical */
302 if (len == 0) 396 if (len == 0) {
303 die(STATE_CRITICAL, _("No data received from host\n")); 397 xasprintf(&expected_data_result.output, "Received no data when some was expected");
398 expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_CRITICAL);
399 mp_add_subcheck_to_check(&overall, expected_data_result);
400 mp_exit(overall);
401 }
304 402
305 /* print raw output if we're debugging */ 403 /* print raw output if we're debugging */
306 if (flags & FLAG_VERBOSE) 404 if (verbosity > 0) {
307 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n", (int)len + 1, status); 405 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n",
406 (int)len + 1, received_buffer);
407 }
308 /* strip whitespace from end of output */ 408 /* strip whitespace from end of output */
309 while (--len > 0 && isspace(status[len])) 409 while (--len > 0 && isspace(received_buffer[len])) {
310 status[len] = '\0'; 410 received_buffer[len] = '\0';
411 }
311 } 412 }
312 413
313 if (server_quit != NULL) { 414 if (config.quit != NULL) {
314 my_send(server_quit, strlen(server_quit)); 415 my_send(socket_descriptor, config.quit, strlen(config.quit), config.use_tls);
416 }
417
418 if (socket_descriptor) {
419 close(socket_descriptor);
315 } 420 }
316 if (sd)
317 close(sd);
318#ifdef HAVE_SSL 421#ifdef HAVE_SSL
319 np_net_ssl_cleanup(); 422 np_net_ssl_cleanup();
320#endif 423#endif
321 424
322 microsec = deltime(tv); 425 long microsec = deltime(start_time);
323 elapsed_time = (double)microsec / 1.0e6; 426 double elapsed_time = (double)microsec / 1.0e6;
427
428 mp_subcheck elapsed_time_result = mp_subcheck_init();
429
430 mp_perfdata time_pd = perfdata_init();
431 time_pd = mp_set_pd_value(time_pd, elapsed_time);
432 time_pd.label = "time";
433 time_pd.uom = "s";
434
435 if (config.critical_time_set && elapsed_time > config.critical_time) {
436 xasprintf(&elapsed_time_result.output,
437 "Connection time %fs exceeded critical threshold (%f)", elapsed_time,
438 config.critical_time);
439
440 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_CRITICAL);
441 time_pd.crit_present = true;
442 mp_range crit_val = mp_range_init();
443
444 crit_val.end = mp_create_pd_value(config.critical_time);
445 crit_val.end_infinity = false;
446
447 time_pd.crit = crit_val;
448 } else if (config.warning_time_set && elapsed_time > config.warning_time) {
449 xasprintf(&elapsed_time_result.output,
450 "Connection time %fs exceeded warning threshold (%f)", elapsed_time,
451 config.critical_time);
452
453 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_WARNING);
454 time_pd.warn_present = true;
455 mp_range warn_val = mp_range_init();
456 warn_val.end = mp_create_pd_value(config.critical_time);
457 warn_val.end_infinity = false;
458
459 time_pd.warn = warn_val;
460 } else {
461 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_OK);
462 xasprintf(&elapsed_time_result.output, "Connection time %fs is within thresholds",
463 elapsed_time);
464 }
324 465
325 if (flags & FLAG_TIME_CRIT && elapsed_time > critical_time) 466 mp_add_perfdata_to_subcheck(&elapsed_time_result, time_pd);
326 result = STATE_CRITICAL; 467 mp_add_subcheck_to_check(&overall, elapsed_time_result);
327 else if (flags & FLAG_TIME_WARN && elapsed_time > warning_time)
328 result = STATE_WARNING;
329 468
330 /* did we get the response we hoped? */ 469 /* did we get the response we hoped? */
331 if (match == NP_MATCH_FAILURE && result != STATE_CRITICAL) 470 if (match == NP_MATCH_FAILURE) {
332 result = expect_mismatch_state; 471 expected_data_result =
472 mp_set_subcheck_state(expected_data_result, config.expect_mismatch_state);
473 xasprintf(&expected_data_result.output, "Answer failed to match expectation");
474 mp_add_subcheck_to_check(&overall, expected_data_result);
475 } else if (match == NP_MATCH_SUCCESS) {
476 expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_OK);
477 xasprintf(&expected_data_result.output, "The answer of the server matched the expectation");
478 mp_add_subcheck_to_check(&overall, expected_data_result);
479 }
333 480
334 /* reset the alarm */ 481 /* reset the alarm */
335 alarm(0); 482 alarm(0);
336 483
337 /* this is a bit stupid, because we don't want to print the 484 mp_exit(overall);
338 * response time (which can look ok to the user) if we didn't get
339 * the response we were looking for. if-else */
340 printf("%s %s - ", SERVICE, state_text(result));
341
342 if (match == NP_MATCH_FAILURE && len && !(flags & FLAG_HIDE_OUTPUT))
343 printf("Unexpected response from host/socket: %s", status);
344 else {
345 if (match == NP_MATCH_FAILURE)
346 printf("Unexpected response from host/socket on ");
347 else
348 printf("%.3f second response time on ", elapsed_time);
349 if (server_address[0] != '/') {
350 if (host_specified)
351 printf("%s port %d", server_address, server_port);
352 else
353 printf("port %d", server_port);
354 } else
355 printf("socket %s", server_address);
356 }
357
358 if (match != NP_MATCH_FAILURE && !(flags & FLAG_HIDE_OUTPUT) && len)
359 printf(" [%s]", status);
360
361 /* perf-data doesn't apply when server doesn't talk properly,
362 * so print all zeroes on warn and crit. Use fperfdata since
363 * localisation settings can make different outputs */
364 if (match == NP_MATCH_FAILURE)
365 printf("|%s", fperfdata("time", elapsed_time, "s", (flags & FLAG_TIME_WARN ? true : false), 0,
366 (flags & FLAG_TIME_CRIT ? true : false), 0, true, 0, true, socket_timeout));
367 else
368 printf("|%s", fperfdata("time", elapsed_time, "s", (flags & FLAG_TIME_WARN ? true : false), warning_time,
369 (flags & FLAG_TIME_CRIT ? true : false), critical_time, true, 0, true, socket_timeout));
370
371 putchar('\n');
372 return result;
373} 485}
374 486
375/* process command-line arguments */ 487/* process command-line arguments */
376static int process_arguments(int argc, char **argv) { 488static check_tcp_config_wrapper process_arguments(int argc, char **argv, check_tcp_config config) {
377 enum { 489 enum {
378 SNI_OPTION = CHAR_MAX + 1 490 SNI_OPTION = CHAR_MAX + 1,
491 output_format_index,
379 }; 492 };
380 493
381 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 494 static struct option longopts[] = {
382 {"critical", required_argument, 0, 'c'}, 495 {"hostname", required_argument, 0, 'H'},
383 {"warning", required_argument, 0, 'w'}, 496 {"critical", required_argument, 0, 'c'},
384 {"critical-codes", required_argument, 0, 'C'}, 497 {"warning", required_argument, 0, 'w'},
385 {"warning-codes", required_argument, 0, 'W'}, 498 {"critical-codes", required_argument, 0, 'C'},
386 {"timeout", required_argument, 0, 't'}, 499 {"warning-codes", required_argument, 0, 'W'},
387 {"protocol", required_argument, 0, 'P'}, /* FIXME: Unhandled */ 500 {"timeout", required_argument, 0, 't'},
388 {"port", required_argument, 0, 'p'}, 501 {"protocol", required_argument, 0, 'P'}, /* FIXME: Unhandled */
389 {"escape", no_argument, 0, 'E'}, 502 {"port", required_argument, 0, 'p'},
390 {"all", no_argument, 0, 'A'}, 503 {"escape", no_argument, 0, 'E'},
391 {"send", required_argument, 0, 's'}, 504 {"all", no_argument, 0, 'A'},
392 {"expect", required_argument, 0, 'e'}, 505 {"send", required_argument, 0, 's'},
393 {"maxbytes", required_argument, 0, 'm'}, 506 {"expect", required_argument, 0, 'e'},
394 {"quit", required_argument, 0, 'q'}, 507 {"maxbytes", required_argument, 0, 'm'},
395 {"jail", no_argument, 0, 'j'}, 508 {"quit", required_argument, 0, 'q'},
396 {"delay", required_argument, 0, 'd'}, 509 {"jail", no_argument, 0, 'j'},
397 {"refuse", required_argument, 0, 'r'}, 510 {"delay", required_argument, 0, 'd'},
398 {"mismatch", required_argument, 0, 'M'}, 511 {"refuse", required_argument, 0, 'r'},
399 {"use-ipv4", no_argument, 0, '4'}, 512 {"mismatch", required_argument, 0, 'M'},
400 {"use-ipv6", no_argument, 0, '6'}, 513 {"use-ipv4", no_argument, 0, '4'},
401 {"verbose", no_argument, 0, 'v'}, 514 {"use-ipv6", no_argument, 0, '6'},
402 {"version", no_argument, 0, 'V'}, 515 {"verbose", no_argument, 0, 'v'},
403 {"help", no_argument, 0, 'h'}, 516 {"version", no_argument, 0, 'V'},
404 {"ssl", no_argument, 0, 'S'}, 517 {"help", no_argument, 0, 'h'},
405 {"sni", required_argument, 0, SNI_OPTION}, 518 {"ssl", no_argument, 0, 'S'},
406 {"certificate", required_argument, 0, 'D'}, 519 {"sni", required_argument, 0, SNI_OPTION},
407 {0, 0, 0, 0}}; 520 {"certificate", required_argument, 0, 'D'},
408 521 {"output-format", required_argument, 0, output_format_index},
409 if (argc < 2) 522 {0, 0, 0, 0}};
523
524 if (argc < 2) {
410 usage4(_("No arguments found")); 525 usage4(_("No arguments found"));
526 }
411 527
412 /* backwards compatibility */ 528 /* backwards compatibility */
413 for (int i = 1; i < argc; i++) { 529 for (int i = 1; i < argc; i++) {
414 if (strcmp("-to", argv[i]) == 0) 530 if (strcmp("-to", argv[i]) == 0) {
415 strcpy(argv[i], "-t"); 531 strcpy(argv[i], "-t");
416 else if (strcmp("-wt", argv[i]) == 0) 532 } else if (strcmp("-wt", argv[i]) == 0) {
417 strcpy(argv[i], "-w"); 533 strcpy(argv[i], "-w");
418 else if (strcmp("-ct", argv[i]) == 0) 534 } else if (strcmp("-ct", argv[i]) == 0) {
419 strcpy(argv[i], "-c"); 535 strcpy(argv[i], "-c");
536 }
420 } 537 }
421 538
422 if (!is_option(argv[1])) { 539 if (!is_option(argv[1])) {
423 server_address = argv[1]; 540 config.server_address = argv[1];
424 argv[1] = argv[0]; 541 argv[1] = argv[0];
425 argv = &argv[1]; 542 argv = &argv[1];
426 argc--; 543 argc--;
427 } 544 }
428 545
429 int option_char;
430 bool escape = false; 546 bool escape = false;
547
431 while (true) { 548 while (true) {
432 int option = 0; 549 int option = 0;
433 option_char = getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option); 550 int option_index =
551 getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option);
434 552
435 if (option_char == -1 || option_char == EOF || option_char == 1) 553 if (option_index == -1 || option_index == EOF || option_index == 1) {
436 break; 554 break;
555 }
437 556
438 switch (option_char) { 557 switch (option_index) {
439 case '?': /* print short usage statement if args not parsable */ 558 case '?': /* print short usage statement if args not parsable */
440 usage5(); 559 usage5();
441 case 'h': /* help */ 560 case 'h': /* help */
442 print_help(); 561 print_help(config.service);
443 exit(STATE_UNKNOWN); 562 exit(STATE_UNKNOWN);
444 case 'V': /* version */ 563 case 'V': /* version */
445 print_revision(progname, NP_VERSION); 564 print_revision(progname, NP_VERSION);
446 exit(STATE_UNKNOWN); 565 exit(STATE_UNKNOWN);
447 case 'v': /* verbose mode */ 566 case 'v': /* verbose mode */
448 flags |= FLAG_VERBOSE; 567 verbosity++;
449 match_flags |= NP_MATCH_VERBOSE; 568 config.match_flags |= NP_MATCH_VERBOSE;
450 break; 569 break;
451 case '4': 570 case '4': // Apparently unused TODO
452 address_family = AF_INET; 571 address_family = AF_INET;
453 break; 572 break;
454 case '6': 573 case '6': // Apparently unused TODO
455#ifdef USE_IPV6 574#ifdef USE_IPV6
456 address_family = AF_INET6; 575 address_family = AF_INET6;
457#else 576#else
@@ -459,163 +578,192 @@ static int process_arguments(int argc, char **argv) {
459#endif 578#endif
460 break; 579 break;
461 case 'H': /* hostname */ 580 case 'H': /* hostname */
462 host_specified = true; 581 config.host_specified = true;
463 server_address = optarg; 582 config.server_address = optarg;
464 break; 583 break;
465 case 'c': /* critical */ 584 case 'c': /* critical */
466 critical_time = strtod(optarg, NULL); 585 config.critical_time = strtod(optarg, NULL);
467 flags |= FLAG_TIME_CRIT; 586 config.critical_time_set = true;
468 break; 587 break;
469 case 'j': /* hide output */ 588 case 'j': /* hide output */
470 flags |= FLAG_HIDE_OUTPUT; 589 config.hide_output = true;
471 break; 590 break;
472 case 'w': /* warning */ 591 case 'w': /* warning */
473 warning_time = strtod(optarg, NULL); 592 config.warning_time = strtod(optarg, NULL);
474 flags |= FLAG_TIME_WARN; 593 config.warning_time_set = true;
475 break;
476 case 'C':
477 crit_codes = realloc(crit_codes, ++crit_codes_count);
478 crit_codes[crit_codes_count - 1] = optarg;
479 break;
480 case 'W':
481 warn_codes = realloc(warn_codes, ++warn_codes_count);
482 warn_codes[warn_codes_count - 1] = optarg;
483 break; 594 break;
484 case 't': /* timeout */ 595 case 't': /* timeout */
485 if (!is_intpos(optarg)) 596 if (!is_intpos(optarg)) {
486 usage4(_("Timeout interval must be a positive integer")); 597 usage4(_("Timeout interval must be a positive integer"));
487 else 598 } else {
488 socket_timeout = atoi(optarg); 599 socket_timeout = atoi(optarg);
600 }
489 break; 601 break;
490 case 'p': /* port */ 602 case 'p': /* port */
491 if (!is_intpos(optarg)) 603 if (!is_intpos(optarg)) {
492 usage4(_("Port must be a positive integer")); 604 usage4(_("Port must be a positive integer"));
493 else 605 } else {
494 server_port = atoi(optarg); 606 config.server_port = atoi(optarg);
607 }
495 break; 608 break;
496 case 'E': 609 case 'E':
497 escape = true; 610 escape = true;
498 break; 611 break;
499 case 's': 612 case 's':
500 if (escape) 613 if (escape) {
501 server_send = np_escaped_string(optarg); 614 config.send = np_escaped_string(optarg);
502 else 615 } else {
503 xasprintf(&server_send, "%s", optarg); 616 xasprintf(&config.send, "%s", optarg);
617 }
504 break; 618 break;
505 case 'e': /* expect string (may be repeated) */ 619 case 'e': /* expect string (may be repeated) */
506 match_flags &= ~NP_MATCH_EXACT; 620 config.match_flags &= ~NP_MATCH_EXACT;
507 if (server_expect_count == 0) 621 if (config.server_expect_count == 0) {
508 server_expect = malloc(sizeof(char *) * (++server_expect_count)); 622 config.server_expect = malloc(sizeof(char *) * (++config.server_expect_count));
509 else 623 } else {
510 server_expect = realloc(server_expect, sizeof(char *) * (++server_expect_count)); 624 config.server_expect =
511 server_expect[server_expect_count - 1] = optarg; 625 realloc(config.server_expect, sizeof(char *) * (++config.server_expect_count));
626 }
627
628 if (config.server_expect == NULL) {
629 die(STATE_UNKNOWN, _("Allocation failed"));
630 }
631 config.server_expect[config.server_expect_count - 1] = optarg;
512 break; 632 break;
513 case 'm': 633 case 'm':
514 if (!is_intpos(optarg)) 634 if (!is_intpos(optarg)) {
515 usage4(_("Maxbytes must be a positive integer")); 635 usage4(_("Maxbytes must be a positive integer"));
516 else 636 } else {
517 maxbytes = strtol(optarg, NULL, 0); 637 config.maxbytes = strtol(optarg, NULL, 0);
638 }
518 break; 639 break;
519 case 'q': 640 case 'q':
520 if (escape) 641 if (escape) {
521 server_quit = np_escaped_string(optarg); 642 config.quit = np_escaped_string(optarg);
522 else 643 } else {
523 xasprintf(&server_quit, "%s\r\n", optarg); 644 xasprintf(&config.quit, "%s\r\n", optarg);
645 }
524 break; 646 break;
525 case 'r': 647 case 'r':
526 if (!strncmp(optarg, "ok", 2)) 648 if (!strncmp(optarg, "ok", 2)) {
527 econn_refuse_state = STATE_OK; 649 config.econn_refuse_state = STATE_OK;
528 else if (!strncmp(optarg, "warn", 4)) 650 } else if (!strncmp(optarg, "warn", 4)) {
529 econn_refuse_state = STATE_WARNING; 651 config.econn_refuse_state = STATE_WARNING;
530 else if (!strncmp(optarg, "crit", 4)) 652 } else if (!strncmp(optarg, "crit", 4)) {
531 econn_refuse_state = STATE_CRITICAL; 653 config.econn_refuse_state = STATE_CRITICAL;
532 else 654 } else {
533 usage4(_("Refuse must be one of ok, warn, crit")); 655 usage4(_("Refuse must be one of ok, warn, crit"));
656 }
534 break; 657 break;
535 case 'M': 658 case 'M':
536 if (!strncmp(optarg, "ok", 2)) 659 if (!strncmp(optarg, "ok", 2)) {
537 expect_mismatch_state = STATE_OK; 660 config.expect_mismatch_state = STATE_OK;
538 else if (!strncmp(optarg, "warn", 4)) 661 } else if (!strncmp(optarg, "warn", 4)) {
539 expect_mismatch_state = STATE_WARNING; 662 config.expect_mismatch_state = STATE_WARNING;
540 else if (!strncmp(optarg, "crit", 4)) 663 } else if (!strncmp(optarg, "crit", 4)) {
541 expect_mismatch_state = STATE_CRITICAL; 664 config.expect_mismatch_state = STATE_CRITICAL;
542 else 665 } else {
543 usage4(_("Mismatch must be one of ok, warn, crit")); 666 usage4(_("Mismatch must be one of ok, warn, crit"));
667 }
544 break; 668 break;
545 case 'd': 669 case 'd':
546 if (is_intpos(optarg)) 670 if (is_intpos(optarg)) {
547 delay = atoi(optarg); 671 config.delay = atoi(optarg);
548 else 672 } else {
549 usage4(_("Delay must be a positive integer")); 673 usage4(_("Delay must be a positive integer"));
674 }
550 break; 675 break;
551 case 'D': { /* Check SSL cert validity - days 'til certificate expiration */ 676 case 'D': /* Check SSL cert validity - days 'til certificate expiration */
552#ifdef HAVE_SSL 677#ifdef HAVE_SSL
553# ifdef USE_OPENSSL /* XXX */ 678# ifdef USE_OPENSSL /* XXX */
679 {
554 char *temp; 680 char *temp;
555 if ((temp = strchr(optarg, ',')) != NULL) { 681 if ((temp = strchr(optarg, ',')) != NULL) {
556 *temp = '\0'; 682 *temp = '\0';
557 if (!is_intnonneg(optarg)) 683 if (!is_intnonneg(optarg)) {
558 usage2(_("Invalid certificate expiration period"), optarg); 684 usage2(_("Invalid certificate expiration period"), optarg);
559 days_till_exp_warn = atoi(optarg); 685 }
686 config.days_till_exp_warn = atoi(optarg);
560 *temp = ','; 687 *temp = ',';
561 temp++; 688 temp++;
562 if (!is_intnonneg(temp)) 689 if (!is_intnonneg(temp)) {
563 usage2(_("Invalid certificate expiration period"), temp); 690 usage2(_("Invalid certificate expiration period"), temp);
564 days_till_exp_crit = atoi(temp); 691 }
692 config.days_till_exp_crit = atoi(temp);
565 } else { 693 } else {
566 days_till_exp_crit = 0; 694 config.days_till_exp_crit = 0;
567 if (!is_intnonneg(optarg)) 695 if (!is_intnonneg(optarg)) {
568 usage2(_("Invalid certificate expiration period"), optarg); 696 usage2(_("Invalid certificate expiration period"), optarg);
569 days_till_exp_warn = atoi(optarg); 697 }
698 config.days_till_exp_warn = atoi(optarg);
570 } 699 }
571 check_cert = true; 700 config.check_cert = true;
572 flags |= FLAG_SSL; 701 config.use_tls = true;
573 } break; 702 } break;
574# endif /* USE_OPENSSL */ 703# endif /* USE_OPENSSL */
575#endif 704#endif
576 /* fallthrough if we don't have ssl */ 705 /* fallthrough if we don't have ssl */
577 case 'S': 706 case 'S':
578#ifdef HAVE_SSL 707#ifdef HAVE_SSL
579 flags |= FLAG_SSL; 708 config.use_tls = true;
580#else 709#else
581 die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); 710 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
582#endif 711#endif
583 break; 712 break;
584 case SNI_OPTION: 713 case SNI_OPTION:
585#ifdef HAVE_SSL 714#ifdef HAVE_SSL
586 flags |= FLAG_SSL; 715 config.use_tls = true;
587 sni_specified = true; 716 config.sni_specified = true;
588 sni = optarg; 717 config.sni = optarg;
589#else 718#else
590 die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); 719 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
591#endif 720#endif
592 break; 721 break;
593 case 'A': 722 case 'A':
594 match_flags |= NP_MATCH_ALL; 723 config.match_flags |= NP_MATCH_ALL;
724 break;
725 case output_format_index: {
726 parsed_output_format parser = mp_parse_output_format(optarg);
727 if (!parser.parsing_success) {
728 // TODO List all available formats here, maybe add anothoer usage function
729 printf("Invalid output format: %s\n", optarg);
730 exit(STATE_UNKNOWN);
731 }
732
733 config.output_format_set = true;
734 config.output_format = parser.output_format;
595 break; 735 break;
596 } 736 }
737 }
597 } 738 }
598 739
599 option_char = optind; 740 int index = optind;
600 if (!host_specified && option_char < argc) 741 if (!config.host_specified && index < argc) {
601 server_address = strdup(argv[option_char++]); 742 config.server_address = strdup(argv[index++]);
743 }
602 744
603 if (server_address == NULL) 745 if (config.server_address == NULL) {
604 usage4(_("You must provide a server address")); 746 usage4(_("You must provide a server address"));
605 else if (server_address[0] != '/' && !is_host(server_address)) 747 } else if (config.server_address[0] != '/' && !is_host(config.server_address)) {
606 die(STATE_CRITICAL, "%s %s - %s: %s\n", SERVICE, state_text(STATE_CRITICAL), _("Invalid hostname, address or socket"), 748 die(STATE_CRITICAL, "%s %s - %s: %s\n", config.service, state_text(STATE_CRITICAL),
607 server_address); 749 _("Invalid hostname, address or socket"), config.server_address);
750 }
608 751
609 return OK; 752 check_tcp_config_wrapper result = {
753 .config = config,
754 .errorcode = OK,
755 };
756 return result;
610} 757}
611 758
612void print_help(void) { 759void print_help(const char *service) {
613 print_revision(progname, NP_VERSION); 760 print_revision(progname, NP_VERSION);
614 761
615 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 762 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
616 printf(COPYRIGHT, copyright, email); 763 printf(COPYRIGHT, copyright, email);
617 764
618 printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"), SERVICE); 765 printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"),
766 service);
619 767
620 print_usage(); 768 print_usage();
621 769
@@ -627,7 +775,8 @@ void print_help(void) {
627 printf(UT_IPv46); 775 printf(UT_IPv46);
628 776
629 printf(" %s\n", "-E, --escape"); 777 printf(" %s\n", "-E, --escape");
630 printf(" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before send or quit option")); 778 printf(" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before "
779 "send or quit option"));
631 printf(" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit")); 780 printf(" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit"));
632 printf(" %s\n", "-s, --send=STRING"); 781 printf(" %s\n", "-s, --send=STRING");
633 printf(" %s\n", _("String to send to the server")); 782 printf(" %s\n", _("String to send to the server"));
@@ -640,7 +789,8 @@ void print_help(void) {
640 printf(" %s\n", "-r, --refuse=ok|warn|crit"); 789 printf(" %s\n", "-r, --refuse=ok|warn|crit");
641 printf(" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)")); 790 printf(" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)"));
642 printf(" %s\n", "-M, --mismatch=ok|warn|crit"); 791 printf(" %s\n", "-M, --mismatch=ok|warn|crit");
643 printf(" %s\n", _("Accept expected string mismatches with states ok, warn, crit (default: warn)")); 792 printf(" %s\n",
793 _("Accept expected string mismatches with states ok, warn, crit (default: warn)"));
644 printf(" %s\n", "-j, --jail"); 794 printf(" %s\n", "-j, --jail");
645 printf(" %s\n", _("Hide output from TCP socket")); 795 printf(" %s\n", _("Hide output from TCP socket"));
646 printf(" %s\n", "-m, --maxbytes=INTEGER"); 796 printf(" %s\n", "-m, --maxbytes=INTEGER");
@@ -662,6 +812,7 @@ void print_help(void) {
662 812
663 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 813 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
664 814
815 printf(UT_OUTPUT_FORMAT);
665 printf(UT_VERBOSE); 816 printf(UT_VERBOSE);
666 817
667 printf(UT_SUPPORT); 818 printf(UT_SUPPORT);
@@ -669,7 +820,8 @@ void print_help(void) {
669 820
670void print_usage(void) { 821void print_usage(void) {
671 printf("%s\n", _("Usage:")); 822 printf("%s\n", _("Usage:"));
672 printf("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n", progname); 823 printf("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n",
824 progname);
673 printf("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n"); 825 printf("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n");
674 printf("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n"); 826 printf("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n");
675 printf("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n"); 827 printf("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n");
diff --git a/plugins/check_tcp.d/config.h b/plugins/check_tcp.d/config.h
new file mode 100644
index 00000000..dc25d79e
--- /dev/null
+++ b/plugins/check_tcp.d/config.h
@@ -0,0 +1,84 @@
1#pragma once
2
3#include "../../lib/utils_tcp.h"
4#include "output.h"
5#include "states.h"
6#include <netinet/in.h>
7
8typedef struct {
9 char *server_address;
10 bool host_specified;
11 int server_port; // TODO can this be a uint16?
12
13 int protocol; /* most common is default */
14 char *service;
15 char *send;
16 char *quit;
17 char **server_expect;
18 size_t server_expect_count;
19 bool use_tls;
20#ifdef HAVE_SSL
21 char *sni;
22 bool sni_specified;
23 bool check_cert;
24 int days_till_exp_warn;
25 int days_till_exp_crit;
26#endif // HAVE_SSL
27 int match_flags;
28 mp_state_enum expect_mismatch_state;
29 unsigned int delay;
30
31 bool warning_time_set;
32 double warning_time;
33 bool critical_time_set;
34 double critical_time;
35
36 mp_state_enum econn_refuse_state;
37
38 ssize_t maxbytes;
39
40 bool hide_output;
41
42 bool output_format_set;
43 mp_output_format output_format;
44} check_tcp_config;
45
46check_tcp_config check_tcp_config_init() {
47 check_tcp_config result = {
48 .server_address = "127.0.0.1",
49 .host_specified = false,
50 .server_port = 0,
51
52 .protocol = IPPROTO_TCP,
53 .service = "TCP",
54 .send = NULL,
55 .quit = NULL,
56 .server_expect = NULL,
57 .server_expect_count = 0,
58 .use_tls = false,
59#ifdef HAVE_SSL
60 .sni = NULL,
61 .sni_specified = false,
62 .check_cert = false,
63 .days_till_exp_warn = 0,
64 .days_till_exp_crit = 0,
65#endif // HAVE_SSL
66 .match_flags = NP_MATCH_EXACT,
67 .expect_mismatch_state = STATE_WARNING,
68 .delay = 0,
69
70 .warning_time_set = false,
71 .warning_time = 0,
72 .critical_time_set = false,
73 .critical_time = 0,
74
75 .econn_refuse_state = STATE_CRITICAL,
76
77 .maxbytes = 0,
78
79 .hide_output = false,
80
81 .output_format_set = false,
82 };
83 return result;
84}
diff --git a/plugins/check_time.c b/plugins/check_time.c
index d1f50683..fc9ba3f9 100644
--- a/plugins/check_time.c
+++ b/plugins/check_time.c
@@ -28,6 +28,7 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
31const char *progname = "check_time"; 32const char *progname = "check_time";
32const char *copyright = "1999-2024"; 33const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
@@ -35,28 +36,15 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 36#include "common.h"
36#include "netutils.h" 37#include "netutils.h"
37#include "utils.h" 38#include "utils.h"
38 39#include "check_time.d/config.h"
39enum {
40 TIME_PORT = 37
41};
42 40
43#define UNIX_EPOCH 2208988800UL 41#define UNIX_EPOCH 2208988800UL
44 42
45static uint32_t raw_server_time; 43typedef struct {
46static unsigned long server_time, diff_time; 44 int errorcode;
47static int warning_time = 0; 45 check_time_config config;
48static bool check_warning_time = false; 46} check_time_config_wrapper;
49static int critical_time = 0; 47static check_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50static bool check_critical_time = false;
51static unsigned long warning_diff = 0;
52static bool check_warning_diff = false;
53static unsigned long critical_diff = 0;
54static bool check_critical_diff = false;
55static int server_port = TIME_PORT;
56static char *server_address = NULL;
57static bool use_udp = false;
58
59static int process_arguments(int, char **);
60static void print_help(void); 48static void print_help(void);
61void print_usage(void); 49void print_usage(void);
62 50
@@ -68,8 +56,12 @@ int main(int argc, char **argv) {
68 /* Parse extra opts if any */ 56 /* Parse extra opts if any */
69 argv = np_extra_opts(&argc, argv, progname); 57 argv = np_extra_opts(&argc, argv, progname);
70 58
71 if (process_arguments(argc, argv) == ERROR) 59 check_time_config_wrapper tmp_config = process_arguments(argc, argv);
60 if (tmp_config.errorcode == ERROR) {
72 usage4(_("Could not parse arguments")); 61 usage4(_("Could not parse arguments"));
62 }
63
64 const check_time_config config = tmp_config.config;
73 65
74 /* initialize alarm signal handling */ 66 /* initialize alarm signal handling */
75 signal(SIGALRM, socket_timeout_alarm_handler); 67 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -79,37 +71,42 @@ int main(int argc, char **argv) {
79 time(&start_time); 71 time(&start_time);
80 72
81 int socket; 73 int socket;
82 int result = STATE_UNKNOWN; 74 mp_state_enum result = STATE_UNKNOWN;
83 /* try to connect to the host at the given port number */ 75 /* try to connect to the host at the given port number */
84 if (use_udp) { 76 if (config.use_udp) {
85 result = my_udp_connect(server_address, server_port, &socket); 77 result = my_udp_connect(config.server_address, config.server_port, &socket);
86 } else { 78 } else {
87 result = my_tcp_connect(server_address, server_port, &socket); 79 result = my_tcp_connect(config.server_address, config.server_port, &socket);
88 } 80 }
89 81
90 if (result != STATE_OK) { 82 if (result != STATE_OK) {
91 if (check_critical_time) 83 if (config.check_critical_time) {
92 result = STATE_CRITICAL; 84 result = STATE_CRITICAL;
93 else if (check_warning_time) 85 } else if (config.check_warning_time) {
94 result = STATE_WARNING; 86 result = STATE_WARNING;
95 else 87 } else {
96 result = STATE_UNKNOWN; 88 result = STATE_UNKNOWN;
97 die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"), server_address, server_port); 89 }
90 die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"),
91 config.server_address, config.server_port);
98 } 92 }
99 93
100 if (use_udp) { 94 if (config.use_udp) {
101 if (send(socket, "", 0, 0) < 0) { 95 if (send(socket, "", 0, 0) < 0) {
102 if (check_critical_time) 96 if (config.check_critical_time) {
103 result = STATE_CRITICAL; 97 result = STATE_CRITICAL;
104 else if (check_warning_time) 98 } else if (config.check_warning_time) {
105 result = STATE_WARNING; 99 result = STATE_WARNING;
106 else 100 } else {
107 result = STATE_UNKNOWN; 101 result = STATE_UNKNOWN;
108 die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"), server_address, server_port); 102 }
103 die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"),
104 config.server_address, config.server_port);
109 } 105 }
110 } 106 }
111 107
112 /* watch for the connection string */ 108 /* watch for the connection string */
109 uint32_t raw_server_time;
113 result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0); 110 result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0);
114 111
115 /* close the connection */ 112 /* close the connection */
@@ -121,48 +118,59 @@ int main(int argc, char **argv) {
121 118
122 /* return a WARNING status if we couldn't read any data */ 119 /* return a WARNING status if we couldn't read any data */
123 if (result <= 0) { 120 if (result <= 0) {
124 if (check_critical_time) 121 if (config.check_critical_time) {
125 result = STATE_CRITICAL; 122 result = STATE_CRITICAL;
126 else if (check_warning_time) 123 } else if (config.check_warning_time) {
127 result = STATE_WARNING; 124 result = STATE_WARNING;
128 else 125 } else {
129 result = STATE_UNKNOWN; 126 result = STATE_UNKNOWN;
130 die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"), server_address, server_port); 127 }
128 die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"),
129 config.server_address, config.server_port);
131 } 130 }
132 131
133 result = STATE_OK; 132 result = STATE_OK;
134 133
135 time_t conntime = (end_time - start_time); 134 time_t conntime = (end_time - start_time);
136 if (check_critical_time && conntime > critical_time) 135 if (config.check_critical_time && conntime > config.critical_time) {
137 result = STATE_CRITICAL; 136 result = STATE_CRITICAL;
138 else if (check_warning_time && conntime > warning_time) 137 } else if (config.check_warning_time && conntime > config.warning_time) {
139 result = STATE_WARNING; 138 result = STATE_WARNING;
139 }
140 140
141 if (result != STATE_OK) 141 if (result != STATE_OK) {
142 die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime, 142 die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime,
143 perfdata("time", (long)conntime, "s", check_warning_time, (long)warning_time, check_critical_time, (long)critical_time, true, 0, 143 perfdata("time", (long)conntime, "s", config.check_warning_time,
144 false, 0)); 144 (long)config.warning_time, config.check_critical_time,
145 (long)config.critical_time, true, 0, false, 0));
146 }
145 147
148 unsigned long server_time;
149 unsigned long diff_time;
146 server_time = ntohl(raw_server_time) - UNIX_EPOCH; 150 server_time = ntohl(raw_server_time) - UNIX_EPOCH;
147 if (server_time > (unsigned long)end_time) 151 if (server_time > (unsigned long)end_time) {
148 diff_time = server_time - (unsigned long)end_time; 152 diff_time = server_time - (unsigned long)end_time;
149 else 153 } else {
150 diff_time = (unsigned long)end_time - server_time; 154 diff_time = (unsigned long)end_time - server_time;
155 }
151 156
152 if (check_critical_diff && diff_time > critical_diff) 157 if (config.check_critical_diff && diff_time > config.critical_diff) {
153 result = STATE_CRITICAL; 158 result = STATE_CRITICAL;
154 else if (check_warning_diff && diff_time > warning_diff) 159 } else if (config.check_warning_diff && diff_time > config.warning_diff) {
155 result = STATE_WARNING; 160 result = STATE_WARNING;
161 }
156 162
157 printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time, 163 printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time,
158 perfdata("time", (long)conntime, "s", check_warning_time, (long)warning_time, check_critical_time, (long)critical_time, true, 0, 164 perfdata("time", (long)conntime, "s", config.check_warning_time,
159 false, 0), 165 (long)config.warning_time, config.check_critical_time,
160 perfdata("offset", diff_time, "s", check_warning_diff, warning_diff, check_critical_diff, critical_diff, true, 0, false, 0)); 166 (long)config.critical_time, true, 0, false, 0),
167 perfdata("offset", diff_time, "s", config.check_warning_diff, config.warning_diff,
168 config.check_critical_diff, config.critical_diff, true, 0, false, 0));
161 return result; 169 return result;
162} 170}
163 171
164/* process command-line arguments */ 172/* process command-line arguments */
165int process_arguments(int argc, char **argv) { 173check_time_config_wrapper process_arguments(int argc, char **argv) {
166 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 174 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
167 {"warning-variance", required_argument, 0, 'w'}, 175 {"warning-variance", required_argument, 0, 'w'},
168 {"critical-variance", required_argument, 0, 'c'}, 176 {"critical-variance", required_argument, 0, 'c'},
@@ -175,29 +183,37 @@ int process_arguments(int argc, char **argv) {
175 {"help", no_argument, 0, 'h'}, 183 {"help", no_argument, 0, 'h'},
176 {0, 0, 0, 0}}; 184 {0, 0, 0, 0}};
177 185
178 if (argc < 2) 186 if (argc < 2) {
179 usage("\n"); 187 usage("\n");
188 }
180 189
181 for (int i = 1; i < argc; i++) { 190 for (int i = 1; i < argc; i++) {
182 if (strcmp("-to", argv[i]) == 0) 191 if (strcmp("-to", argv[i]) == 0) {
183 strcpy(argv[i], "-t"); 192 strcpy(argv[i], "-t");
184 else if (strcmp("-wd", argv[i]) == 0) 193 } else if (strcmp("-wd", argv[i]) == 0) {
185 strcpy(argv[i], "-w"); 194 strcpy(argv[i], "-w");
186 else if (strcmp("-cd", argv[i]) == 0) 195 } else if (strcmp("-cd", argv[i]) == 0) {
187 strcpy(argv[i], "-c"); 196 strcpy(argv[i], "-c");
188 else if (strcmp("-wt", argv[i]) == 0) 197 } else if (strcmp("-wt", argv[i]) == 0) {
189 strcpy(argv[i], "-W"); 198 strcpy(argv[i], "-W");
190 else if (strcmp("-ct", argv[i]) == 0) 199 } else if (strcmp("-ct", argv[i]) == 0) {
191 strcpy(argv[i], "-C"); 200 strcpy(argv[i], "-C");
201 }
192 } 202 }
193 203
204 check_time_config_wrapper result = {
205 .errorcode = OK,
206 .config = check_time_config_init(),
207 };
208
194 int option_char; 209 int option_char;
195 while (true) { 210 while (true) {
196 int option = 0; 211 int option = 0;
197 option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option); 212 option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option);
198 213
199 if (option_char == -1 || option_char == EOF) 214 if (option_char == -1 || option_char == EOF) {
200 break; 215 break;
216 }
201 217
202 switch (option_char) { 218 switch (option_char) {
203 case '?': /* print short usage statement if args not parsable */ 219 case '?': /* print short usage statement if args not parsable */
@@ -209,18 +225,20 @@ int process_arguments(int argc, char **argv) {
209 print_revision(progname, NP_VERSION); 225 print_revision(progname, NP_VERSION);
210 exit(STATE_UNKNOWN); 226 exit(STATE_UNKNOWN);
211 case 'H': /* hostname */ 227 case 'H': /* hostname */
212 if (!is_host(optarg)) 228 if (!is_host(optarg)) {
213 usage2(_("Invalid hostname/address"), optarg); 229 usage2(_("Invalid hostname/address"), optarg);
214 server_address = optarg; 230 }
231 result.config.server_address = optarg;
215 break; 232 break;
216 case 'w': /* warning-variance */ 233 case 'w': /* warning-variance */
217 if (is_intnonneg(optarg)) { 234 if (is_intnonneg(optarg)) {
218 warning_diff = strtoul(optarg, NULL, 10); 235 result.config.warning_diff = strtoul(optarg, NULL, 10);
219 check_warning_diff = true; 236 result.config.check_warning_diff = true;
220 } else if (strspn(optarg, "0123456789:,") > 0) { 237 } else if (strspn(optarg, "0123456789:,") > 0) {
221 if (sscanf(optarg, "%lu%*[:,]%d", &warning_diff, &warning_time) == 2) { 238 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.warning_diff,
222 check_warning_diff = true; 239 &result.config.warning_time) == 2) {
223 check_warning_time = true; 240 result.config.check_warning_diff = true;
241 result.config.check_warning_time = true;
224 } else { 242 } else {
225 usage4(_("Warning thresholds must be a positive integer")); 243 usage4(_("Warning thresholds must be a positive integer"));
226 } 244 }
@@ -230,12 +248,13 @@ int process_arguments(int argc, char **argv) {
230 break; 248 break;
231 case 'c': /* critical-variance */ 249 case 'c': /* critical-variance */
232 if (is_intnonneg(optarg)) { 250 if (is_intnonneg(optarg)) {
233 critical_diff = strtoul(optarg, NULL, 10); 251 result.config.critical_diff = strtoul(optarg, NULL, 10);
234 check_critical_diff = true; 252 result.config.check_critical_diff = true;
235 } else if (strspn(optarg, "0123456789:,") > 0) { 253 } else if (strspn(optarg, "0123456789:,") > 0) {
236 if (sscanf(optarg, "%lu%*[:,]%d", &critical_diff, &critical_time) == 2) { 254 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.critical_diff,
237 check_critical_diff = true; 255 &result.config.critical_time) == 2) {
238 check_critical_time = true; 256 result.config.check_critical_diff = true;
257 result.config.check_critical_time = true;
239 } else { 258 } else {
240 usage4(_("Critical thresholds must be a positive integer")); 259 usage4(_("Critical thresholds must be a positive integer"));
241 } 260 }
@@ -244,48 +263,53 @@ int process_arguments(int argc, char **argv) {
244 } 263 }
245 break; 264 break;
246 case 'W': /* warning-connect */ 265 case 'W': /* warning-connect */
247 if (!is_intnonneg(optarg)) 266 if (!is_intnonneg(optarg)) {
248 usage4(_("Warning threshold must be a positive integer")); 267 usage4(_("Warning threshold must be a positive integer"));
249 else 268 } else {
250 warning_time = atoi(optarg); 269 result.config.warning_time = atoi(optarg);
251 check_warning_time = true; 270 }
271 result.config.check_warning_time = true;
252 break; 272 break;
253 case 'C': /* critical-connect */ 273 case 'C': /* critical-connect */
254 if (!is_intnonneg(optarg)) 274 if (!is_intnonneg(optarg)) {
255 usage4(_("Critical threshold must be a positive integer")); 275 usage4(_("Critical threshold must be a positive integer"));
256 else 276 } else {
257 critical_time = atoi(optarg); 277 result.config.critical_time = atoi(optarg);
258 check_critical_time = true; 278 }
279 result.config.check_critical_time = true;
259 break; 280 break;
260 case 'p': /* port */ 281 case 'p': /* port */
261 if (!is_intnonneg(optarg)) 282 if (!is_intnonneg(optarg)) {
262 usage4(_("Port must be a positive integer")); 283 usage4(_("Port must be a positive integer"));
263 else 284 } else {
264 server_port = atoi(optarg); 285 result.config.server_port = atoi(optarg);
286 }
265 break; 287 break;
266 case 't': /* timeout */ 288 case 't': /* timeout */
267 if (!is_intnonneg(optarg)) 289 if (!is_intnonneg(optarg)) {
268 usage2(_("Timeout interval must be a positive integer"), optarg); 290 usage2(_("Timeout interval must be a positive integer"), optarg);
269 else 291 } else {
270 socket_timeout = atoi(optarg); 292 socket_timeout = atoi(optarg);
293 }
271 break; 294 break;
272 case 'u': /* udp */ 295 case 'u': /* udp */
273 use_udp = true; 296 result.config.use_udp = true;
274 } 297 }
275 } 298 }
276 299
277 option_char = optind; 300 option_char = optind;
278 if (server_address == NULL) { 301 if (result.config.server_address == NULL) {
279 if (argc > option_char) { 302 if (argc > option_char) {
280 if (!is_host(argv[option_char])) 303 if (!is_host(argv[option_char])) {
281 usage2(_("Invalid hostname/address"), optarg); 304 usage2(_("Invalid hostname/address"), optarg);
282 server_address = argv[option_char]; 305 }
306 result.config.server_address = argv[option_char];
283 } else { 307 } else {
284 usage4(_("Hostname was not supplied")); 308 usage4(_("Hostname was not supplied"));
285 } 309 }
286 } 310 }
287 311
288 return OK; 312 return result;
289} 313}
290 314
291void print_help(void) { 315void print_help(void) {
diff --git a/plugins/check_time.d/config.h b/plugins/check_time.d/config.h
new file mode 100644
index 00000000..09bd7c45
--- /dev/null
+++ b/plugins/check_time.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 TIME_PORT = 37
8};
9
10typedef struct {
11 char *server_address;
12 int server_port;
13 bool use_udp;
14
15 int warning_time;
16 bool check_warning_time;
17 int critical_time;
18 bool check_critical_time;
19 unsigned long warning_diff;
20 bool check_warning_diff;
21 unsigned long critical_diff;
22 bool check_critical_diff;
23} check_time_config;
24
25check_time_config check_time_config_init() {
26 check_time_config tmp = {
27 .server_address = NULL,
28 .server_port = TIME_PORT,
29 .use_udp = false,
30
31 .warning_time = 0,
32 .check_warning_time = false,
33 .critical_time = 0,
34 .check_critical_time = false,
35
36 .warning_diff = 0,
37 .check_warning_diff = false,
38 .critical_diff = 0,
39 .check_critical_diff = false,
40 };
41 return tmp;
42}
diff --git a/plugins/check_ups.c b/plugins/check_ups.c
index 526a29df..54decce3 100644
--- a/plugins/check_ups.c
+++ b/plugins/check_ups.c
@@ -39,69 +39,29 @@ const char *email = "devel@monitoring-plugins.org";
39#include "common.h" 39#include "common.h"
40#include "netutils.h" 40#include "netutils.h"
41#include "utils.h" 41#include "utils.h"
42 42#include "check_ups.d/config.h"
43enum { 43#include "states.h"
44 PORT = 3493
45};
46
47#define UPS_NONE 0 /* no supported options */
48#define UPS_UTILITY 1 /* supports utility line */
49#define UPS_BATTPCT 2 /* supports percent battery remaining */
50#define UPS_STATUS 4 /* supports UPS status */
51#define UPS_TEMP 8 /* supports UPS temperature */
52#define UPS_LOADPCT 16 /* supports load percent */
53#define UPS_REALPOWER 32 /* supports real power */
54
55#define UPSSTATUS_NONE 0
56#define UPSSTATUS_OFF 1
57#define UPSSTATUS_OL 2
58#define UPSSTATUS_OB 4
59#define UPSSTATUS_LB 8
60#define UPSSTATUS_CAL 16
61#define UPSSTATUS_RB 32 /*Replace Battery */
62#define UPSSTATUS_BYPASS 64
63#define UPSSTATUS_OVER 128
64#define UPSSTATUS_TRIM 256
65#define UPSSTATUS_BOOST 512
66#define UPSSTATUS_CHRG 1024
67#define UPSSTATUS_DISCHRG 2048
68#define UPSSTATUS_UNKNOWN 4096
69#define UPSSTATUS_ALARM 8192
70 44
71enum { 45enum {
72 NOSUCHVAR = ERROR - 1 46 NOSUCHVAR = ERROR - 1
73}; 47};
74 48
75typedef struct ups_config {
76 unsigned int server_port;
77 char *server_address;
78 char *ups_name;
79 double warning_value;
80 double critical_value;
81 bool check_warn;
82 bool check_crit;
83 int check_variable;
84 int status;
85 bool temp_output_c;
86} ups_config;
87
88ups_config ups_config_init(void) {
89 ups_config tmp = {0};
90 tmp.server_port = PORT;
91 tmp.server_address = NULL;
92 tmp.ups_name = NULL;
93 tmp.check_variable = UPS_NONE;
94 tmp.status = UPSSTATUS_NONE;
95
96 return tmp;
97}
98
99// Forward declarations 49// Forward declarations
100static int determine_status(ups_config * /*config*/, int *supported_options); 50typedef struct {
101static int get_ups_variable(const char * /*varname*/, char * /*buf*/, ups_config config); 51 int errorcode;
52 int ups_status;
53 int supported_options;
54} determine_status_result;
55static determine_status_result determine_status(check_ups_config /*config*/);
56static int get_ups_variable(const char * /*varname*/, char * /*buf*/, check_ups_config config);
57
58typedef struct {
59 int errorcode;
60 check_ups_config config;
61} check_ups_config_wrapper;
62static check_ups_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
63static check_ups_config_wrapper validate_arguments(check_ups_config_wrapper /*config_wrapper*/);
102 64
103static int process_arguments(int /*argc*/, char ** /*argv*/, ups_config * /*config*/);
104static int validate_arguments(ups_config /*config*/);
105static void print_help(void); 65static void print_help(void);
106void print_usage(void); 66void print_usage(void);
107 67
@@ -109,28 +69,16 @@ int main(int argc, char **argv) {
109 setlocale(LC_ALL, ""); 69 setlocale(LC_ALL, "");
110 bindtextdomain(PACKAGE, LOCALEDIR); 70 bindtextdomain(PACKAGE, LOCALEDIR);
111 textdomain(PACKAGE); 71 textdomain(PACKAGE);
112
113 char *ups_status;
114 ups_status = strdup("N/A");
115
116 char *data;
117 data = strdup("");
118
119 char *message;
120 message = strdup("");
121
122 // Exit result
123 int result = STATE_UNKNOWN;
124
125 /* Parse extra opts if any */ 72 /* Parse extra opts if any */
126 argv = np_extra_opts(&argc, argv, progname); 73 argv = np_extra_opts(&argc, argv, progname);
127 74
128 // Config from commandline 75 check_ups_config_wrapper tmp_config = process_arguments(argc, argv);
129 ups_config config = ups_config_init();
130 76
131 if (process_arguments(argc, argv, &config) == ERROR) { 77 if (tmp_config.errorcode == ERROR) {
132 usage4(_("Could not parse arguments")); 78 usage4(_("Could not parse arguments"));
133 } 79 }
80 // Config from commandline
81 check_ups_config config = tmp_config.config;
134 82
135 /* initialize alarm signal handling */ 83 /* initialize alarm signal handling */
136 signal(SIGALRM, socket_timeout_alarm_handler); 84 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -138,71 +86,76 @@ int main(int argc, char **argv) {
138 /* set socket timeout */ 86 /* set socket timeout */
139 alarm(socket_timeout); 87 alarm(socket_timeout);
140 88
141 int supported_options = UPS_NONE;
142
143 /* get the ups status if possible */ 89 /* get the ups status if possible */
144 if (determine_status(&config, &supported_options) != OK) { 90 determine_status_result query_result = determine_status(config);
91 if (query_result.errorcode != OK) {
145 return STATE_CRITICAL; 92 return STATE_CRITICAL;
146 } 93 }
147 94
148 if (supported_options & UPS_STATUS) { 95 int ups_status_flags = query_result.ups_status;
96 int supported_options = query_result.supported_options;
149 97
150 ups_status = strdup(""); 98 // Exit result
99 mp_state_enum result = STATE_UNKNOWN;
100 char *message = NULL;
151 101
102 if (supported_options & UPS_STATUS) {
103 char *ups_status = strdup("");
152 result = STATE_OK; 104 result = STATE_OK;
153 105
154 if (config.status & UPSSTATUS_OFF) { 106 if (ups_status_flags & UPSSTATUS_OFF) {
155 xasprintf(&ups_status, "Off"); 107 xasprintf(&ups_status, "Off");
156 result = STATE_CRITICAL; 108 result = STATE_CRITICAL;
157 } else if ((config.status & (UPSSTATUS_OB | UPSSTATUS_LB)) == (UPSSTATUS_OB | UPSSTATUS_LB)) { 109 } else if ((ups_status_flags & (UPSSTATUS_OB | UPSSTATUS_LB)) ==
110 (UPSSTATUS_OB | UPSSTATUS_LB)) {
158 xasprintf(&ups_status, _("On Battery, Low Battery")); 111 xasprintf(&ups_status, _("On Battery, Low Battery"));
159 result = STATE_CRITICAL; 112 result = STATE_CRITICAL;
160 } else { 113 } else {
161 if (config.status & UPSSTATUS_OL) { 114 if (ups_status_flags & UPSSTATUS_OL) {
162 xasprintf(&ups_status, "%s%s", ups_status, _("Online")); 115 xasprintf(&ups_status, "%s%s", ups_status, _("Online"));
163 } 116 }
164 if (config.status & UPSSTATUS_OB) { 117 if (ups_status_flags & UPSSTATUS_OB) {
165 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery")); 118 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery"));
166 result = max_state(result, STATE_WARNING); 119 result = max_state(result, STATE_WARNING);
167 } 120 }
168 if (config.status & UPSSTATUS_LB) { 121 if (ups_status_flags & UPSSTATUS_LB) {
169 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery")); 122 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery"));
170 result = max_state(result, STATE_WARNING); 123 result = max_state(result, STATE_WARNING);
171 } 124 }
172 if (config.status & UPSSTATUS_CAL) { 125 if (ups_status_flags & UPSSTATUS_CAL) {
173 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating")); 126 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating"));
174 } 127 }
175 if (config.status & UPSSTATUS_RB) { 128 if (ups_status_flags & UPSSTATUS_RB) {
176 xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery")); 129 xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery"));
177 result = max_state(result, STATE_WARNING); 130 result = max_state(result, STATE_WARNING);
178 } 131 }
179 if (config.status & UPSSTATUS_BYPASS) { 132 if (ups_status_flags & UPSSTATUS_BYPASS) {
180 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass")); 133 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass"));
181 // Bypassing the battery is likely a bad thing 134 // Bypassing the battery is likely a bad thing
182 result = STATE_CRITICAL; 135 result = STATE_CRITICAL;
183 } 136 }
184 if (config.status & UPSSTATUS_OVER) { 137 if (ups_status_flags & UPSSTATUS_OVER) {
185 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload")); 138 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload"));
186 result = max_state(result, STATE_WARNING); 139 result = max_state(result, STATE_WARNING);
187 } 140 }
188 if (config.status & UPSSTATUS_TRIM) { 141 if (ups_status_flags & UPSSTATUS_TRIM) {
189 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming")); 142 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming"));
190 } 143 }
191 if (config.status & UPSSTATUS_BOOST) { 144 if (ups_status_flags & UPSSTATUS_BOOST) {
192 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting")); 145 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting"));
193 } 146 }
194 if (config.status & UPSSTATUS_CHRG) { 147 if (ups_status_flags & UPSSTATUS_CHRG) {
195 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging")); 148 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging"));
196 } 149 }
197 if (config.status & UPSSTATUS_DISCHRG) { 150 if (ups_status_flags & UPSSTATUS_DISCHRG) {
198 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging")); 151 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging"));
199 result = max_state(result, STATE_WARNING); 152 result = max_state(result, STATE_WARNING);
200 } 153 }
201 if (config.status & UPSSTATUS_ALARM) { 154 if (ups_status_flags & UPSSTATUS_ALARM) {
202 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM")); 155 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM"));
203 result = STATE_CRITICAL; 156 result = STATE_CRITICAL;
204 } 157 }
205 if (config.status & UPSSTATUS_UNKNOWN) { 158 if (ups_status_flags & UPSSTATUS_UNKNOWN) {
206 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown")); 159 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown"));
207 } 160 }
208 } 161 }
@@ -211,7 +164,7 @@ int main(int argc, char **argv) {
211 164
212 int res; 165 int res;
213 char temp_buffer[MAX_INPUT_BUFFER]; 166 char temp_buffer[MAX_INPUT_BUFFER];
214 167 char *performance_data = strdup("");
215 /* get the ups utility voltage if possible */ 168 /* get the ups utility voltage if possible */
216 res = get_ups_variable("input.voltage", temp_buffer, config); 169 res = get_ups_variable("input.voltage", temp_buffer, config);
217 if (res == NOSUCHVAR) { 170 if (res == NOSUCHVAR) {
@@ -239,11 +192,15 @@ int main(int argc, char **argv) {
239 } else if (config.check_warn && ups_utility_deviation >= config.warning_value) { 192 } else if (config.check_warn && ups_utility_deviation >= config.warning_value) {
240 result = max_state(result, STATE_WARNING); 193 result = max_state(result, STATE_WARNING);
241 } 194 }
242 xasprintf(&data, "%s", 195 xasprintf(&performance_data, "%s",
243 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", config.check_warn, (long)(1000 * config.warning_value), 196 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV",
244 config.check_crit, (long)(1000 * config.critical_value), true, 0, false, 0)); 197 config.check_warn, (long)(1000 * config.warning_value),
198 config.check_crit, (long)(1000 * config.critical_value), true, 0,
199 false, 0));
245 } else { 200 } else {
246 xasprintf(&data, "%s", perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false, 0, true, 0, false, 0)); 201 xasprintf(&performance_data, "%s",
202 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false,
203 0, true, 0, false, 0));
247 } 204 }
248 } 205 }
249 206
@@ -266,11 +223,14 @@ int main(int argc, char **argv) {
266 } else if (config.check_warn && ups_battery_percent <= config.warning_value) { 223 } else if (config.check_warn && ups_battery_percent <= config.warning_value) {
267 result = max_state(result, STATE_WARNING); 224 result = max_state(result, STATE_WARNING);
268 } 225 }
269 xasprintf(&data, "%s %s", data, 226 xasprintf(&performance_data, "%s %s", performance_data,
270 perfdata("battery", (long)ups_battery_percent, "%", config.check_warn, (long)(config.warning_value), 227 perfdata("battery", (long)ups_battery_percent, "%", config.check_warn,
271 config.check_crit, (long)(config.critical_value), true, 0, true, 100)); 228 (long)(config.warning_value), config.check_crit,
229 (long)(config.critical_value), true, 0, true, 100));
272 } else { 230 } else {
273 xasprintf(&data, "%s %s", data, perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true, 0, true, 100)); 231 xasprintf(&performance_data, "%s %s", performance_data,
232 perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true,
233 0, true, 100));
274 } 234 }
275 } 235 }
276 236
@@ -293,11 +253,14 @@ int main(int argc, char **argv) {
293 } else if (config.check_warn && ups_load_percent >= config.warning_value) { 253 } else if (config.check_warn && ups_load_percent >= config.warning_value) {
294 result = max_state(result, STATE_WARNING); 254 result = max_state(result, STATE_WARNING);
295 } 255 }
296 xasprintf(&data, "%s %s", data, 256 xasprintf(&performance_data, "%s %s", performance_data,
297 perfdata("load", (long)ups_load_percent, "%", config.check_warn, (long)(config.warning_value), config.check_crit, 257 perfdata("load", (long)ups_load_percent, "%", config.check_warn,
258 (long)(config.warning_value), config.check_crit,
298 (long)(config.critical_value), true, 0, true, 100)); 259 (long)(config.critical_value), true, 0, true, 100));
299 } else { 260 } else {
300 xasprintf(&data, "%s %s", data, perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0, true, 100)); 261 xasprintf(&performance_data, "%s %s", performance_data,
262 perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0,
263 true, 100));
301 } 264 }
302 } 265 }
303 266
@@ -329,11 +292,14 @@ int main(int argc, char **argv) {
329 } else if (config.check_warn && ups_temperature >= config.warning_value) { 292 } else if (config.check_warn && ups_temperature >= config.warning_value) {
330 result = max_state(result, STATE_WARNING); 293 result = max_state(result, STATE_WARNING);
331 } 294 }
332 xasprintf(&data, "%s %s", data, 295 xasprintf(&performance_data, "%s %s", performance_data,
333 perfdata("temp", (long)ups_temperature, tunits, config.check_warn, (long)(config.warning_value), config.check_crit, 296 perfdata("temp", (long)ups_temperature, tunits, config.check_warn,
297 (long)(config.warning_value), config.check_crit,
334 (long)(config.critical_value), true, 0, false, 0)); 298 (long)(config.critical_value), true, 0, false, 0));
335 } else { 299 } else {
336 xasprintf(&data, "%s %s", data, perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0, false, 0)); 300 xasprintf(&performance_data, "%s %s", performance_data,
301 perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0,
302 false, 0));
337 } 303 }
338 } 304 }
339 305
@@ -355,11 +321,14 @@ int main(int argc, char **argv) {
355 } else if (config.check_warn && ups_realpower >= config.warning_value) { 321 } else if (config.check_warn && ups_realpower >= config.warning_value) {
356 result = max_state(result, STATE_WARNING); 322 result = max_state(result, STATE_WARNING);
357 } 323 }
358 xasprintf(&data, "%s %s", data, 324 xasprintf(&performance_data, "%s %s", performance_data,
359 perfdata("realpower", (long)ups_realpower, "W", config.check_warn, (long)(config.warning_value), config.check_crit, 325 perfdata("realpower", (long)ups_realpower, "W", config.check_warn,
326 (long)(config.warning_value), config.check_crit,
360 (long)(config.critical_value), true, 0, false, 0)); 327 (long)(config.critical_value), true, 0, false, 0));
361 } else { 328 } else {
362 xasprintf(&data, "%s %s", data, perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0, false, 0)); 329 xasprintf(&performance_data, "%s %s", performance_data,
330 perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0,
331 false, 0));
363 } 332 }
364 } 333 }
365 334
@@ -373,71 +342,79 @@ int main(int argc, char **argv) {
373 /* reset timeout */ 342 /* reset timeout */
374 alarm(0); 343 alarm(0);
375 344
376 printf("UPS %s - %s|%s\n", state_text(result), message, data); 345 printf("UPS %s - %s|%s\n", state_text(result), message, performance_data);
377 return result; 346 exit(result);
378} 347}
379 348
380/* determines what options are supported by the UPS */ 349/* determines what options are supported by the UPS */
381int determine_status(ups_config *config, int *supported_options) { 350determine_status_result determine_status(const check_ups_config config) {
382 char recv_buffer[MAX_INPUT_BUFFER];
383 351
384 int res = get_ups_variable("ups.status", recv_buffer, *config); 352 determine_status_result result = {
353 .errorcode = OK,
354 .ups_status = UPSSTATUS_NONE,
355 .supported_options = 0,
356 };
357
358 char recv_buffer[MAX_INPUT_BUFFER];
359 int res = get_ups_variable("ups.status", recv_buffer, config);
385 if (res == NOSUCHVAR) { 360 if (res == NOSUCHVAR) {
386 return OK; 361 return result;
387 } 362 }
388 363
389 if (res != STATE_OK) { 364 if (res != STATE_OK) {
390 printf("%s\n", _("Invalid response received from host")); 365 printf("%s\n", _("Invalid response received from host"));
391 return ERROR; 366 result.errorcode = ERROR;
367 return result;
392 } 368 }
393 369
394 *supported_options |= UPS_STATUS; 370 result.supported_options |= UPS_STATUS;
395 371
396 char temp_buffer[MAX_INPUT_BUFFER]; 372 char temp_buffer[MAX_INPUT_BUFFER];
397 373
398 strcpy(temp_buffer, recv_buffer); 374 strcpy(temp_buffer, recv_buffer);
399 for (char *ptr = (char *)strtok(temp_buffer, " "); ptr != NULL; ptr = (char *)strtok(NULL, " ")) { 375 for (char *ptr = strtok(temp_buffer, " "); ptr != NULL; ptr = strtok(NULL, " ")) {
400 if (!strcmp(ptr, "OFF")) { 376 if (!strcmp(ptr, "OFF")) {
401 config->status |= UPSSTATUS_OFF; 377 result.ups_status |= UPSSTATUS_OFF;
402 } else if (!strcmp(ptr, "OL")) { 378 } else if (!strcmp(ptr, "OL")) {
403 config->status |= UPSSTATUS_OL; 379 result.ups_status |= UPSSTATUS_OL;
404 } else if (!strcmp(ptr, "OB")) { 380 } else if (!strcmp(ptr, "OB")) {
405 config->status |= UPSSTATUS_OB; 381 result.ups_status |= UPSSTATUS_OB;
406 } else if (!strcmp(ptr, "LB")) { 382 } else if (!strcmp(ptr, "LB")) {
407 config->status |= UPSSTATUS_LB; 383 result.ups_status |= UPSSTATUS_LB;
408 } else if (!strcmp(ptr, "CAL")) { 384 } else if (!strcmp(ptr, "CAL")) {
409 config->status |= UPSSTATUS_CAL; 385 result.ups_status |= UPSSTATUS_CAL;
410 } else if (!strcmp(ptr, "RB")) { 386 } else if (!strcmp(ptr, "RB")) {
411 config->status |= UPSSTATUS_RB; 387 result.ups_status |= UPSSTATUS_RB;
412 } else if (!strcmp(ptr, "BYPASS")) { 388 } else if (!strcmp(ptr, "BYPASS")) {
413 config->status |= UPSSTATUS_BYPASS; 389 result.ups_status |= UPSSTATUS_BYPASS;
414 } else if (!strcmp(ptr, "OVER")) { 390 } else if (!strcmp(ptr, "OVER")) {
415 config->status |= UPSSTATUS_OVER; 391 result.ups_status |= UPSSTATUS_OVER;
416 } else if (!strcmp(ptr, "TRIM")) { 392 } else if (!strcmp(ptr, "TRIM")) {
417 config->status |= UPSSTATUS_TRIM; 393 result.ups_status |= UPSSTATUS_TRIM;
418 } else if (!strcmp(ptr, "BOOST")) { 394 } else if (!strcmp(ptr, "BOOST")) {
419 config->status |= UPSSTATUS_BOOST; 395 result.ups_status |= UPSSTATUS_BOOST;
420 } else if (!strcmp(ptr, "CHRG")) { 396 } else if (!strcmp(ptr, "CHRG")) {
421 config->status |= UPSSTATUS_CHRG; 397 result.ups_status |= UPSSTATUS_CHRG;
422 } else if (!strcmp(ptr, "DISCHRG")) { 398 } else if (!strcmp(ptr, "DISCHRG")) {
423 config->status |= UPSSTATUS_DISCHRG; 399 result.ups_status |= UPSSTATUS_DISCHRG;
424 } else if (!strcmp(ptr, "ALARM")) { 400 } else if (!strcmp(ptr, "ALARM")) {
425 config->status |= UPSSTATUS_ALARM; 401 result.ups_status |= UPSSTATUS_ALARM;
426 } else { 402 } else {
427 config->status |= UPSSTATUS_UNKNOWN; 403 result.ups_status |= UPSSTATUS_UNKNOWN;
428 } 404 }
429 } 405 }
430 406
431 return OK; 407 return result;
432} 408}
433 409
434/* gets a variable value for a specific UPS */ 410/* gets a variable value for a specific UPS */
435int 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) {
436 char send_buffer[MAX_INPUT_BUFFER]; 412 char send_buffer[MAX_INPUT_BUFFER];
437 413
438 /* create the command string to send to the UPS daemon */ 414 /* create the command string to send to the UPS daemon */
439 /* Add LOGOUT to avoid read failure logs */ 415 /* Add LOGOUT to avoid read failure logs */
440 int res = snprintf(send_buffer, sizeof(send_buffer), "GET VAR %s %s\nLOGOUT\n", config.ups_name, varname); 416 int res = snprintf(send_buffer, sizeof(send_buffer), "GET VAR %s %s\nLOGOUT\n", config.ups_name,
417 varname);
441 if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) { 418 if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) {
442 printf("%s\n", _("UPS name to long for buffer")); 419 printf("%s\n", _("UPS name to long for buffer"));
443 return ERROR; 420 return ERROR;
@@ -446,7 +423,8 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
446 char temp_buffer[MAX_INPUT_BUFFER]; 423 char temp_buffer[MAX_INPUT_BUFFER];
447 424
448 /* send the command to the daemon and get a response back */ 425 /* send the command to the daemon and get a response back */
449 if (process_tcp_request(config.server_address, config.server_port, send_buffer, temp_buffer, sizeof(temp_buffer)) != STATE_OK) { 426 if (process_tcp_request(config.server_address, config.server_port, send_buffer, temp_buffer,
427 sizeof(temp_buffer)) != STATE_OK) {
450 printf("%s\n", _("Invalid response received from host")); 428 printf("%s\n", _("Invalid response received from host"));
451 return ERROR; 429 return ERROR;
452 } 430 }
@@ -500,7 +478,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
500 [-wv warn_value] [-cv crit_value] [-to to_sec] */ 478 [-wv warn_value] [-cv crit_value] [-to to_sec] */
501 479
502/* process command-line arguments */ 480/* process command-line arguments */
503int process_arguments(int argc, char **argv, ups_config *config) { 481check_ups_config_wrapper process_arguments(int argc, char **argv) {
504 482
505 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 483 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
506 {"ups", required_argument, 0, 'u'}, 484 {"ups", required_argument, 0, 'u'},
@@ -514,8 +492,14 @@ int process_arguments(int argc, char **argv, ups_config *config) {
514 {"help", no_argument, 0, 'h'}, 492 {"help", no_argument, 0, 'h'},
515 {0, 0, 0, 0}}; 493 {0, 0, 0, 0}};
516 494
495 check_ups_config_wrapper result = {
496 .errorcode = OK,
497 .config = check_ups_config_init(),
498 };
499
517 if (argc < 2) { 500 if (argc < 2) {
518 return ERROR; 501 result.errorcode = ERROR;
502 return result;
519 } 503 }
520 504
521 int c; 505 int c;
@@ -542,52 +526,52 @@ int process_arguments(int argc, char **argv, ups_config *config) {
542 usage5(); 526 usage5();
543 case 'H': /* hostname */ 527 case 'H': /* hostname */
544 if (is_host(optarg)) { 528 if (is_host(optarg)) {
545 config->server_address = optarg; 529 result.config.server_address = optarg;
546 } else { 530 } else {
547 usage2(_("Invalid hostname/address"), optarg); 531 usage2(_("Invalid hostname/address"), optarg);
548 } 532 }
549 break; 533 break;
550 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
551 Fahrenheit) */ 535 Fahrenheit) */
552 config->temp_output_c = true; 536 result.config.temp_output_c = true;
553 break; 537 break;
554 case 'u': /* ups name */ 538 case 'u': /* ups name */
555 config->ups_name = optarg; 539 result.config.ups_name = optarg;
556 break; 540 break;
557 case 'p': /* port */ 541 case 'p': /* port */
558 if (is_intpos(optarg)) { 542 if (is_intpos(optarg)) {
559 config->server_port = atoi(optarg); 543 result.config.server_port = atoi(optarg);
560 } else { 544 } else {
561 usage2(_("Port must be a positive integer"), optarg); 545 usage2(_("Port must be a positive integer"), optarg);
562 } 546 }
563 break; 547 break;
564 case 'c': /* critical time threshold */ 548 case 'c': /* critical time threshold */
565 if (is_intnonneg(optarg)) { 549 if (is_intnonneg(optarg)) {
566 config->critical_value = atoi(optarg); 550 result.config.critical_value = atoi(optarg);
567 config->check_crit = true; 551 result.config.check_crit = true;
568 } else { 552 } else {
569 usage2(_("Critical time must be a positive integer"), optarg); 553 usage2(_("Critical time must be a positive integer"), optarg);
570 } 554 }
571 break; 555 break;
572 case 'w': /* warning time threshold */ 556 case 'w': /* warning time threshold */
573 if (is_intnonneg(optarg)) { 557 if (is_intnonneg(optarg)) {
574 config->warning_value = atoi(optarg); 558 result.config.warning_value = atoi(optarg);
575 config->check_warn = true; 559 result.config.check_warn = true;
576 } else { 560 } else {
577 usage2(_("Warning time must be a positive integer"), optarg); 561 usage2(_("Warning time must be a positive integer"), optarg);
578 } 562 }
579 break; 563 break;
580 case 'v': /* variable */ 564 case 'v': /* variable */
581 if (!strcmp(optarg, "LINE")) { 565 if (!strcmp(optarg, "LINE")) {
582 config->check_variable = UPS_UTILITY; 566 result.config.check_variable = UPS_UTILITY;
583 } else if (!strcmp(optarg, "TEMP")) { 567 } else if (!strcmp(optarg, "TEMP")) {
584 config->check_variable = UPS_TEMP; 568 result.config.check_variable = UPS_TEMP;
585 } else if (!strcmp(optarg, "BATTPCT")) { 569 } else if (!strcmp(optarg, "BATTPCT")) {
586 config->check_variable = UPS_BATTPCT; 570 result.config.check_variable = UPS_BATTPCT;
587 } else if (!strcmp(optarg, "LOADPCT")) { 571 } else if (!strcmp(optarg, "LOADPCT")) {
588 config->check_variable = UPS_LOADPCT; 572 result.config.check_variable = UPS_LOADPCT;
589 } else if (!strcmp(optarg, "REALPOWER")) { 573 } else if (!strcmp(optarg, "REALPOWER")) {
590 config->check_variable = UPS_REALPOWER; 574 result.config.check_variable = UPS_REALPOWER;
591 } else { 575 } else {
592 usage2(_("Unrecognized UPS variable"), optarg); 576 usage2(_("Unrecognized UPS variable"), optarg);
593 } 577 }
@@ -608,27 +592,27 @@ int process_arguments(int argc, char **argv, ups_config *config) {
608 } 592 }
609 } 593 }
610 594
611 if (config->server_address == NULL && argc > optind) { 595 if (result.config.server_address == NULL && argc > optind) {
612 if (is_host(argv[optind])) { 596 if (is_host(argv[optind])) {
613 config->server_address = argv[optind++]; 597 result.config.server_address = argv[optind++];
614 } else { 598 } else {
615 usage2(_("Invalid hostname/address"), optarg); 599 usage2(_("Invalid hostname/address"), optarg);
616 } 600 }
617 } 601 }
618 602
619 if (config->server_address == NULL) { 603 if (result.config.server_address == NULL) {
620 config->server_address = strdup("127.0.0.1"); 604 result.config.server_address = strdup("127.0.0.1");
621 } 605 }
622 606
623 return validate_arguments(*config); 607 return validate_arguments(result);
624} 608}
625 609
626int validate_arguments(ups_config config) { 610check_ups_config_wrapper validate_arguments(check_ups_config_wrapper config_wrapper) {
627 if (!config.ups_name) { 611 if (config_wrapper.config.ups_name) {
628 printf("%s\n", _("Error : no UPS indicated")); 612 printf("%s\n", _("Error : no UPS indicated"));
629 return ERROR; 613 config_wrapper.errorcode = ERROR;
630 } 614 }
631 return OK; 615 return config_wrapper;
632} 616}
633 617
634void print_help(void) { 618void print_help(void) {
@@ -660,7 +644,8 @@ void print_help(void) {
660 printf(" %s\n", "-T, --temperature"); 644 printf(" %s\n", "-T, --temperature");
661 printf(" %s\n", _("Output of temperatures in Celsius")); 645 printf(" %s\n", _("Output of temperatures in Celsius"));
662 printf(" %s\n", "-v, --variable=STRING"); 646 printf(" %s\n", "-v, --variable=STRING");
663 printf(" %s %s\n", _("Valid values for STRING are"), "LINE, TEMP, BATTPCT, LOADPCT or REALPOWER"); 647 printf(" %s %s\n", _("Valid values for STRING are"),
648 "LINE, TEMP, BATTPCT, LOADPCT or REALPOWER");
664 649
665 printf(UT_WARN_CRIT); 650 printf(UT_WARN_CRIT);
666 651
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 f1e1c39d..2340eae4 100644
--- a/plugins/check_users.c
+++ b/plugins/check_users.c
@@ -34,8 +34,15 @@ const char *progname = "check_users";
34const char *copyright = "2000-2024"; 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>
@@ -53,29 +60,16 @@ const char *email = "devel@monitoring-plugins.org";
53# include <systemd/sd-login.h> 60# include <systemd/sd-login.h>
54#endif 61#endif
55 62
56#define possibly_set(a, b) ((a) == 0 ? (b) : 0) 63typedef struct process_argument_wrapper {
64 int errorcode;
65 check_users_config config;
66} check_users_config_wrapper;
67check_users_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57 68
58static int process_arguments(int, char **); 69void print_help(void);
59static void print_help(void);
60void print_usage(void); 70void print_usage(void);
61 71
62static char *warning_range = NULL;
63static char *critical_range = NULL;
64static thresholds *thlds = NULL;
65
66int main(int argc, char **argv) { 72int main(int argc, char **argv) {
67 int users = -1;
68 int result = STATE_UNKNOWN;
69#if HAVE_WTSAPI32_H
70 WTS_SESSION_INFO *wtsinfo;
71 DWORD wtscount;
72 DWORD index;
73#elif HAVE_UTMPX_H
74 struct utmpx *putmpx;
75#else
76 char input_buffer[MAX_INPUT_BUFFER];
77#endif
78
79 setlocale(LC_ALL, ""); 73 setlocale(LC_ALL, "");
80 bindtextdomain(PACKAGE, LOCALEDIR); 74 bindtextdomain(PACKAGE, LOCALEDIR);
81 textdomain(PACKAGE); 75 textdomain(PACKAGE);
@@ -83,121 +77,104 @@ int main(int argc, char **argv) {
83 /* Parse extra opts if any */ 77 /* Parse extra opts if any */
84 argv = np_extra_opts(&argc, argv, progname); 78 argv = np_extra_opts(&argc, argv, progname);
85 79
86 if (process_arguments(argc, argv) == ERROR) 80 check_users_config_wrapper tmp_config = process_arguments(argc, argv);
87 usage4(_("Could not parse arguments"));
88
89 users = 0;
90
91#ifdef HAVE_LIBSYSTEMD
92 if (sd_booted() > 0)
93 users = sd_get_sessions(NULL);
94 else {
95#endif
96#if HAVE_WTSAPI32_H
97 if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) {
98 printf(_("Could not enumerate RD sessions: %d\n"), GetLastError());
99 return STATE_UNKNOWN;
100 }
101
102 for (index = 0; index < wtscount; index++) {
103 LPTSTR username;
104 DWORD size;
105 int len;
106
107 if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size))
108 continue;
109
110 len = lstrlen(username);
111
112 WTSFreeMemory(username);
113
114 if (len == 0)
115 continue;
116 81
117 if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) 82 if (tmp_config.errorcode == ERROR) {
118 users++; 83 usage4(_("Could not parse arguments"));
119 }
120
121 WTSFreeMemory(wtsinfo);
122#elif HAVE_UTMPX_H
123 /* get currently logged users from utmpx */
124 setutxent();
125
126 while ((putmpx = getutxent()) != NULL)
127 if (putmpx->ut_type == USER_PROCESS)
128 users++;
129
130 endutxent();
131#else
132 /* run the command */
133 child_process = spopen(WHO_COMMAND);
134 if (child_process == NULL) {
135 printf(_("Could not open pipe: %s\n"), WHO_COMMAND);
136 return STATE_UNKNOWN;
137 } 84 }
138 85
139 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); 86 check_users_config config = tmp_config.config;
140 if (child_stderr == NULL)
141 printf(_("Could not open stderr for %s\n"), WHO_COMMAND);
142
143 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
144 /* increment 'users' on all lines except total user count */
145 if (input_buffer[0] != '#') {
146 users++;
147 continue;
148 }
149 87
150 /* get total logged in users */ 88#ifdef _WIN32
151 if (sscanf(input_buffer, _("# users=%d"), &users) == 1) 89# if HAVE_WTSAPI32_H
152 break; 90 get_num_of_users_wrapper user_wrapper = get_num_of_users_windows();
91# else
92# error Did not find WTSAPI32
93# endif // HAVE_WTSAPI32_H
94#else
95# ifdef HAVE_LIBSYSTEMD
96 get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd();
97# elif HAVE_UTMPX_H
98 get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp();
99# else // !HAVE_LIBSYSTEMD && !HAVE_UTMPX_H
100 get_num_of_users_wrapper user_wrapper = get_num_of_users_who_command();
101# endif // HAVE_LIBSYSTEMD
102#endif // _WIN32
103
104 mp_check overall = mp_check_init();
105 if (config.output_format_is_set) {
106 mp_set_format(config.output_format);
153 } 107 }
108 mp_subcheck sc_users = mp_subcheck_init();
154 109
155 /* check STDERR */ 110 if (user_wrapper.errorcode != 0) {
156 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) 111 sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN);
157 result = possibly_set(result, STATE_UNKNOWN); 112 sc_users.output = "Failed to retrieve number of users";
158 (void)fclose(child_stderr); 113 mp_add_subcheck_to_check(&overall, sc_users);
159 114 mp_exit(overall);
160 /* close the pipe */
161 if (spclose(child_process))
162 result = possibly_set(result, STATE_UNKNOWN);
163#endif
164#ifdef HAVE_LIBSYSTEMD
165 } 115 }
166#endif
167
168 /* check the user count against warning and critical thresholds */ 116 /* check the user count against warning and critical thresholds */
169 result = get_status((double)users, thlds);
170 117
171 if (result == STATE_UNKNOWN) 118 mp_perfdata users_pd = {
172 printf("%s\n", _("Unable to read output")); 119 .label = "users",
173 else { 120 .value = mp_create_pd_value(user_wrapper.users),
174 printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, 121 };
175 sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); 122
123 users_pd = mp_pd_set_thresholds(users_pd, config.thresholds);
124 mp_add_perfdata_to_subcheck(&sc_users, users_pd);
125
126 int tmp_status = mp_get_pd_status(users_pd);
127 sc_users = mp_set_subcheck_state(sc_users, tmp_status);
128
129 switch (tmp_status) {
130 case STATE_WARNING:
131 xasprintf(&sc_users.output,
132 "%d users currently logged in. This violates the warning threshold",
133 user_wrapper.users);
134 break;
135 case STATE_CRITICAL:
136 xasprintf(&sc_users.output,
137 "%d users currently logged in. This violates the critical threshold",
138 user_wrapper.users);
139 break;
140 default:
141 xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users);
176 } 142 }
177 143
178 return result; 144 mp_add_subcheck_to_check(&overall, sc_users);
145 mp_exit(overall);
179} 146}
180 147
148#define output_format_index CHAR_MAX + 1
149
181/* process command-line arguments */ 150/* process command-line arguments */
182int process_arguments(int argc, char **argv) { 151check_users_config_wrapper process_arguments(int argc, char **argv) {
183 static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, 152 static struct option longopts[] = {{"critical", required_argument, 0, 'c'},
184 {"warning", required_argument, 0, 'w'}, 153 {"warning", required_argument, 0, 'w'},
185 {"version", no_argument, 0, 'V'}, 154 {"version", no_argument, 0, 'V'},
186 {"help", no_argument, 0, 'h'}, 155 {"help", no_argument, 0, 'h'},
156 {"output-format", required_argument, 0, output_format_index},
187 {0, 0, 0, 0}}; 157 {0, 0, 0, 0}};
188 158
189 if (argc < 2) 159 if (argc < 2) {
190 usage("\n"); 160 usage(progname);
161 }
162
163 char *warning_range = NULL;
164 char *critical_range = NULL;
165 check_users_config_wrapper result = {
166 .config = check_users_config_init(),
167 .errorcode = OK,
168 };
191 169
192 int option_char;
193 while (true) { 170 while (true) {
194 int option = 0; 171 int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL);
195 option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option);
196 172
197 if (option_char == -1 || option_char == EOF || option_char == 1) 173 if (counter == -1 || counter == EOF || counter == 1) {
198 break; 174 break;
175 }
199 176
200 switch (option_char) { 177 switch (counter) {
201 case '?': /* print short usage statement if args not parsable */ 178 case '?': /* print short usage statement if args not parsable */
202 usage5(); 179 usage5();
203 case 'h': /* help */ 180 case 'h': /* help */
@@ -212,29 +189,66 @@ int process_arguments(int argc, char **argv) {
212 case 'w': /* warning */ 189 case 'w': /* warning */
213 warning_range = optarg; 190 warning_range = optarg;
214 break; 191 break;
192 case output_format_index: {
193 parsed_output_format parser = mp_parse_output_format(optarg);
194 if (!parser.parsing_success) {
195 // TODO List all available formats here, maybe add anothoer usage function
196 printf("Invalid output format: %s\n", optarg);
197 exit(STATE_UNKNOWN);
198 }
199
200 result.config.output_format_is_set = true;
201 result.config.output_format = parser.output_format;
202 break;
203 }
215 } 204 }
216 } 205 }
217 206
218 option_char = optind; 207 int option_char = optind;
219 208
220 if (warning_range == NULL && argc > option_char) 209 if (warning_range == NULL && argc > option_char) {
221 warning_range = argv[option_char++]; 210 warning_range = argv[option_char++];
211 }
222 212
223 if (critical_range == NULL && argc > option_char) 213 if (critical_range == NULL && argc > option_char) {
224 critical_range = argv[option_char++]; 214 critical_range = argv[option_char++];
215 }
225 216
226 /* this will abort in case of invalid ranges */ 217 // TODO add proper verification for ranges here!
227 set_thresholds(&thlds, warning_range, critical_range); 218 mp_range_parsed tmp;
219 if (warning_range) {
220 tmp = mp_parse_range_string(warning_range);
221 } else {
222 printf("Warning threshold missing\n");
223 print_usage();
224 exit(STATE_UNKNOWN);
225 }
228 226
229 if (!thlds->warning) { 227 if (tmp.error == MP_PARSING_SUCCES) {
230 usage4(_("Warning threshold must be a valid range expression")); 228 result.config.thresholds.warning = tmp.range;
229 result.config.thresholds.warning_is_set = true;
230 } else {
231 printf("Failed to parse warning range: %s", warning_range);
232 exit(STATE_UNKNOWN);
231 } 233 }
232 234
233 if (!thlds->critical) { 235 if (critical_range) {
234 usage4(_("Critical threshold must be a valid range expression")); 236 tmp = mp_parse_range_string(critical_range);
237 } else {
238 printf("Critical threshold missing\n");
239 print_usage();
240 exit(STATE_UNKNOWN);
235 } 241 }
236 242
237 return OK; 243 if (tmp.error == MP_PARSING_SUCCES) {
244 result.config.thresholds.critical = tmp.range;
245 result.config.thresholds.critical_is_set = true;
246 } else {
247 printf("Failed to parse critical range: %s", critical_range);
248 exit(STATE_UNKNOWN);
249 }
250
251 return result;
238} 252}
239 253
240void print_help(void) { 254void print_help(void) {
@@ -244,7 +258,8 @@ void print_help(void) {
244 printf(COPYRIGHT, copyright, email); 258 printf(COPYRIGHT, copyright, email);
245 259
246 printf("%s\n", _("This plugin checks the number of users currently logged in on the local")); 260 printf("%s\n", _("This plugin checks the number of users currently logged in on the local"));
247 printf("%s\n", _("system and generates an error if the number exceeds the thresholds specified.")); 261 printf("%s\n",
262 _("system and generates an error if the number exceeds the thresholds specified."));
248 263
249 printf("\n\n"); 264 printf("\n\n");
250 265
@@ -254,9 +269,12 @@ void print_help(void) {
254 printf(UT_EXTRA_OPTS); 269 printf(UT_EXTRA_OPTS);
255 270
256 printf(" %s\n", "-w, --warning=RANGE_EXPRESSION"); 271 printf(" %s\n", "-w, --warning=RANGE_EXPRESSION");
257 printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); 272 printf(" %s\n",
273 _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION"));
258 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); 274 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION");
259 printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); 275 printf(" %s\n",
276 _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION"));
277 printf(UT_OUTPUT_FORMAT);
260 278
261 printf(UT_SUPPORT); 279 printf(UT_SUPPORT);
262} 280}
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..f37819b1
--- /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 "../popen.h"
117# include "../common.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 603bae55..ef888d08 100644
--- a/plugins/common.h
+++ b/plugins/common.h
@@ -1,121 +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#include "../lib/monitoringplug.h"
36 36
37#ifdef HAVE_FEATURES_H 37#ifdef HAVE_FEATURES_H
38#include <features.h> 38# include <features.h>
39#endif 39#endif
40 40
41#include <stdio.h> /* obligatory includes */ 41#include <stdio.h> /* obligatory includes */
42#include <stdlib.h> 42#include <stdlib.h>
43#include <errno.h> 43#include <errno.h>
44 44
45/* 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 */
46#if HAVE_INTTYPES_H 47#if HAVE_INTTYPES_H
47# include <inttypes.h> 48# include <inttypes.h>
48#endif 49#endif
49#if HAVE_STDINT_H 50#if HAVE_STDINT_H
50# include <stdint.h> 51# include <stdint.h>
51#endif 52#endif
52#include <unistd.h> 53#include <unistd.h>
53#ifndef UINTMAX_MAX 54#ifndef UINTMAX_MAX
54# define UINTMAX_MAX ((uintmax_t) -1) 55# define UINTMAX_MAX ((uintmax_t) - 1)
55#endif 56#endif
56 57
57#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 */
58 59
59#ifdef HAVE_MATH_H 60#ifdef HAVE_MATH_H
60#include <math.h> 61# include <math.h>
61#endif 62#endif
62 63
63#ifdef _AIX 64#ifdef _AIX
64#ifdef HAVE_MP_H 65# ifdef HAVE_MP_H
65#include <mp.h> 66# include <mp.h>
66#endif 67# endif
67#endif 68#endif
68 69
69#ifdef HAVE_STRINGS_H 70#ifdef HAVE_STRINGS_H
70#include <strings.h> 71# include <strings.h>
71#endif 72#endif
72#ifdef HAVE_STRING_H 73#ifdef HAVE_STRING_H
73#include <string.h> 74# include <string.h>
74#endif 75#endif
75 76
76#ifdef HAVE_UNISTD_H 77#ifdef HAVE_UNISTD_H
77#include <unistd.h> 78# include <unistd.h>
78#endif 79#endif
79 80
80/* GET_NUMBER_OF_CPUS is a macro to return 81/* GET_NUMBER_OF_CPUS is a macro to return
81 number of CPUs, if we can get that data. 82 number of CPUs, if we can get that data.
82 Use configure.in to test for various OS ways of 83 Use configure.in to test for various OS ways of
83 getting that data 84 getting that data
84 Will return -1 if cannot get data 85 Will return -1 if cannot get data
85*/ 86*/
86#if defined(HAVE_SYSCONF__SC_NPROCESSORS_ONLN) 87#if defined(HAVE_SYSCONF__SC_NPROCESSORS_ONLN)
87# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_ONLN) 88# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_ONLN)
88#elif defined (HAVE_SYSCONF__SC_NPROCESSORS_CONF) 89#elif defined(HAVE_SYSCONF__SC_NPROCESSORS_CONF)
89# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_CONF) 90# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_CONF)
90#else 91#else
91# define GET_NUMBER_OF_CPUS() -1 92# define GET_NUMBER_OF_CPUS() -1
92#endif 93#endif
93 94
94#ifdef HAVE_SYS_TIME_H 95#ifdef HAVE_SYS_TIME_H
95# include <sys/time.h> 96# include <sys/time.h>
96#endif 97#endif
97#include <time.h> 98#include <time.h>
98 99
99#ifdef HAVE_SYS_TYPES_H 100#ifdef HAVE_SYS_TYPES_H
100#include <sys/types.h> 101# include <sys/types.h>
101#endif 102#endif
102 103
103#ifdef HAVE_SYS_SOCKET_H 104#ifdef HAVE_SYS_SOCKET_H
104#include <sys/socket.h> 105# include <sys/socket.h>
105#endif 106#endif
106 107
107#ifdef HAVE_SIGNAL_H 108#ifdef HAVE_SIGNAL_H
108#include <signal.h> 109# include <signal.h>
109#endif 110#endif
110 111
111/* GNU Libraries */ 112/* GNU Libraries */
112#include <getopt.h> 113#include <getopt.h>
113#include "dirname.h" 114#include "../gl/dirname.h"
114 115
115#include <locale.h> 116#include <locale.h>
116 117
117#ifdef HAVE_SYS_POLL_H 118#ifdef HAVE_SYS_POLL_H
118# include "sys/poll.h" 119# include "sys/poll.h"
119#endif 120#endif
120 121
121/* 122/*
@@ -125,42 +126,42 @@
125 */ 126 */
126 127
127#ifndef HAVE_STRTOL 128#ifndef HAVE_STRTOL
128# define strtol(a,b,c) atol((a)) 129# define strtol(a, b, c) atol((a))
129#endif 130#endif
130 131
131#ifndef HAVE_STRTOUL 132#ifndef HAVE_STRTOUL
132# define strtoul(a,b,c) (unsigned long)atol((a)) 133# define strtoul(a, b, c) (unsigned long)atol((a))
133#endif 134#endif
134 135
135/* SSL implementations */ 136/* SSL implementations */
136#ifdef HAVE_GNUTLS_OPENSSL_H 137#ifdef HAVE_GNUTLS_OPENSSL_H
137# include <gnutls/openssl.h> 138# include <gnutls/openssl.h>
138#else 139#else
139# define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */ 140# define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */
140# ifdef HAVE_SSL_H 141# ifdef HAVE_SSL_H
141# include <rsa.h> 142# include <rsa.h>
142# include <crypto.h> 143# include <crypto.h>
143# include <x509.h> 144# include <x509.h>
144# include <pem.h> 145# include <pem.h>
145# include <ssl.h> 146# include <ssl.h>
146# include <err.h> 147# include <err.h>
147# else 148# else
148# ifdef HAVE_OPENSSL_SSL_H 149# ifdef HAVE_OPENSSL_SSL_H
149# include <openssl/rsa.h> 150# include <openssl/rsa.h>
150# include <openssl/crypto.h> 151# include <openssl/crypto.h>
151# include <openssl/x509.h> 152# include <openssl/x509.h>
152# include <openssl/pem.h> 153# include <openssl/pem.h>
153# include <openssl/ssl.h> 154# include <openssl/ssl.h>
154# include <openssl/err.h> 155# include <openssl/err.h>
155# endif 156# endif
156# endif 157# endif
157#endif 158#endif
158 159
159/* 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 */
160#ifdef OPENSSL_VERSION_NUMBER 161#ifdef OPENSSL_VERSION_NUMBER
161# if OPENSSL_VERSION_NUMBER >= 0x10100000 162# if OPENSSL_VERSION_NUMBER >= 0x10100000
162# define OPENSSL_NO_SSL2 163# define OPENSSL_NO_SSL2
163# endif 164# endif
164#endif 165#endif
165 166
166/* 167/*
@@ -171,7 +172,7 @@
171 172
172/* MariaDB 10.2 client does not set MYSQL_PORT */ 173/* MariaDB 10.2 client does not set MYSQL_PORT */
173#ifndef MYSQL_PORT 174#ifndef MYSQL_PORT
174# define MYSQL_PORT 3306 175# define MYSQL_PORT 3306
175#endif 176#endif
176 177
177enum { 178enum {
@@ -180,9 +181,9 @@ enum {
180}; 181};
181 182
182enum { 183enum {
183 DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */ 184 DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
184 MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */ 185 MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */
185 MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */ 186 MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */
186}; 187};
187 188
188/* 189/*
@@ -190,18 +191,18 @@ enum {
190 * Internationalization 191 * Internationalization
191 * 192 *
192 */ 193 */
193#include "gettext.h" 194#include "../gl/gettext.h"
194#define _(String) gettext (String) 195#define _(String) gettext(String)
195#if ! ENABLE_NLS 196#if !ENABLE_NLS
196# undef textdomain 197# undef textdomain
197# define textdomain(Domainname) /* empty */ 198# define textdomain(Domainname) /* empty */
198# undef bindtextdomain 199# undef bindtextdomain
199# define bindtextdomain(Domainname, Dirname) /* empty */ 200# define bindtextdomain(Domainname, Dirname) /* empty */
200#endif 201#endif
201 202
202/* For non-GNU compilers to ignore __attribute__ */ 203/* For non-GNU compilers to ignore __attribute__ */
203#ifndef __GNUC__ 204#ifndef __GNUC__
204# define __attribute__(x) /* do nothing */ 205# define __attribute__(x) /* do nothing */
205#endif 206#endif
206 207
207#endif /* _COMMON_H_ */ 208#endif /* _COMMON_H_ */
diff --git a/plugins/negate.c b/plugins/negate.c
index 750c0bfb..a42a6c59 100644
--- a/plugins/negate.c
+++ b/plugins/negate.c
@@ -38,21 +38,18 @@ const char *email = "devel@monitoring-plugins.org";
38#include "common.h" 38#include "common.h"
39#include "utils.h" 39#include "utils.h"
40#include "utils_cmd.h" 40#include "utils_cmd.h"
41#include "negate.d/config.h"
42#include "../lib/states.h"
41 43
42#include <ctype.h> 44typedef struct {
45 int errorcode;
46 negate_config config;
47} negate_config_wrapper;
48static negate_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
49static negate_config_wrapper validate_arguments(negate_config_wrapper /*config_wrapper*/);
43 50
44static const char **process_arguments(int /*argc*/, char ** /*argv*/);
45static void validate_arguments(char ** /*command_line*/);
46static void print_help(void); 51static void print_help(void);
47void print_usage(void); 52void print_usage(void);
48static bool subst_text = false;
49
50static int state[4] = {
51 STATE_OK,
52 STATE_WARNING,
53 STATE_CRITICAL,
54 STATE_UNKNOWN,
55};
56 53
57int main(int argc, char **argv) { 54int main(int argc, char **argv) {
58 setlocale(LC_ALL, ""); 55 setlocale(LC_ALL, "");
@@ -61,15 +58,24 @@ int main(int argc, char **argv) {
61 58
62 timeout_interval = DEFAULT_TIMEOUT; 59 timeout_interval = DEFAULT_TIMEOUT;
63 60
64 char **command_line = (char **)process_arguments(argc, argv); 61 negate_config_wrapper tmp_config = process_arguments(argc, argv);
62
63 if (tmp_config.errorcode == ERROR) {
64 die(STATE_UNKNOWN, _("negate: Failed to parse input"));
65 }
66
67 negate_config config = tmp_config.config;
68
69 char **command_line = config.command_line;
65 70
66 /* Set signal handling and alarm */ 71 /* Set signal handling and alarm */
67 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) 72 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
68 die(STATE_UNKNOWN, _("Cannot catch SIGALRM")); 73 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
74 }
69 75
70 (void)alarm((unsigned)timeout_interval); 76 (void)alarm(timeout_interval);
71 77
72 int result = STATE_UNKNOWN; 78 mp_state_enum result = STATE_UNKNOWN;
73 output chld_out; 79 output chld_out;
74 output chld_err; 80 output chld_err;
75 81
@@ -86,46 +92,54 @@ int main(int argc, char **argv) {
86 } 92 }
87 93
88 /* Return UNKNOWN or worse if no output is returned */ 94 /* Return UNKNOWN or worse if no output is returned */
89 if (chld_out.lines == 0) 95 if (chld_out.lines == 0) {
90 die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n")); 96 die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n"));
97 }
91 98
92 char *sub; 99 char *sub;
93 for (size_t i = 0; i < chld_out.lines; i++) { 100 for (size_t i = 0; i < chld_out.lines; i++) {
94 if (subst_text && result >= 0 && result <= 4 && result != state[result]) { 101 if (config.subst_text && result >= 0 && result <= 4 && result != config.state[result]) {
95 /* Loop over each match found */ 102 /* Loop over each match found */
96 while ((sub = strstr(chld_out.line[i], state_text(result)))) { 103 while ((sub = strstr(chld_out.line[i], state_text(result)))) {
97 /* Terminate the first part and skip over the string we'll substitute */ 104 /* Terminate the first part and skip over the string we'll substitute */
98 *sub = '\0'; 105 *sub = '\0';
99 sub += strlen(state_text(result)); 106 sub += strlen(state_text(result));
100 /* then put everything back together */ 107 /* then put everything back together */
101 xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i], state_text(state[result]), sub); 108 xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i],
109 state_text(config.state[result]), sub);
102 } 110 }
103 } 111 }
104 printf("%s\n", chld_out.line[i]); 112 printf("%s\n", chld_out.line[i]);
105 } 113 }
106 114
107 if (result >= 0 && result <= 4) { 115 if (result >= 0 && result <= 4) {
108 exit(state[result]); 116 exit(config.state[result]);
109 } else { 117 } else {
110 exit(result); 118 exit(result);
111 } 119 }
112} 120}
113 121
114/* process command-line arguments */ 122/* process command-line arguments */
115static const char **process_arguments(int argc, char **argv) { 123static negate_config_wrapper process_arguments(int argc, char **argv) {
116 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, 124 static struct option longopts[] = {
117 {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'}, 125 {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'},
118 {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'}, 126 {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'},
119 {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'}, 127 {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'},
120 {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}}; 128 {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'},
121 129 {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}};
130
131 negate_config_wrapper result = {
132 .errorcode = OK,
133 .config = negate_config_init(),
134 };
122 bool permute = true; 135 bool permute = true;
123 while (true) { 136 while (true) {
124 int option = 0; 137 int option = 0;
125 int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option); 138 int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option);
126 139
127 if (option_char == -1 || option_char == EOF) 140 if (option_char == -1 || option_char == EOF) {
128 break; 141 break;
142 }
129 143
130 switch (option_char) { 144 switch (option_char) {
131 case '?': /* help */ 145 case '?': /* help */
@@ -139,58 +153,74 @@ static const char **process_arguments(int argc, char **argv) {
139 print_revision(progname, NP_VERSION); 153 print_revision(progname, NP_VERSION);
140 exit(STATE_UNKNOWN); 154 exit(STATE_UNKNOWN);
141 case 't': /* timeout period */ 155 case 't': /* timeout period */
142 if (!is_integer(optarg)) 156 if (!is_integer(optarg)) {
143 usage2(_("Timeout interval must be a positive integer"), optarg); 157 usage2(_("Timeout interval must be a positive integer"), optarg);
144 else 158 } else {
145 timeout_interval = atoi(optarg); 159 timeout_interval = atoi(optarg);
160 }
146 break; 161 break;
147 case 'T': /* Result to return on timeouts */ 162 case 'T': /* Result to return on timeouts */
148 if ((timeout_state = mp_translate_state(optarg)) == ERROR) 163 if ((timeout_state = mp_translate_state(optarg)) == ERROR) {
149 usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 164 usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, "
165 "UNKNOWN) or integer (0-3)."));
166 }
150 break; 167 break;
151 case 'o': /* replacement for OK */ 168 case 'o': /* replacement for OK */
152 if ((state[STATE_OK] = mp_translate_state(optarg)) == ERROR) 169 if ((result.config.state[STATE_OK] = mp_translate_state(optarg)) == ERROR) {
153 usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 170 usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
171 "integer (0-3)."));
172 }
154 permute = false; 173 permute = false;
155 break; 174 break;
156 175
157 case 'w': /* replacement for WARNING */ 176 case 'w': /* replacement for WARNING */
158 if ((state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) 177 if ((result.config.state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) {
159 usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 178 usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
179 "integer (0-3)."));
180 }
160 permute = false; 181 permute = false;
161 break; 182 break;
162 case 'c': /* replacement for CRITICAL */ 183 case 'c': /* replacement for CRITICAL */
163 if ((state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) 184 if ((result.config.state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) {
164 usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 185 usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
186 "integer (0-3)."));
187 }
165 permute = false; 188 permute = false;
166 break; 189 break;
167 case 'u': /* replacement for UNKNOWN */ 190 case 'u': /* replacement for UNKNOWN */
168 if ((state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) 191 if ((result.config.state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) {
169 usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 192 usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
193 "integer (0-3)."));
194 }
170 permute = false; 195 permute = false;
171 break; 196 break;
172 case 's': /* Substitute status text */ 197 case 's': /* Substitute status text */
173 subst_text = true; 198 result.config.subst_text = true;
174 break; 199 break;
175 } 200 }
176 } 201 }
177 202
178 validate_arguments(&argv[optind]);
179
180 if (permute) { /* No [owcu] switch specified, default to this */ 203 if (permute) { /* No [owcu] switch specified, default to this */
181 state[STATE_OK] = STATE_CRITICAL; 204 result.config.state[STATE_OK] = STATE_CRITICAL;
182 state[STATE_CRITICAL] = STATE_OK; 205 result.config.state[STATE_CRITICAL] = STATE_OK;
183 } 206 }
184 207
185 return (const char **)&argv[optind]; 208 result.config.command_line = &argv[optind];
209
210 return validate_arguments(result);
186} 211}
187 212
188void validate_arguments(char **command_line) { 213negate_config_wrapper validate_arguments(negate_config_wrapper config_wrapper) {
189 if (command_line[0] == NULL) 214 if (config_wrapper.config.command_line[0] == NULL) {
190 usage4(_("Could not parse arguments")); 215 usage4(_("Could not parse arguments"));
216 }
191 217
192 if (strncmp(command_line[0], "/", 1) != 0 && strncmp(command_line[0], "./", 2) != 0) 218 if (strncmp(config_wrapper.config.command_line[0], "/", 1) != 0 &&
219 strncmp(config_wrapper.config.command_line[0], "./", 2) != 0) {
193 usage4(_("Require path to command")); 220 usage4(_("Require path to command"));
221 }
222
223 return config_wrapper;
194} 224}
195 225
196void print_help(void) { 226void print_help(void) {
@@ -198,7 +228,8 @@ void print_help(void) {
198 228
199 printf(COPYRIGHT, copyright, email); 229 printf(COPYRIGHT, copyright, email);
200 230
201 printf("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and vice-versa) by default.")); 231 printf("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and "
232 "vice-versa) by default."));
202 printf("%s\n", _("Additional switches can be used to control:\n")); 233 printf("%s\n", _("Additional switches can be used to control:\n"));
203 printf("\t - which state becomes what\n"); 234 printf("\t - which state becomes what\n");
204 printf("\t - changing the plugin output text to match the return code"); 235 printf("\t - changing the plugin output text to match the return code");
@@ -228,17 +259,20 @@ void print_help(void) {
228 printf("%s\n", _("Examples:")); 259 printf("%s\n", _("Examples:"));
229 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");
230 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"));
231 printf(" %s\n", "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'"); 262 printf(" %s\n",
263 "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'");
232 printf(" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL")); 264 printf(" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL"));
233 printf("\n"); 265 printf("\n");
234 printf("%s\n", _("Notes:")); 266 printf("%s\n", _("Notes:"));
235 printf(" %s\n", _("This plugin is a wrapper to take the output of another plugin and invert it.")); 267 printf(" %s\n",
268 _("This plugin is a wrapper to take the output of another plugin and invert it."));
236 printf(" %s\n", _("The full path of the plugin must be provided.")); 269 printf(" %s\n", _("The full path of the plugin must be provided."));
237 printf(" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL.")); 270 printf(" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL."));
238 printf(" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK.")); 271 printf(" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK."));
239 printf(" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged.")); 272 printf(" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged."));
240 printf("\n"); 273 printf("\n");
241 printf(" %s\n", _("Using timeout-result, it is possible to override the timeout behaviour or a")); 274 printf(" %s\n",
275 _("Using timeout-result, it is possible to override the timeout behaviour or a"));
242 printf(" %s\n", _("plugin by setting the negate timeout a bit lower.")); 276 printf(" %s\n", _("plugin by setting the negate timeout a bit lower."));
243 277
244 printf(UT_SUPPORT); 278 printf(UT_SUPPORT);
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 e2916c65..b4c6ff0a 100644
--- a/plugins/netutils.c
+++ b/plugins/netutils.c
@@ -30,13 +30,14 @@
30#include "common.h" 30#include "common.h"
31#include "output.h" 31#include "output.h"
32#include "states.h" 32#include "states.h"
33#include <sys/types.h>
33#include "netutils.h" 34#include "netutils.h"
34 35
35unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT; 36unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT;
36unsigned int socket_timeout_state = STATE_CRITICAL; 37mp_state_enum socket_timeout_state = STATE_CRITICAL;
37 38mp_state_enum econn_refuse_state = STATE_CRITICAL;
38int econn_refuse_state = STATE_CRITICAL;
39bool was_refused = false; 39bool was_refused = false;
40
40#if USE_IPV6 41#if USE_IPV6
41int address_family = AF_UNSPEC; 42int address_family = AF_UNSPEC;
42#else 43#else
@@ -63,38 +64,40 @@ void socket_timeout_alarm_handler(int sig) {
63/* connects to a host on a specified tcp port, sends a string, and gets a 64/* connects to a host on a specified tcp port, sends a string, and gets a
64 response. loops on select-recv until timeout or eof to get all of a 65 response. loops on select-recv until timeout or eof to get all of a
65 multi-packet answer */ 66 multi-packet answer */
66int process_tcp_request2(const char *server_address, int server_port, const char *send_buffer, char *recv_buffer, int recv_size) { 67mp_state_enum process_tcp_request2(const char *server_address, const int server_port,
68 const char *send_buffer, char *recv_buffer,
69 const int recv_size) {
67 70
68 int result; 71 int socket;
69 int send_result;
70 int recv_result;
71 int sd;
72 struct timeval tv;
73 fd_set readfds;
74 int recv_length = 0;
75 72
76 result = np_net_connect(server_address, server_port, &sd, IPPROTO_TCP); 73 mp_state_enum connect_result =
77 if (result != STATE_OK) { 74 np_net_connect(server_address, server_port, &socket, IPPROTO_TCP);
75 if (connect_result != STATE_OK) {
78 return STATE_CRITICAL; 76 return STATE_CRITICAL;
79 } 77 }
80 78
81 send_result = send(sd, send_buffer, strlen(send_buffer), 0); 79 mp_state_enum result;
80 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
82 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) { 81 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
83 // printf("%s\n", _("Send failed")); 82 // printf("%s\n", _("Send failed"));
84 result = STATE_WARNING; 83 result = STATE_WARNING;
85 } 84 }
86 85
87 while (1) { 86 fd_set readfds;
87 ssize_t recv_length = 0;
88 while (true) {
88 /* wait up to the number of seconds for socket timeout 89 /* wait up to the number of seconds for socket timeout
89 minus one for data from the host */ 90 minus one for data from the host */
90 tv.tv_sec = socket_timeout - 1; 91 struct timeval timeout = {
91 tv.tv_usec = 0; 92 .tv_sec = socket_timeout - 1,
93 .tv_usec = 0,
94 };
92 FD_ZERO(&readfds); 95 FD_ZERO(&readfds);
93 FD_SET(sd, &readfds); 96 FD_SET(socket, &readfds);
94 select(sd + 1, &readfds, NULL, NULL, &tv); 97 select(socket + 1, &readfds, NULL, NULL, &timeout);
95 98
96 /* make sure some data has arrived */ 99 /* make sure some data has arrived */
97 if (!FD_ISSET(sd, &readfds)) { /* it hasn't */ 100 if (!FD_ISSET(socket, &readfds)) { /* it hasn't */
98 if (!recv_length) { 101 if (!recv_length) {
99 strcpy(recv_buffer, ""); 102 strcpy(recv_buffer, "");
100 // printf("%s\n", _("No data was received from host!")); 103 // printf("%s\n", _("No data was received from host!"));
@@ -103,70 +106,69 @@ int process_tcp_request2(const char *server_address, int server_port, const char
103 recv_buffer[recv_length] = 0; 106 recv_buffer[recv_length] = 0;
104 } 107 }
105 break; 108 break;
106 } else { /* it has */ 109 } /* it has */
107 recv_result = recv(sd, recv_buffer + recv_length, (size_t)recv_size - recv_length - 1, 0); 110
108 if (recv_result == -1) { 111 ssize_t recv_result =
109 /* recv failed, bail out */ 112 recv(socket, recv_buffer + recv_length, (size_t)(recv_size - recv_length - 1), 0);
110 strcpy(recv_buffer + recv_length, ""); 113 if (recv_result == -1) {
111 result = STATE_WARNING; 114 /* recv failed, bail out */
112 break; 115 strcpy(recv_buffer + recv_length, "");
113 } else if (recv_result == 0) { 116 result = STATE_WARNING;
114 /* end of file ? */ 117 break;
115 recv_buffer[recv_length] = 0; 118 }
116 break; 119
117 } else { /* we got data! */ 120 if (recv_result == 0) {
118 recv_length += recv_result; 121 /* end of file ? */
119 if (recv_length >= recv_size - 1) { 122 recv_buffer[recv_length] = 0;
120 /* buffer full, we're done */ 123 break;
121 recv_buffer[recv_size - 1] = 0; 124 }
122 break; 125
123 } 126 /* we got data! */
124 } 127 recv_length += recv_result;
128 if (recv_length >= recv_size - 1) {
129 /* buffer full, we're done */
130 recv_buffer[recv_size - 1] = 0;
131 break;
125 } 132 }
126 /* end if(!FD_ISSET(sd,&readfds)) */ 133 /* end if(!FD_ISSET(sd,&readfds)) */
127 } 134 }
128 /* end while(1) */
129 135
130 close(sd); 136 close(socket);
131 return result; 137 return result;
132} 138}
133 139
134/* connects to a host on a specified port, sends a string, and gets a 140/* connects to a host on a specified port, sends a string, and gets a
135 response */ 141 response */
136int process_request(const char *server_address, int server_port, int proto, const char *send_buffer, char *recv_buffer, int recv_size) { 142mp_state_enum process_request(const char *server_address, const int server_port, const int proto,
137 int result; 143 const char *send_buffer, char *recv_buffer, const int recv_size) {
138 int sd;
139 144
140 result = STATE_OK; 145 mp_state_enum result = STATE_OK;
141 146 int socket;
142 result = np_net_connect(server_address, server_port, &sd, proto); 147 result = np_net_connect(server_address, server_port, &socket, proto);
143 if (result != STATE_OK) { 148 if (result != STATE_OK) {
144 return STATE_CRITICAL; 149 return STATE_CRITICAL;
145 } 150 }
146 151
147 result = send_request(sd, proto, send_buffer, recv_buffer, recv_size); 152 result = send_request(socket, proto, send_buffer, recv_buffer, recv_size);
148 153
149 close(sd); 154 close(socket);
150 155
151 return result; 156 return result;
152} 157}
153 158
154/* opens a tcp or udp connection to a remote host or local socket */ 159/* opens a tcp or udp connection to a remote host or local socket */
155int np_net_connect(const char *host_name, int port, int *sd, int proto) { 160mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor,
161 const int proto) {
156 /* send back STATE_UNKOWN if there's an error 162 /* send back STATE_UNKOWN if there's an error
157 send back STATE_OK if we connect 163 send back STATE_OK if we connect
158 send back STATE_CRITICAL if we can't connect. 164 send back STATE_CRITICAL if we can't connect.
159 Let upstream figure out what to send to the user. */ 165 Let upstream figure out what to send to the user. */
160 struct addrinfo hints; 166 bool is_socket = (host_name[0] == '/');
161 struct addrinfo *r, *res; 167 int socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
162 struct sockaddr_un su;
163 char port_str[6], host[MAX_HOST_ADDRESS_LENGTH];
164 size_t len;
165 int socktype, result;
166 short is_socket = (host_name[0] == '/');
167
168 socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
169 168
169 struct addrinfo hints = {};
170 struct addrinfo *res = NULL;
171 int result;
170 /* as long as it doesn't start with a '/', it's assumed a host or ip */ 172 /* as long as it doesn't start with a '/', it's assumed a host or ip */
171 if (!is_socket) { 173 if (!is_socket) {
172 memset(&hints, 0, sizeof(hints)); 174 memset(&hints, 0, sizeof(hints));
@@ -174,38 +176,46 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
174 hints.ai_protocol = proto; 176 hints.ai_protocol = proto;
175 hints.ai_socktype = socktype; 177 hints.ai_socktype = socktype;
176 178
177 len = strlen(host_name); 179 size_t len = strlen(host_name);
178 /* check for an [IPv6] address (and strip the brackets) */ 180 /* check for an [IPv6] address (and strip the brackets) */
179 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') { 181 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') {
180 host_name++; 182 host_name++;
181 len -= 2; 183 len -= 2;
182 } 184 }
185
186 char host[MAX_HOST_ADDRESS_LENGTH];
187
183 if (len >= sizeof(host)) { 188 if (len >= sizeof(host)) {
184 return STATE_UNKNOWN; 189 return STATE_UNKNOWN;
185 } 190 }
191
186 memcpy(host, host_name, len); 192 memcpy(host, host_name, len);
187 host[len] = '\0'; 193 host[len] = '\0';
194
195 char port_str[6];
188 snprintf(port_str, sizeof(port_str), "%d", port); 196 snprintf(port_str, sizeof(port_str), "%d", port);
189 result = getaddrinfo(host, port_str, &hints, &res); 197 int getaddrinfo_err = getaddrinfo(host, port_str, &hints, &res);
190 198
191 if (result != 0) { 199 if (getaddrinfo_err != 0) {
192 // printf("%s\n", gai_strerror(result)); 200 // printf("%s\n", gai_strerror(result));
193 return STATE_UNKNOWN; 201 return STATE_UNKNOWN;
194 } 202 }
195 203
196 r = res; 204 struct addrinfo *addressPointer = res;
197 while (r) { 205 while (addressPointer) {
198 /* attempt to create a socket */ 206 /* attempt to create a socket */
199 *sd = socket(r->ai_family, socktype, r->ai_protocol); 207 *socketDescriptor =
208 socket(addressPointer->ai_family, socktype, addressPointer->ai_protocol);
200 209
201 if (*sd < 0) { 210 if (*socketDescriptor < 0) {
202 // printf("%s\n", _("Socket creation failed")); 211 // printf("%s\n", _("Socket creation failed"));
203 freeaddrinfo(r); 212 freeaddrinfo(addressPointer);
204 return STATE_UNKNOWN; 213 return STATE_UNKNOWN;
205 } 214 }
206 215
207 /* attempt to open a connection */ 216 /* attempt to open a connection */
208 result = connect(*sd, r->ai_addr, r->ai_addrlen); 217 result =
218 connect(*socketDescriptor, addressPointer->ai_addr, addressPointer->ai_addrlen);
209 219
210 if (result == 0) { 220 if (result == 0) {
211 was_refused = false; 221 was_refused = false;
@@ -220,24 +230,28 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
220 } 230 }
221 } 231 }
222 232
223 close(*sd); 233 close(*socketDescriptor);
224 r = r->ai_next; 234 addressPointer = addressPointer->ai_next;
225 } 235 }
236
226 freeaddrinfo(res); 237 freeaddrinfo(res);
227 } 238
228 /* else the hostname is interpreted as a path to a unix socket */ 239 } else {
229 else { 240 /* else the hostname is interpreted as a path to a unix socket */
230 if (strlen(host_name) >= UNIX_PATH_MAX) { 241 if (strlen(host_name) >= UNIX_PATH_MAX) {
231 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket")); 242 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket"));
232 } 243 }
233 memset(&su, 0, sizeof(su)); 244
245 struct sockaddr_un su = {};
234 su.sun_family = AF_UNIX; 246 su.sun_family = AF_UNIX;
235 strncpy(su.sun_path, host_name, UNIX_PATH_MAX); 247 strncpy(su.sun_path, host_name, UNIX_PATH_MAX);
236 *sd = socket(PF_UNIX, SOCK_STREAM, 0); 248 *socketDescriptor = socket(PF_UNIX, SOCK_STREAM, 0);
237 if (*sd < 0) { 249
250 if (*socketDescriptor < 0) {
238 die(STATE_UNKNOWN, _("Socket creation failed")); 251 die(STATE_UNKNOWN, _("Socket creation failed"));
239 } 252 }
240 result = connect(*sd, (struct sockaddr *)&su, sizeof(su)); 253
254 result = connect(*socketDescriptor, (struct sockaddr *)&su, sizeof(su));
241 if (result < 0 && errno == ECONNREFUSED) { 255 if (result < 0 && errno == ECONNREFUSED) {
242 was_refused = true; 256 was_refused = true;
243 } 257 }
@@ -245,7 +259,9 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
245 259
246 if (result == 0) { 260 if (result == 0) {
247 return STATE_OK; 261 return STATE_OK;
248 } else if (was_refused) { 262 }
263
264 if (was_refused) {
249 switch (econn_refuse_state) { /* a user-defined expected outcome */ 265 switch (econn_refuse_state) { /* a user-defined expected outcome */
250 case STATE_OK: 266 case STATE_OK:
251 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */ 267 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */
@@ -253,7 +269,8 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
253 if (is_socket) { 269 if (is_socket) {
254 // printf("connect to file socket %s: %s\n", host_name, strerror(errno)); 270 // printf("connect to file socket %s: %s\n", host_name, strerror(errno));
255 } else { 271 } else {
256 // printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno)); 272 // printf("connect to address %s and port %d: %s\n", host_name, port,
273 // strerror(errno));
257 } 274 }
258 return STATE_CRITICAL; 275 return STATE_CRITICAL;
259 break; 276 break;
@@ -271,14 +288,11 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
271 } 288 }
272} 289}
273 290
274int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size) { 291mp_state_enum send_request(const int socket, const int proto, const char *send_buffer,
275 int result = STATE_OK; 292 char *recv_buffer, const int recv_size) {
276 int send_result; 293 mp_state_enum result = STATE_OK;
277 int recv_result;
278 struct timeval tv;
279 fd_set readfds;
280 294
281 send_result = send(sd, send_buffer, strlen(send_buffer), 0); 295 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
282 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) { 296 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
283 // printf("%s\n", _("Send failed")); 297 // printf("%s\n", _("Send failed"));
284 result = STATE_WARNING; 298 result = STATE_WARNING;
@@ -286,21 +300,22 @@ int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer,
286 300
287 /* wait up to the number of seconds for socket timeout minus one 301 /* wait up to the number of seconds for socket timeout minus one
288 for data from the host */ 302 for data from the host */
289 tv.tv_sec = socket_timeout - 1; 303 struct timeval timestamp = {
290 tv.tv_usec = 0; 304 .tv_sec = socket_timeout - 1,
305 .tv_usec = 0,
306 };
307 fd_set readfds;
291 FD_ZERO(&readfds); 308 FD_ZERO(&readfds);
292 FD_SET(sd, &readfds); 309 FD_SET(socket, &readfds);
293 select(sd + 1, &readfds, NULL, NULL, &tv); 310 select(socket + 1, &readfds, NULL, NULL, &timestamp);
294 311
295 /* make sure some data has arrived */ 312 /* make sure some data has arrived */
296 if (!FD_ISSET(sd, &readfds)) { 313 if (!FD_ISSET(socket, &readfds)) {
297 strcpy(recv_buffer, ""); 314 strcpy(recv_buffer, "");
298 // printf("%s\n", _("No data was received from host!")); 315 // printf("%s\n", _("No data was received from host!"));
299 result = STATE_WARNING; 316 result = STATE_WARNING;
300 } 317 } else {
301 318 ssize_t recv_result = recv(socket, recv_buffer, (size_t)(recv_size - 1), 0);
302 else {
303 recv_result = recv(sd, recv_buffer, (size_t)recv_size - 1, 0);
304 if (recv_result == -1) { 319 if (recv_result == -1) {
305 strcpy(recv_buffer, ""); 320 strcpy(recv_buffer, "");
306 if (proto != IPPROTO_TCP) { 321 if (proto != IPPROTO_TCP) {
@@ -314,6 +329,7 @@ int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer,
314 /* die returned string */ 329 /* die returned string */
315 recv_buffer[recv_size - 1] = 0; 330 recv_buffer[recv_size - 1] = 0;
316 } 331 }
332
317 return result; 333 return result;
318} 334}
319 335
@@ -335,27 +351,27 @@ bool is_addr(const char *address) {
335#ifdef USE_IPV6 351#ifdef USE_IPV6
336 if (address_family == AF_INET && is_inet_addr(address)) { 352 if (address_family == AF_INET && is_inet_addr(address)) {
337 return true; 353 return true;
338 } else if (address_family == AF_INET6 && is_inet6_addr(address)) { 354 }
355
356 if (address_family == AF_INET6 && is_inet6_addr(address)) {
339 return true; 357 return true;
340 } 358 }
341#else 359#else
342 if (is_inet_addr(address)) { 360 if (is_inet_addr(address)) {
343 return (true); 361 return true;
344 } 362 }
345#endif 363#endif
346 364
347 return (false); 365 return false;
348} 366}
349 367
350int dns_lookup(const char *in, struct sockaddr_storage *ss, int family) { 368bool dns_lookup(const char *node_string, struct sockaddr_storage *ss, const int family) {
351 struct addrinfo hints; 369 struct addrinfo hints;
352 struct addrinfo *res;
353 int retval;
354
355 memset(&hints, 0, sizeof(struct addrinfo)); 370 memset(&hints, 0, sizeof(struct addrinfo));
356 hints.ai_family = family; 371 hints.ai_family = family;
357 372
358 retval = getaddrinfo(in, NULL, &hints, &res); 373 struct addrinfo *res;
374 int retval = getaddrinfo(node_string, NULL, &hints, &res);
359 if (retval != 0) { 375 if (retval != 0) {
360 return false; 376 return false;
361 } 377 }
@@ -363,6 +379,8 @@ int dns_lookup(const char *in, struct sockaddr_storage *ss, int family) {
363 if (ss != NULL) { 379 if (ss != NULL) {
364 memcpy(ss, res->ai_addr, res->ai_addrlen); 380 memcpy(ss, res->ai_addr, res->ai_addrlen);
365 } 381 }
382
366 freeaddrinfo(res); 383 freeaddrinfo(res);
384
367 return true; 385 return true;
368} 386}
diff --git a/plugins/netutils.h b/plugins/netutils.h
index a95057e0..c4461113 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -1,120 +1,120 @@
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#ifdef USE_IPV6
83# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) 82# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6)
84# define is_hostname(addr) resolve_host_or_addr(addr, address_family) 83# define is_hostname(addr) resolve_host_or_addr(addr, address_family)
85#else 84#else
86# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET) 85# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET)
87#endif 86#endif
88 87
89extern unsigned int socket_timeout; 88extern unsigned int socket_timeout;
90extern unsigned int socket_timeout_state; 89extern mp_state_enum socket_timeout_state;
91extern int econn_refuse_state; 90extern mp_state_enum econn_refuse_state;
92extern bool was_refused; 91extern bool was_refused;
93extern int address_family; 92extern int address_family;
94 93
95void socket_timeout_alarm_handler (int) __attribute__((noreturn)); 94void socket_timeout_alarm_handler(int) __attribute__((noreturn));
96 95
97/* SSL-Related functionality */ 96/* SSL-Related functionality */
98#ifdef HAVE_SSL 97#ifdef HAVE_SSL
99# define MP_SSLv2 1 98# define MP_SSLv2 1
100# define MP_SSLv3 2 99# define MP_SSLv3 2
101# define MP_TLSv1 3 100# define MP_TLSv1 3
102# define MP_TLSv1_1 4 101# define MP_TLSv1_1 4
103# define MP_TLSv1_2 5 102# define MP_TLSv1_2 5
104# define MP_SSLv2_OR_NEWER 6 103# define MP_SSLv2_OR_NEWER 6
105# define MP_SSLv3_OR_NEWER 7 104# define MP_SSLv3_OR_NEWER 7
106# define MP_TLSv1_OR_NEWER 8 105# define MP_TLSv1_OR_NEWER 8
107# define MP_TLSv1_1_OR_NEWER 9 106# define MP_TLSv1_1_OR_NEWER 9
108# define MP_TLSv1_2_OR_NEWER 10 107# define MP_TLSv1_2_OR_NEWER 10
109/* maybe this could be merged with the above np_net_connect, via some flags */ 108/* maybe this could be merged with the above np_net_connect, via some flags */
110int np_net_ssl_init(int sd); 109int np_net_ssl_init(int socket);
111int np_net_ssl_init_with_hostname(int sd, char *host_name); 110int 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); 111int 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); 112int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name, int version,
114void np_net_ssl_cleanup(); 113 char *cert, char *privkey);
114void np_net_ssl_cleanup(void);
115int np_net_ssl_write(const void *buf, int num); 115int np_net_ssl_write(const void *buf, int num);
116int np_net_ssl_read(void *buf, int num); 116int np_net_ssl_read(void *buf, int num);
117int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); 117mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118#endif /* HAVE_SSL */ 119#endif /* HAVE_SSL */
119
120#endif /* _NETUTILS_H_ */ 120#endif /* _NETUTILS_H_ */
diff --git a/plugins/picohttpparser/picohttpparser.c b/plugins/picohttpparser/picohttpparser.c
index 2ae92d66..e87388b0 100644
--- a/plugins/picohttpparser/picohttpparser.c
+++ b/plugins/picohttpparser/picohttpparser.c
@@ -50,59 +50,61 @@
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
106static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges,
107 size_t ranges_size, int *found) {
106 *found = 0; 108 *found = 0;
107#if __SSE4_2__ 109#if __SSE4_2__
108 if (likely(buf_end - buf >= 16)) { 110 if (likely(buf_end - buf >= 16)) {
@@ -111,7 +113,8 @@ static const char *findchar_fast(const char *buf, const char *buf_end, const cha
111 size_t left = (buf_end - buf) & ~15; 113 size_t left = (buf_end - buf) & ~15;
112 do { 114 do {
113 __m128i b16 = _mm_loadu_si128((const __m128i *)buf); 115 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
114 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,
117 _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
115 if (unlikely(r != 16)) { 118 if (unlikely(r != 16)) {
116 buf += r; 119 buf += r;
117 *found = 1; 120 *found = 1;
@@ -130,25 +133,29 @@ static const char *findchar_fast(const char *buf, const char *buf_end, const cha
130 return buf; 133 return buf;
131} 134}
132 135
133static 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,
137 size_t *token_len, int *ret) {
134 const char *token_start = buf; 138 const char *token_start = buf;
135 139
136#ifdef __SSE4_2__ 140#ifdef __SSE4_2__
137 static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ 141 static const char ALIGNED(16) ranges1[16] =
138 "\012\037" /* allow SP and up to but not including DEL */ 142 "\0\010" /* allow HT */
139 "\177\177"; /* allow chars w. MSB set */ 143 "\012\037" /* allow SP and up to but not including DEL */
144 "\177\177"; /* allow chars w. MSB set */
140 int found; 145 int found;
141 buf = findchar_fast(buf, buf_end, ranges1, 6, &found); 146 buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
142 if (found) 147 if (found) {
143 goto FOUND_CTL; 148 goto FOUND_CTL;
149 }
144#else 150#else
145 /* 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
152 */
146 while (likely(buf_end - buf >= 8)) { 153 while (likely(buf_end - buf >= 8)) {
147# define DOIT() \ 154# define DOIT() \
148 do { \ 155 do { \
149 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ 156 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
150 goto NonPrintable; \ 157 goto NonPrintable; \
151 ++buf; \ 158 ++buf; \
152 } while (0) 159 } while (0)
153 DOIT(); 160 DOIT();
154 DOIT(); 161 DOIT();
@@ -161,7 +168,8 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const
161# undef DOIT 168# undef DOIT
162 continue; 169 continue;
163 NonPrintable: 170 NonPrintable:
164 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 171 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
172 unlikely(*buf == '\177')) {
165 goto FOUND_CTL; 173 goto FOUND_CTL;
166 } 174 }
167 ++buf; 175 ++buf;
@@ -170,7 +178,8 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const
170 for (;; ++buf) { 178 for (;; ++buf) {
171 CHECK_EOF(); 179 CHECK_EOF();
172 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { 180 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
173 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 181 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
182 unlikely(*buf == '\177')) {
174 goto FOUND_CTL; 183 goto FOUND_CTL;
175 } 184 }
176 } 185 }
@@ -219,27 +228,28 @@ static const char *is_complete(const char *buf, const char *buf_end, size_t last
219 return NULL; 228 return NULL;
220} 229}
221 230
222#define PARSE_INT(valp_, mul_) \ 231#define PARSE_INT(valp_, mul_) \
223 if (*buf < '0' || '9' < *buf) { \ 232 if (*buf < '0' || '9' < *buf) { \
224 buf++; \ 233 buf++; \
225 *ret = -1; \ 234 *ret = -1; \
226 return NULL; \ 235 return NULL; \
227 } \ 236 } \
228 *(valp_) = (mul_) * (*buf++ - '0'); 237 *(valp_) = (mul_) * (*buf++ - '0');
229 238
230#define PARSE_INT_3(valp_) \ 239#define PARSE_INT_3(valp_) \
231 do { \ 240 do { \
232 int res_ = 0; \ 241 int res_ = 0; \
233 PARSE_INT(&res_, 100) \ 242 PARSE_INT(&res_, 100) \
234 *valp_ = res_; \ 243 *valp_ = res_; \
235 PARSE_INT(&res_, 10) \ 244 PARSE_INT(&res_, 10) \
236 *valp_ += res_; \ 245 *valp_ += res_; \
237 PARSE_INT(&res_, 1) \ 246 PARSE_INT(&res_, 1) \
238 *valp_ += res_; \ 247 *valp_ += res_; \
239 } while (0) 248 } while (0)
240 249
241/* returned pointer is always within [buf, buf_end), or null */ 250/* returned pointer is always within [buf, buf_end), or null */
242static 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,
252 int *minor_version, int *ret) {
243 /* 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 */
244 if (buf_end - buf < 9) { 254 if (buf_end - buf < 9) {
245 *ret = -2; 255 *ret = -2;
@@ -260,8 +270,8 @@ static const char *parse_http_version(const char *buf, const char *buf_end, int
260 return buf; 270 return buf;
261} 271}
262 272
263static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, size_t max_headers, 273static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers,
264 int *ret) { 274 size_t *num_headers, size_t max_headers, int *ret) {
265 for (;; ++*num_headers) { 275 for (;; ++*num_headers) {
266 CHECK_EOF(); 276 CHECK_EOF();
267 if (*buf == '\015') { 277 if (*buf == '\015') {
@@ -337,9 +347,10 @@ static const char *parse_headers(const char *buf, const char *buf_end, struct ph
337 return buf; 347 return buf;
338} 348}
339 349
340static 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,
341 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,
342 size_t max_headers, int *ret) { 352 int *major_version, int *minor_version, struct phr_header *headers,
353 size_t *num_headers, size_t max_headers, int *ret) {
343 /* skip first empty line (some clients add CRLF after POST content) */ 354 /* skip first empty line (some clients add CRLF after POST content) */
344 CHECK_EOF(); 355 CHECK_EOF();
345 if (*buf == '\015') { 356 if (*buf == '\015') {
@@ -378,8 +389,9 @@ static const char *parse_request(const char *buf, const char *buf_end, const cha
378 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);
379} 390}
380 391
381int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 392int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len,
382 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,
394 struct phr_header *headers, size_t *num_headers, size_t last_len) {
383 const char *buf = buf_start, *buf_end = buf_start + len; 395 const char *buf = buf_start, *buf_end = buf_start + len;
384 size_t max_headers = *num_headers; 396 size_t max_headers = *num_headers;
385 int r; 397 int r;
@@ -398,17 +410,18 @@ int phr_parse_request(const char *buf_start, size_t len, const char **method, si
398 return r; 410 return r;
399 } 411 }
400 412
401 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version, minor_version, headers, num_headers, 413 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version,
402 max_headers, &r)) == NULL) { 414 minor_version, headers, num_headers, max_headers, &r)) == NULL) {
403 return r; 415 return r;
404 } 416 }
405 417
406 return (int)(buf - buf_start); 418 return (int)(buf - buf_start);
407} 419}
408 420
409static const char *parse_response(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *status, 421static const char *parse_response(const char *buf, const char *buf_end, int *major_version,
410 const char **msg, size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, 422 int *minor_version, int *status, const char **msg,
411 int *ret) { 423 size_t *msg_len, struct phr_header *headers, size_t *num_headers,
424 size_t max_headers, int *ret) {
412 /* parse "HTTP/1.x" */ 425 /* parse "HTTP/1.x" */
413 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { 426 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) {
414 return NULL; 427 return NULL;
@@ -421,7 +434,8 @@ static const char *parse_response(const char *buf, const char *buf_end, int *maj
421 do { 434 do {
422 ++buf; 435 ++buf;
423 } while (*buf == ' '); 436 } while (*buf == ' ');
424 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */ 437 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse
438 */
425 if (buf_end - buf < 4) { 439 if (buf_end - buf < 4) {
426 *ret = -2; 440 *ret = -2;
427 return NULL; 441 return NULL;
@@ -449,8 +463,9 @@ static const char *parse_response(const char *buf, const char *buf_end, int *maj
449 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 463 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
450} 464}
451 465
452int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version, int *status, const char **msg, 466int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version,
453 size_t *msg_len, 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,
468 size_t *num_headers, size_t last_len) {
454 const char *buf = buf_start, *buf_end = buf + len; 469 const char *buf = buf_start, *buf_end = buf + len;
455 size_t max_headers = *num_headers; 470 size_t max_headers = *num_headers;
456 int r; 471 int r;
@@ -468,15 +483,16 @@ int phr_parse_response(const char *buf_start, size_t len, int *major_version, in
468 return r; 483 return r;
469 } 484 }
470 485
471 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == 486 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len,
472 NULL) { 487 headers, num_headers, max_headers, &r)) == NULL) {
473 return r; 488 return r;
474 } 489 }
475 490
476 return (int)(buf - buf_start); 491 return (int)(buf - buf_start);
477} 492}
478 493
479int 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,
495 size_t *num_headers, size_t last_len) {
480 const char *buf = buf_start, *buf_end = buf + len; 496 const char *buf = buf_start, *buf_end = buf + len;
481 size_t max_headers = *num_headers; 497 size_t max_headers = *num_headers;
482 int r; 498 int r;
@@ -526,8 +542,9 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
526 case CHUNKED_IN_CHUNK_SIZE: 542 case CHUNKED_IN_CHUNK_SIZE:
527 for (;; ++src) { 543 for (;; ++src) {
528 int v; 544 int v;
529 if (src == bufsz) 545 if (src == bufsz) {
530 goto Exit; 546 goto Exit;
547 }
531 if ((v = decode_hex(buf[src])) == -1) { 548 if ((v = decode_hex(buf[src])) == -1) {
532 if (decoder->_hex_count == 0) { 549 if (decoder->_hex_count == 0) {
533 ret = -1; 550 ret = -1;
@@ -548,10 +565,12 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
548 case CHUNKED_IN_CHUNK_EXT: 565 case CHUNKED_IN_CHUNK_EXT:
549 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ 566 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
550 for (;; ++src) { 567 for (;; ++src) {
551 if (src == bufsz) 568 if (src == bufsz) {
552 goto Exit; 569 goto Exit;
553 if (buf[src] == '\012') 570 }
571 if (buf[src] == '\012') {
554 break; 572 break;
573 }
555 } 574 }
556 ++src; 575 ++src;
557 if (decoder->bytes_left_in_chunk == 0) { 576 if (decoder->bytes_left_in_chunk == 0) {
@@ -567,15 +586,17 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
567 case CHUNKED_IN_CHUNK_DATA: { 586 case CHUNKED_IN_CHUNK_DATA: {
568 size_t avail = bufsz - src; 587 size_t avail = bufsz - src;
569 if (avail < decoder->bytes_left_in_chunk) { 588 if (avail < decoder->bytes_left_in_chunk) {
570 if (dst != src) 589 if (dst != src) {
571 memmove(buf + dst, buf + src, avail); 590 memmove(buf + dst, buf + src, avail);
591 }
572 src += avail; 592 src += avail;
573 dst += avail; 593 dst += avail;
574 decoder->bytes_left_in_chunk -= avail; 594 decoder->bytes_left_in_chunk -= avail;
575 goto Exit; 595 goto Exit;
576 } 596 }
577 if (dst != src) 597 if (dst != src) {
578 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); 598 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
599 }
579 src += decoder->bytes_left_in_chunk; 600 src += decoder->bytes_left_in_chunk;
580 dst += decoder->bytes_left_in_chunk; 601 dst += decoder->bytes_left_in_chunk;
581 decoder->bytes_left_in_chunk = 0; 602 decoder->bytes_left_in_chunk = 0;
@@ -584,10 +605,12 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
584 /* fallthru */ 605 /* fallthru */
585 case CHUNKED_IN_CHUNK_CRLF: 606 case CHUNKED_IN_CHUNK_CRLF:
586 for (;; ++src) { 607 for (;; ++src) {
587 if (src == bufsz) 608 if (src == bufsz) {
588 goto Exit; 609 goto Exit;
589 if (buf[src] != '\015') 610 }
611 if (buf[src] != '\015') {
590 break; 612 break;
613 }
591 } 614 }
592 if (buf[src] != '\012') { 615 if (buf[src] != '\012') {
593 ret = -1; 616 ret = -1;
@@ -598,21 +621,26 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
598 break; 621 break;
599 case CHUNKED_IN_TRAILERS_LINE_HEAD: 622 case CHUNKED_IN_TRAILERS_LINE_HEAD:
600 for (;; ++src) { 623 for (;; ++src) {
601 if (src == bufsz) 624 if (src == bufsz) {
602 goto Exit; 625 goto Exit;
603 if (buf[src] != '\015') 626 }
627 if (buf[src] != '\015') {
604 break; 628 break;
629 }
605 } 630 }
606 if (buf[src++] == '\012') 631 if (buf[src++] == '\012') {
607 goto Complete; 632 goto Complete;
633 }
608 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; 634 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
609 /* fallthru */ 635 /* fallthru */
610 case CHUNKED_IN_TRAILERS_LINE_MIDDLE: 636 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
611 for (;; ++src) { 637 for (;; ++src) {
612 if (src == bufsz) 638 if (src == bufsz) {
613 goto Exit; 639 goto Exit;
614 if (buf[src] == '\012') 640 }
641 if (buf[src] == '\012') {
615 break; 642 break;
643 }
616 } 644 }
617 ++src; 645 ++src;
618 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; 646 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
@@ -625,13 +653,16 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
625Complete: 653Complete:
626 ret = bufsz - src; 654 ret = bufsz - src;
627Exit: 655Exit:
628 if (dst != src) 656 if (dst != src) {
629 memmove(buf + dst, buf + src, bufsz - src); 657 memmove(buf + dst, buf + src, bufsz - src);
658 }
630 *_bufsz = dst; 659 *_bufsz = dst;
631 return ret; 660 return ret;
632} 661}
633 662
634int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) { return decoder->_state == CHUNKED_IN_CHUNK_DATA; } 663int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) {
664 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
665}
635 666
636#undef CHECK_EOF 667#undef CHECK_EOF
637#undef EXPECT_CHAR 668#undef EXPECT_CHAR
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 cfe930b6..c596d1e0 100644
--- a/plugins/popen.c
+++ b/plugins/popen.c
@@ -68,7 +68,7 @@ void popen_timeout_alarm_handler(int /*signo*/);
68#endif 68#endif
69 69
70#ifndef WIFEXITED 70#ifndef WIFEXITED
71# define WIFEXITED(stat_val) (((stat_val)&255) == 0) 71# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
72#endif 72#endif
73 73
74/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ 74/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
@@ -96,24 +96,28 @@ FILE *spopen(const char *cmdstring) {
96 env[1] = NULL; 96 env[1] = NULL;
97 97
98 /* if no command was passed, return with no error */ 98 /* if no command was passed, return with no error */
99 if (cmdstring == NULL) 99 if (cmdstring == NULL) {
100 return (NULL); 100 return (NULL);
101 }
101 102
102 char *cmd = NULL; 103 char *cmd = NULL;
103 /* 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 */
104 /* (the calling program may want to access it later) */ 105 /* (the calling program may want to access it later) */
105 cmd = malloc(strlen(cmdstring) + 1); 106 cmd = malloc(strlen(cmdstring) + 1);
106 if (cmd == NULL) 107 if (cmd == NULL) {
107 return NULL; 108 return NULL;
109 }
108 strcpy(cmd, cmdstring); 110 strcpy(cmd, cmdstring);
109 111
110 /* This is not a shell, so we don't handle "???" */ 112 /* This is not a shell, so we don't handle "???" */
111 if (strstr(cmdstring, "\"")) 113 if (strstr(cmdstring, "\"")) {
112 return NULL; 114 return NULL;
115 }
113 116
114 /* 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 */
115 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) 118 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
116 return NULL; 119 return NULL;
120 }
117 121
118 int argc; 122 int argc;
119 char **argv = NULL; 123 char **argv = NULL;
@@ -140,15 +144,17 @@ FILE *spopen(const char *cmdstring) {
140 144
141 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ 145 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
142 str++; 146 str++;
143 if (!strstr(str, "'")) 147 if (!strstr(str, "'")) {
144 return NULL; /* balanced? */ 148 return NULL; /* balanced? */
149 }
145 cmd = 1 + strstr(str, "'"); 150 cmd = 1 + strstr(str, "'");
146 str[strcspn(str, "'")] = 0; 151 str[strcspn(str, "'")] = 0;
147 } else if (strcspn(str, "'") < strcspn(str, " \t\r\n")) { 152 } else if (strcspn(str, "'") < strcspn(str, " \t\r\n")) {
148 /* handle --option='foo bar' strings */ 153 /* handle --option='foo bar' strings */
149 char *tmp = str + strcspn(str, "'") + 1; 154 char *tmp = str + strcspn(str, "'") + 1;
150 if (!strstr(tmp, "'")) 155 if (!strstr(tmp, "'")) {
151 return NULL; /* balanced? */ 156 return NULL; /* balanced? */
157 }
152 tmp += strcspn(tmp, "'") + 1; 158 tmp += strcspn(tmp, "'") + 1;
153 *tmp = 0; 159 *tmp = 0;
154 cmd = tmp + 1; 160 cmd = tmp + 1;
@@ -161,8 +167,9 @@ FILE *spopen(const char *cmdstring) {
161 } 167 }
162 } 168 }
163 169
164 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) 170 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
165 cmd = NULL; 171 cmd = NULL;
172 }
166 173
167 argv[i++] = str; 174 argv[i++] = str;
168 } 175 }
@@ -171,22 +178,26 @@ FILE *spopen(const char *cmdstring) {
171 long maxfd = mp_open_max(); 178 long maxfd = mp_open_max();
172 179
173 if (childpid == NULL) { /* first time through */ 180 if (childpid == NULL) { /* first time through */
174 if ((childpid = calloc((size_t)maxfd, sizeof(pid_t))) == NULL) 181 if ((childpid = calloc((size_t)maxfd, sizeof(pid_t))) == NULL) {
175 return (NULL); 182 return (NULL);
183 }
176 } 184 }
177 185
178 if (child_stderr_array == NULL) { /* first time through */ 186 if (child_stderr_array == NULL) { /* first time through */
179 if ((child_stderr_array = calloc((size_t)maxfd, sizeof(int))) == NULL) 187 if ((child_stderr_array = calloc((size_t)maxfd, sizeof(int))) == NULL) {
180 return (NULL); 188 return (NULL);
189 }
181 } 190 }
182 191
183 int pfd[2]; 192 int pfd[2];
184 if (pipe(pfd) < 0) 193 if (pipe(pfd) < 0) {
185 return (NULL); /* errno set by pipe() */ 194 return (NULL); /* errno set by pipe() */
195 }
186 196
187 int pfderr[2]; 197 int pfderr[2];
188 if (pipe(pfderr) < 0) 198 if (pipe(pfderr) < 0) {
189 return (NULL); /* errno set by pipe() */ 199 return (NULL); /* errno set by pipe() */
200 }
190 201
191#ifdef REDHAT_SPOPEN_ERROR 202#ifdef REDHAT_SPOPEN_ERROR
192 if (signal(SIGCHLD, popen_sigchld_handler) == SIG_ERR) { 203 if (signal(SIGCHLD, popen_sigchld_handler) == SIG_ERR) {
@@ -195,8 +206,9 @@ FILE *spopen(const char *cmdstring) {
195#endif 206#endif
196 207
197 pid_t pid; 208 pid_t pid;
198 if ((pid = fork()) < 0) 209 if ((pid = fork()) < 0) {
199 return (NULL); /* errno set by fork() */ 210 return (NULL); /* errno set by fork() */
211 }
200 212
201 if (pid == 0) { /* child */ 213 if (pid == 0) { /* child */
202 close(pfd[0]); 214 close(pfd[0]);
@@ -210,17 +222,20 @@ FILE *spopen(const char *cmdstring) {
210 close(pfderr[1]); 222 close(pfderr[1]);
211 } 223 }
212 /* close all descriptors in childpid[] */ 224 /* close all descriptors in childpid[] */
213 for (i = 0; i < maxfd; i++) 225 for (i = 0; i < maxfd; i++) {
214 if (childpid[i] > 0) 226 if (childpid[i] > 0) {
215 close(i); 227 close(i);
228 }
229 }
216 230
217 execve(argv[0], argv, env); 231 execve(argv[0], argv, env);
218 _exit(0); 232 _exit(0);
219 } 233 }
220 234
221 close(pfd[1]); /* parent */ 235 close(pfd[1]); /* parent */
222 if ((child_process = fdopen(pfd[0], "r")) == NULL) 236 if ((child_process = fdopen(pfd[0], "r")) == NULL) {
223 return (NULL); 237 return (NULL);
238 }
224 close(pfderr[1]); 239 close(pfderr[1]);
225 240
226 childpid[fileno(child_process)] = pid; /* remember child pid for this fd */ 241 childpid[fileno(child_process)] = pid; /* remember child pid for this fd */
@@ -229,17 +244,20 @@ FILE *spopen(const char *cmdstring) {
229} 244}
230 245
231int spclose(FILE *fp) { 246int spclose(FILE *fp) {
232 if (childpid == NULL) 247 if (childpid == NULL) {
233 return (1); /* popen() has never been called */ 248 return (1); /* popen() has never been called */
249 }
234 250
235 pid_t pid; 251 pid_t pid;
236 int fd = fileno(fp); 252 int fd = fileno(fp);
237 if ((pid = childpid[fd]) == 0) 253 if ((pid = childpid[fd]) == 0) {
238 return (1); /* fp wasn't opened by popen() */ 254 return (1); /* fp wasn't opened by popen() */
255 }
239 256
240 childpid[fd] = 0; 257 childpid[fd] = 0;
241 if (fclose(fp) == EOF) 258 if (fclose(fp) == EOF) {
242 return (1); 259 return (1);
260 }
243 261
244#ifdef REDHAT_SPOPEN_ERROR 262#ifdef REDHAT_SPOPEN_ERROR
245 while (!childtermd) 263 while (!childtermd)
@@ -247,20 +265,24 @@ int spclose(FILE *fp) {
247#endif 265#endif
248 266
249 int status; 267 int status;
250 while (waitpid(pid, &status, 0) < 0) 268 while (waitpid(pid, &status, 0) < 0) {
251 if (errno != EINTR) 269 if (errno != EINTR) {
252 return (1); /* error other than EINTR from waitpid() */ 270 return (1); /* error other than EINTR from waitpid() */
271 }
272 }
253 273
254 if (WIFEXITED(status)) 274 if (WIFEXITED(status)) {
255 return (WEXITSTATUS(status)); /* return child's termination status */ 275 return (WEXITSTATUS(status)); /* return child's termination status */
276 }
256 277
257 return (1); 278 return (1);
258} 279}
259 280
260#ifdef REDHAT_SPOPEN_ERROR 281#ifdef REDHAT_SPOPEN_ERROR
261void popen_sigchld_handler(int signo) { 282void popen_sigchld_handler(int signo) {
262 if (signo == SIGCHLD) 283 if (signo == SIGCHLD) {
263 childtermd = 1; 284 childtermd = 1;
285 }
264} 286}
265#endif 287#endif
266 288
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 4429ceb0..7c583b85 100644
--- a/plugins/runcmd.c
+++ b/plugins/runcmd.c
@@ -53,7 +53,7 @@
53#endif 53#endif
54 54
55#ifndef WIFEXITED 55#ifndef WIFEXITED
56# define WIFEXITED(stat_val) (((stat_val)&255) == 0) 56# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
57#endif 57#endif
58 58
59/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ 59/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
@@ -87,8 +87,9 @@ extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__(
87 * through this api and thus achieve async-safeness throughout the api */ 87 * through this api and thus achieve async-safeness throughout the api */
88void np_runcmd_init(void) { 88void np_runcmd_init(void) {
89 long maxfd = mp_open_max(); 89 long maxfd = mp_open_max();
90 if (!np_pids) 90 if (!np_pids) {
91 np_pids = calloc(maxfd, sizeof(pid_t)); 91 np_pids = calloc(maxfd, sizeof(pid_t));
92 }
92} 93}
93 94
94/* Start running a command */ 95/* Start running a command */
@@ -106,8 +107,9 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
106 107
107 int i = 0; 108 int i = 0;
108 109
109 if (!np_pids) 110 if (!np_pids) {
110 NP_RUNCMD_INIT; 111 NP_RUNCMD_INIT;
112 }
111 113
112 env[0] = strdup("LC_ALL=C"); 114 env[0] = strdup("LC_ALL=C");
113 env[1] = NULL; 115 env[1] = NULL;
@@ -115,18 +117,21 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
115 /* 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 */
116 /* (the calling program may want to access it later) */ 118 /* (the calling program may want to access it later) */
117 cmdlen = strlen(cmdstring); 119 cmdlen = strlen(cmdstring);
118 if ((cmd = malloc(cmdlen + 1)) == NULL) 120 if ((cmd = malloc(cmdlen + 1)) == NULL) {
119 return -1; 121 return -1;
122 }
120 memcpy(cmd, cmdstring, cmdlen); 123 memcpy(cmd, cmdstring, cmdlen);
121 cmd[cmdlen] = '\0'; 124 cmd[cmdlen] = '\0';
122 125
123 /* This is not a shell, so we don't handle "???" */ 126 /* This is not a shell, so we don't handle "???" */
124 if (strstr(cmdstring, "\"")) 127 if (strstr(cmdstring, "\"")) {
125 return -1; 128 return -1;
129 }
126 130
127 /* 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 */
128 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) 132 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
129 return -1; 133 return -1;
134 }
130 135
131 /* 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
132 * 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 */
@@ -145,8 +150,9 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
145 150
146 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ 151 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
147 str++; 152 str++;
148 if (!strstr(str, "'")) 153 if (!strstr(str, "'")) {
149 return -1; /* balanced? */ 154 return -1; /* balanced? */
155 }
150 cmd = 1 + strstr(str, "'"); 156 cmd = 1 + strstr(str, "'");
151 str[strcspn(str, "'")] = 0; 157 str[strcspn(str, "'")] = 0;
152 } else { 158 } else {
@@ -158,14 +164,16 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
158 } 164 }
159 } 165 }
160 166
161 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) 167 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
162 cmd = NULL; 168 cmd = NULL;
169 }
163 170
164 argv[i++] = str; 171 argv[i++] = str;
165 } 172 }
166 173
167 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) 174 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) {
168 return -1; /* errno set by the failing function */ 175 return -1; /* errno set by the failing function */
176 }
169 177
170 /* child runs exceve() and _exit. */ 178 /* child runs exceve() and _exit. */
171 if (pid == 0) { 179 if (pid == 0) {
@@ -190,9 +198,11 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
190 * This is executed in a separate address space (pure child), 198 * This is executed in a separate address space (pure child),
191 * so we don't have to worry about async safety */ 199 * so we don't have to worry about async safety */
192 long maxfd = mp_open_max(); 200 long maxfd = mp_open_max();
193 for (i = 0; i < maxfd; i++) 201 for (i = 0; i < maxfd; i++) {
194 if (np_pids[i] > 0) 202 if (np_pids[i] > 0) {
195 close(i); 203 close(i);
204 }
205 }
196 206
197 execve(argv[0], argv, env); 207 execve(argv[0], argv, env);
198 _exit(STATE_UNKNOWN); 208 _exit(STATE_UNKNOWN);
@@ -215,17 +225,21 @@ static int np_runcmd_close(int fd) {
215 225
216 /* make sure this fd was opened by popen() */ 226 /* make sure this fd was opened by popen() */
217 long maxfd = mp_open_max(); 227 long maxfd = mp_open_max();
218 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) {
219 return -1; 229 return -1;
230 }
220 231
221 np_pids[fd] = 0; 232 np_pids[fd] = 0;
222 if (close(fd) == -1) 233 if (close(fd) == -1) {
223 return -1; 234 return -1;
235 }
224 236
225 /* EINTR is ok (sort of), everything else is bad */ 237 /* EINTR is ok (sort of), everything else is bad */
226 while (waitpid(pid, &status, 0) < 0) 238 while (waitpid(pid, &status, 0) < 0) {
227 if (errno != EINTR) 239 if (errno != EINTR) {
228 return -1; 240 return -1;
241 }
242 }
229 243
230 /* return child's termination status */ 244 /* return child's termination status */
231 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; 245 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
@@ -233,15 +247,18 @@ static int np_runcmd_close(int fd) {
233 247
234void runcmd_timeout_alarm_handler(int signo) { 248void runcmd_timeout_alarm_handler(int signo) {
235 249
236 if (signo == SIGALRM) 250 if (signo == SIGALRM) {
237 puts(_("CRITICAL - Plugin timed out while executing system call")); 251 puts(_("CRITICAL - Plugin timed out while executing system call"));
252 }
238 253
239 long maxfd = mp_open_max(); 254 long maxfd = mp_open_max();
240 if (np_pids) 255 if (np_pids) {
241 for (long int i = 0; i < maxfd; i++) { 256 for (long int i = 0; i < maxfd; i++) {
242 if (np_pids[i] != 0) 257 if (np_pids[i] != 0) {
243 kill(np_pids[i], SIGKILL); 258 kill(np_pids[i], SIGKILL);
259 }
244 } 260 }
261 }
245 262
246 exit(STATE_CRITICAL); 263 exit(STATE_CRITICAL);
247} 264}
@@ -270,15 +287,17 @@ static int np_fetch_output(int fd, output *op, int flags) {
270 287
271 /* some plugins may want to keep output unbroken, and some commands 288 /* some plugins may want to keep output unbroken, and some commands
272 * will yield no output, so return here for those */ 289 * will yield no output, so return here for those */
273 if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) 290 if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) {
274 return op->buflen; 291 return op->buflen;
292 }
275 293
276 /* and some may want both */ 294 /* and some may want both */
277 if (flags & RUNCMD_NO_ASSOC) { 295 if (flags & RUNCMD_NO_ASSOC) {
278 buf = malloc(op->buflen); 296 buf = malloc(op->buflen);
279 memcpy(buf, op->buf, op->buflen); 297 memcpy(buf, op->buf, op->buflen);
280 } else 298 } else {
281 buf = op->buf; 299 buf = op->buf;
300 }
282 301
283 op->line = NULL; 302 op->line = NULL;
284 op->lens = NULL; 303 op->lens = NULL;
@@ -299,8 +318,9 @@ static int np_fetch_output(int fd, output *op, int flags) {
299 op->line[lineno] = &buf[i]; 318 op->line[lineno] = &buf[i];
300 319
301 /* hop to next newline or end of buffer */ 320 /* hop to next newline or end of buffer */
302 while (buf[i] != '\n' && i < op->buflen) 321 while (buf[i] != '\n' && i < op->buflen) {
303 i++; 322 i++;
323 }
304 buf[i] = '\0'; 324 buf[i] = '\0';
305 325
306 /* calculate the string length using pointer difference */ 326 /* calculate the string length using pointer difference */
@@ -317,18 +337,23 @@ int np_runcmd(const char *cmd, output *out, output *err, int flags) {
317 int fd, pfd_out[2], pfd_err[2]; 337 int fd, pfd_out[2], pfd_err[2];
318 338
319 /* initialize the structs */ 339 /* initialize the structs */
320 if (out) 340 if (out) {
321 memset(out, 0, sizeof(output)); 341 memset(out, 0, sizeof(output));
322 if (err) 342 }
343 if (err) {
323 memset(err, 0, sizeof(output)); 344 memset(err, 0, sizeof(output));
345 }
324 346
325 if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) 347 if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) {
326 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 348 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
349 }
327 350
328 if (out) 351 if (out) {
329 out->lines = np_fetch_output(pfd_out[0], out, flags); 352 out->lines = np_fetch_output(pfd_out[0], out, flags);
330 if (err) 353 }
354 if (err) {
331 err->lines = np_fetch_output(pfd_err[0], err, flags); 355 err->lines = np_fetch_output(pfd_err[0], err, flags);
356 }
332 357
333 return np_runcmd_close(fd); 358 return np_runcmd_close(fd);
334} 359}
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 719de575..0e6d7525 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -26,10 +26,12 @@
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"
32#include "../lib/monitoringplug.h" 33#include "../lib/monitoringplug.h"
34#include "states.h"
33 35
34#ifdef HAVE_SSL 36#ifdef HAVE_SSL
35static SSL_CTX *ctx = NULL; 37static SSL_CTX *ctx = NULL;
@@ -37,13 +39,16 @@ static SSL *s = NULL;
37 39
38int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); } 40int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); }
39 41
40int np_net_ssl_init_with_hostname(int sd, char *host_name) { return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); } 42int np_net_ssl_init_with_hostname(int sd, char *host_name) {
43 return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0);
44}
41 45
42int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) { 46int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) {
43 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);
44} 48}
45 49
46int 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) {
47 long options = 0; 52 long options = 0;
48 53
49 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) { 54 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) {
@@ -75,7 +80,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
75# endif 80# endif
76 case MP_TLSv1_1: /* TLSv1.1 protocol */ 81 case MP_TLSv1_1: /* TLSv1.1 protocol */
77# if !defined(SSL_OP_NO_TLSv1_1) 82# if !defined(SSL_OP_NO_TLSv1_1)
78 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."));
79 return STATE_UNKNOWN; 85 return STATE_UNKNOWN;
80# else 86# else
81 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); 87 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
@@ -84,7 +90,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
84# endif 90# endif
85 case MP_TLSv1_2: /* TLSv1.2 protocol */ 91 case MP_TLSv1_2: /* TLSv1.2 protocol */
86# if !defined(SSL_OP_NO_TLSv1_2) 92# if !defined(SSL_OP_NO_TLSv1_2)
87 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."));
88 return STATE_UNKNOWN; 95 return STATE_UNKNOWN;
89# else 96# else
90 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); 97 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
@@ -145,8 +152,9 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
145 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); 152 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
146 if ((s = SSL_new(ctx)) != NULL) { 153 if ((s = SSL_new(ctx)) != NULL) {
147# ifdef SSL_set_tlsext_host_name 154# ifdef SSL_set_tlsext_host_name
148 if (host_name != NULL) 155 if (host_name != NULL) {
149 SSL_set_tlsext_host_name(s, host_name); 156 SSL_set_tlsext_host_name(s, host_name);
157 }
150# endif 158# endif
151 SSL_set_fd(s, sd); 159 SSL_set_fd(s, sd);
152 if (SSL_connect(s) == 1) { 160 if (SSL_connect(s) == 1) {
@@ -182,63 +190,54 @@ int np_net_ssl_write(const void *buf, int num) { return SSL_write(s, buf, num);
182 190
183int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); } 191int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); }
184 192
185int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit) { 193mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
194 int days_till_exp_crit) {
186# ifdef USE_OPENSSL 195# ifdef USE_OPENSSL
187 X509_NAME *subj = NULL;
188 char timestamp[50] = "";
189 char cn[MAX_CN_LENGTH] = "";
190 char *tz;
191
192 int cnlen = -1;
193 int status = STATE_UNKNOWN;
194
195 ASN1_STRING *tm;
196 int offset;
197 struct tm stamp;
198 float time_left;
199 int days_left;
200 int time_remaining;
201 time_t tm_t;
202
203 if (!certificate) { 196 if (!certificate) {
204 printf("%s\n", _("CRITICAL - Cannot retrieve server certificate.")); 197 printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
205 return STATE_CRITICAL; 198 return STATE_CRITICAL;
206 } 199 }
207 200
208 /* Extract CN from certificate subject */ 201 /* Extract CN from certificate subject */
209 subj = X509_get_subject_name(certificate); 202 X509_NAME *subj = X509_get_subject_name(certificate);
210 203
211 if (!subj) { 204 if (!subj) {
212 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); 205 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
213 return STATE_CRITICAL; 206 return STATE_CRITICAL;
214 } 207 }
215 cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); 208
216 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) {
217 strcpy(cn, _("Unknown CN")); 212 strcpy(cn, _("Unknown CN"));
213 }
218 214
219 /* Retrieve timestamp of certificate */ 215 /* Retrieve timestamp of certificate */
220 tm = X509_get_notAfter(certificate); 216 ASN1_STRING *tm = X509_get_notAfter(certificate);
221 217
218 int offset = 0;
219 struct tm stamp = {};
222 /* Generate tm structure to process timestamp */ 220 /* Generate tm structure to process timestamp */
223 if (tm->type == V_ASN1_UTCTIME) { 221 if (tm->type == V_ASN1_UTCTIME) {
224 if (tm->length < 10) { 222 if (tm->length < 10) {
225 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 223 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
226 return STATE_CRITICAL; 224 return STATE_CRITICAL;
227 } else {
228 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
229 if (stamp.tm_year < 50)
230 stamp.tm_year += 100;
231 offset = 0;
232 } 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
233 } else { 232 } else {
234 if (tm->length < 12) { 233 if (tm->length < 12) {
235 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 234 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
236 return STATE_CRITICAL; 235 return STATE_CRITICAL;
237 } else {
238 stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
239 stamp.tm_year -= 1900;
240 offset = 2;
241 } 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;
242 } 241 }
243 stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; 242 stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
244 stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); 243 stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
@@ -247,48 +246,60 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int
247 stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0'); 246 stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0');
248 stamp.tm_isdst = -1; 247 stamp.tm_isdst = -1;
249 248
250 tm_t = timegm(&stamp); 249 time_t tm_t = timegm(&stamp);
251 time_left = difftime(tm_t, time(NULL)); 250 float time_left = difftime(tm_t, time(NULL));
252 days_left = time_left / 86400; 251 int days_left = time_left / 86400;
253 tz = getenv("TZ"); 252 char *tz = getenv("TZ");
254 setenv("TZ", "GMT", 1); 253 setenv("TZ", "GMT", 1);
255 tzset(); 254 tzset();
255
256 char timestamp[50] = "";
256 strftime(timestamp, 50, "%c %z", localtime(&tm_t)); 257 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
257 if (tz) 258 if (tz) {
258 setenv("TZ", tz, 1); 259 setenv("TZ", tz, 1);
259 else 260 } else {
260 unsetenv("TZ"); 261 unsetenv("TZ");
262 }
263
261 tzset(); 264 tzset();
262 265
266 int time_remaining;
267 mp_state_enum status = STATE_UNKNOWN;
263 if (days_left > 0 && days_left <= days_till_exp_warn) { 268 if (days_left > 0 && days_left <= days_till_exp_warn) {
264 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, 269 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
265 days_left, timestamp); 270 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, days_left, timestamp);
266 if (days_left > days_till_exp_crit) 271 if (days_left > days_till_exp_crit) {
267 status = STATE_WARNING; 272 status = STATE_WARNING;
268 else 273 } else {
269 status = STATE_CRITICAL; 274 status = STATE_CRITICAL;
275 }
270 } else if (days_left == 0 && time_left > 0) { 276 } else if (days_left == 0 && time_left > 0) {
271 if (time_left >= 3600) 277 if (time_left >= 3600) {
272 time_remaining = (int)time_left / 3600; 278 time_remaining = (int)time_left / 3600;
273 else 279 } else {
274 time_remaining = (int)time_left / 60; 280 time_remaining = (int)time_left / 60;
281 }
275 282
276 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, 283 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
277 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); 284 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining,
285 time_left >= 3600 ? "hours" : "minutes", timestamp);
278 286
279 if (days_left > days_till_exp_crit) 287 if (days_left > days_till_exp_crit) {
280 status = STATE_WARNING; 288 status = STATE_WARNING;
281 else 289 } else {
282 status = STATE_CRITICAL; 290 status = STATE_CRITICAL;
291 }
283 } else if (time_left < 0) { 292 } else if (time_left < 0) {
284 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp); 293 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp);
285 status = STATE_CRITICAL; 294 status = STATE_CRITICAL;
286 } else if (days_left == 0) { 295 } else if (days_left == 0) {
287 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"),
288 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) {
289 status = STATE_WARNING; 299 status = STATE_WARNING;
290 else 300 } else {
291 status = STATE_CRITICAL; 301 status = STATE_CRITICAL;
302 }
292 } else { 303 } else {
293 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp); 304 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp);
294 status = STATE_OK; 305 status = STATE_OK;
@@ -301,7 +312,7 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int
301# endif /* USE_OPENSSL */ 312# endif /* USE_OPENSSL */
302} 313}
303 314
304int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { 315mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
305# ifdef USE_OPENSSL 316# ifdef USE_OPENSSL
306 X509 *certificate = NULL; 317 X509 *certificate = NULL;
307 certificate = SSL_get_peer_certificate(s); 318 certificate = SSL_get_peer_certificate(s);
@@ -312,4 +323,136 @@ int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
312# endif /* USE_OPENSSL */ 323# endif /* USE_OPENSSL */
313} 324}
314 325
326mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
327 int days_till_exp_crit) {
328 mp_subcheck sc_cert = mp_subcheck_init();
329# ifdef USE_OPENSSL
330 if (!certificate) {
331 xasprintf(&sc_cert.output, _("No server certificate present to inspect"));
332 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
333 return sc_cert;
334 }
335
336 /* Extract CN from certificate subject */
337 X509_NAME *subj = X509_get_subject_name(certificate);
338
339 if (!subj) {
340 xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject"));
341 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
342 return sc_cert;
343 }
344
345 char commonName[MAX_CN_LENGTH] = "";
346 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName));
347 if (cnlen == -1) {
348 strcpy(commonName, _("Unknown CN"));
349 }
350
351 /* Retrieve timestamp of certificate */
352 ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate);
353
354 int offset = 0;
355 struct tm stamp = {};
356 /* Generate tm structure to process timestamp */
357 if (expiry_timestamp->type == V_ASN1_UTCTIME) {
358 if (expiry_timestamp->length < 10) {
359 xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
360 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
361 return sc_cert;
362 }
363
364 stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0');
365 if (stamp.tm_year < 50) {
366 stamp.tm_year += 100;
367 }
368
369 offset = 0;
370 } else {
371 if (expiry_timestamp->length < 12) {
372 xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
373 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
374 return sc_cert;
375 }
376 stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 +
377 (expiry_timestamp->data[1] - '0') * 100 +
378 (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0');
379 stamp.tm_year -= 1900;
380 offset = 2;
381 }
382
383 stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 +
384 (expiry_timestamp->data[3 + offset] - '0') - 1;
385 stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 +
386 (expiry_timestamp->data[5 + offset] - '0');
387 stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 +
388 (expiry_timestamp->data[7 + offset] - '0');
389 stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 +
390 (expiry_timestamp->data[9 + offset] - '0');
391 stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 +
392 (expiry_timestamp->data[11 + offset] - '0');
393 stamp.tm_isdst = -1;
394
395 time_t tm_t = timegm(&stamp);
396 double time_left = difftime(tm_t, time(NULL));
397 int days_left = (int)(time_left / 86400);
398 char *timeZone = getenv("TZ");
399 setenv("TZ", "GMT", 1);
400 tzset();
401
402 char timestamp[50] = "";
403 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
404 if (timeZone) {
405 setenv("TZ", timeZone, 1);
406 } else {
407 unsetenv("TZ");
408 }
409
410 tzset();
411
412 int time_remaining;
413 if (days_left > 0 && days_left <= days_till_exp_warn) {
414 xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName,
415 days_left, timestamp);
416 if (days_left > days_till_exp_crit) {
417 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
418 } else {
419 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
420 }
421 } else if (days_left == 0 && time_left > 0) {
422 if (time_left >= 3600) {
423 time_remaining = (int)time_left / 3600;
424 } else {
425 time_remaining = (int)time_left / 60;
426 }
427
428 xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName,
429 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp);
430
431 if (days_left > days_till_exp_crit) {
432 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
433 } else {
434 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
435 }
436 } else if (time_left < 0) {
437 xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp);
438 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
439 } else if (days_left == 0) {
440 xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName, timestamp);
441 if (days_left > days_till_exp_crit) {
442 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
443 } else {
444 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
445 }
446 } else {
447 xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName, timestamp);
448 sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK);
449 }
450 X509_free(certificate);
451 return sc_cert;
452# else /* ifndef USE_OPENSSL */
453 xasprintf(&sc_cert.output, _("Plugin does not support checking certificates"));
454 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
455 return sc_cert;
456# endif /* USE_OPENSSL */
457}
315#endif /* HAVE_SSL */ 458#endif /* HAVE_SSL */
diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t
index 7a930a4e..2c2fafde 100644
--- a/plugins/t/check_curl.t
+++ b/plugins/t/check_curl.t
@@ -13,12 +13,12 @@ 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 ? 55 : 53;
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';
@@ -63,7 +63,7 @@ $res = NPTest->testCmd(
63 ); 63 );
64cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); 64cmp_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?!) 65# 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"); 66like( $res->output, "/cURL returned 28 - Connection timed out after/", "Output OK");
67 67
68$res = NPTest->testCmd( 68$res = NPTest->testCmd(
69 "./$plugin $hostname_invalid -wt 1 -ct 2" 69 "./$plugin $hostname_invalid -wt 1 -ct 2"
@@ -124,14 +124,14 @@ SKIP: {
124 124
125 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" ); 125 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
126 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); 126 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
127 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'"); 127 like ( $res->output, "/matched not/", "Error message says 'matched not'");
128 128
129 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" ); 129 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
130 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); 130 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
131 131
132 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); 132 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
133 cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); 133 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
134 like ( $res->output, "/pattern found/", "Error message says 'pattern found'"); 134 like ( $res->output, "/matched/", "Error message says 'matched'");
135 135
136 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); 136 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
137 cmp_ok( $res->return_code, "==", 0, "And also when not found"); 137 cmp_ok( $res->return_code, "==", 0, "And also when not found");
@@ -151,63 +151,74 @@ SKIP: {
151 151
152 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" ); 152 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
153 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); 153 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" ); 154 like ( $res->output, qr/Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
155 155
156 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); 156 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
157 is( $res->return_code, 0, "Old syntax for cert checking okay" ); 157 is( $res->return_code, 0, "Old syntax for cert checking okay" );
158 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 158 # deactivated since different timings will change the output
159 # TODO compare without perfdata
160 # is( $res->output, $saved_cert_output, "Same output as new syntax" );
159 161
160 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" ); 162 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
161 is( $res->return_code, 0, "Updated syntax for cert checking okay" ); 163 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
162 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" );
163 167
164 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" ); 168 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
165 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); 169 # deactivated since different timings will change the output
170 # TODO compare without perfdata
171 # cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
166 172
167 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); 173 $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"); 174 # deactivated since different timings will change the output
175 # TODO compare without perfdata
176 # cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
169 177
170 # run some certificate checks with faketime 178 # run some certificate checks with faketime
171 SKIP: { 179 SKIP: {
172 skip "No faketime binary found", 12 if !$faketime; 180 skip "No faketime binary found", 12 if !$faketime;
173 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http"); 181 $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"); 182 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" ); 183 is( $res->return_code, 0, "Catch cert output exit code" );
184
176 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); 185 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
177 if(!defined $year) { 186 if(!defined $year) {
178 die("parsing date failed from: ".$res->output); 187 die("parsing date failed from: ".$res->output);
179 } 188 }
189
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}; 190 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); 191 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
182 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); 192 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
193
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"); 194 $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"); 195 like($res->output, qr/Certificate '$host_tls_cert' just expired/, "Output on expire date");
185 is( $res->return_code, 2, "Output on expire date" ); 196 is( $res->return_code, 2, "Output on expire date" );
186 197
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"); 198 $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"); 199 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" ); 200 is( $res->return_code, 2, "cert expires in 1 second exit code" );
190 201
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"); 202 $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"); 203 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" ); 204 is( $res->return_code, 2, "cert expires in 2 minutes exit code" );
194 205
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"); 206 $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"); 207 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" ); 208 is( $res->return_code, 2, "cert expires in 2 hours exit code" );
198 209
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"); 210 $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"); 211 like($res->output, qr/Certificate '$host_tls_cert' expired on/, "Certificate expired output");
201 is( $res->return_code, 2, "Certificate expired exit code" ); 212 is( $res->return_code, 2, "Certificate expired exit code" );
202 }; 213 };
203 214
204 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" ); 215 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
205 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 216 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' );
206 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); 217 like ( $res->output, '/\'time_tls\'=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
207 218
208 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org -u /download.html -f follow" ); 219 $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"); 220 is( $res->return_code, 0, "Redirection based on location is okay");
210 221
211 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" ); 222 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" );
212 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 223 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' );
213} 224}
diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t
index 9eb77ce4..0f62fb2b 100644
--- a/plugins/t/check_disk.t
+++ b/plugins/t/check_disk.t
@@ -10,6 +10,7 @@ use strict;
10use Test::More; 10use Test::More;
11use NPTest; 11use NPTest;
12use POSIX qw(ceil floor); 12use POSIX qw(ceil floor);
13use Data::Dumper;
13 14
14my $successOutput = '/^DISK OK/'; 15my $successOutput = '/^DISK OK/';
15my $failureOutput = '/^DISK CRITICAL/'; 16my $failureOutput = '/^DISK CRITICAL/';
@@ -20,173 +21,216 @@ my $result;
20my $mountpoint_valid = getTestParameter( "NP_MOUNTPOINT_VALID", "Path to valid mountpoint", "/"); 21my $mountpoint_valid = getTestParameter( "NP_MOUNTPOINT_VALID", "Path to valid mountpoint", "/");
21my $mountpoint2_valid = getTestParameter( "NP_MOUNTPOINT2_VALID", "Path to another valid mountpoint. Must be different from 1st one", "/var"); 22my $mountpoint2_valid = getTestParameter( "NP_MOUNTPOINT2_VALID", "Path to another valid mountpoint. Must be different from 1st one", "/var");
22 23
24my $output_format = "--output-format mp-test-json";
25
23if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") { 26if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") {
24 plan skip_all => "Need 2 mountpoints to test"; 27 plan skip_all => "Need 2 mountpoints to test";
25} else { 28} else {
26 plan tests => 94; 29 plan tests => 97;
27} 30}
28 31
29$result = NPTest->testCmd( 32$result = NPTest->testCmd(
30 "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -p $mountpoint2_valid" 33 "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -P -p $mountpoint2_valid $output_format"
31 ); 34 );
32cmp_ok( $result->return_code, "==", 0, "Checking two mountpoints (must have at least 1% free in space and inodes)"); 35cmp_ok( $result->return_code, "==", 0, "Checking two mountpoints (must have at least 1% free in space and inodes)");
33my $c = 0;
34$_ = $result->output;
35$c++ while /\(/g; # counts number of "(" - should be two
36cmp_ok( $c, '==', 2, "Got two mountpoints in output");
37 36
37like($result->{'mp_test_result'}->{'state'}, "/OK/", "Main result is OK");
38like($result->{'mp_test_result'}->{'checks'}->[0]->{'state'}, "/OK/", "First sub result is OK");
39like($result->{'mp_test_result'}->{'checks'}->[1]->{'state'}, "/OK/", "Second sub result is OK");
40
41my $absolut_space_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'};
42# print("absolute space on mp1: ". $absolut_space_mp1 . "\n");
43
44my $free_percent_on_mp1 = ($result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / ($absolut_space_mp1/100));
45print("free percent on mp1: ". $free_percent_on_mp1 . "\n");
46
47my $absolut_space_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'};
48# print("absolute space on mp2: ". $absolut_space_mp2 . "\n");
49
50my $free_percent_on_mp2 = ($result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ ($absolut_space_mp2/100));
51print("free percent on mp2: ". $free_percent_on_mp2 . "\n");
38 52
39# Get perf data 53my @perfdata;
40# Should use Monitoring::Plugin 54@perfdata[0] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0];
41my @perf_data = sort(split(/ /, $result->perf_output)); 55@perfdata[1] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0];
42 56
57# Decrease precision of numbers since the the fs might be modified between the two runs
58$perfdata[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000);
59$perfdata[1]->{'value'}->{'value'} = int($perfdata[1]->{'value'}->{'value'} / 1000000);
43 60
44# Calculate avg_free free on mountpoint1 and mountpoint2 61# Calculate avg_free free on mountpoint1 and mountpoint2
45# because if you check in the middle, you should get different errors 62# because if you check in the middle, you should get different errors
46$_ = $result->output; 63my $avg_free_percent = ceil(($free_percent_on_mp1+$free_percent_on_mp2)/2);
47my ($free_on_mp1, $free_on_mp2) = (m/\((\d+\.\d+)%.*\((\d+\.\d+)%/); 64# print("avg_free: " . $avg_free_percent . "\n");
48die "Cannot parse output: $_" unless ($free_on_mp1 && $free_on_mp2);
49my $avg_free = ceil(($free_on_mp1+$free_on_mp2)/2);
50my ($more_free, $less_free); 65my ($more_free, $less_free);
51if ($free_on_mp1 > $free_on_mp2) { 66if ($free_percent_on_mp1 > $free_percent_on_mp2) {
52 $more_free = $mountpoint_valid; 67 $more_free = $mountpoint_valid;
53 $less_free = $mountpoint2_valid; 68 $less_free = $mountpoint2_valid;
54} elsif ($free_on_mp1 < $free_on_mp2) { 69} elsif ($free_percent_on_mp1 < $free_percent_on_mp2) {
55 $more_free = $mountpoint2_valid; 70 $more_free = $mountpoint2_valid;
56 $less_free = $mountpoint_valid; 71 $less_free = $mountpoint_valid;
57} else { 72} else {
58 die "Two mountpoints are the same - cannot do rest of test"; 73 die "Two mountpoints are the same - cannot do rest of test";
59} 74}
60if($free_on_mp1 == $avg_free || $free_on_mp2 == $avg_free) { 75
76print("less free: " . $less_free . "\n");
77print("more free: " . $more_free . "\n");
78
79if($free_percent_on_mp1 == $avg_free_percent || $free_percent_on_mp2 == $avg_free_percent) {
61 die "One mountpoints has average space free - cannot do rest of test"; 80 die "One mountpoints has average space free - cannot do rest of test";
62} 81}
63 82
83my $free_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'};
84my $total_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'};
85my $free_inode_percentage_on_mp1 = $free_inodes_on_mp1 / ($total_inodes_on_mp1 / 100);
64 86
65# Do same for inodes 87my $free_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'};
66$_ = $result->output; 88my $total_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'};
67my ($free_inode_on_mp1, $free_inode_on_mp2) = (m/inode=(\d+)%.*inode=(\d+)%/); 89my $free_inode_percentage_on_mp2 = $free_inodes_on_mp2 / ($total_inodes_on_mp2 / 100);
68die "Cannot parse free inodes: $_" unless ($free_inode_on_mp1 && $free_inode_on_mp2); 90
69my $avg_inode_free = ceil(($free_inode_on_mp1 + $free_inode_on_mp2)/2); 91my $avg_inode_free_percentage = ceil(($free_inode_percentage_on_mp1 + $free_inode_percentage_on_mp2)/2);
70my ($more_inode_free, $less_inode_free); 92my ($more_inode_free, $less_inode_free);
71if ($free_inode_on_mp1 > $free_inode_on_mp2) { 93if ($free_inode_percentage_on_mp1 > $free_inode_percentage_on_mp2) {
72 $more_inode_free = $mountpoint_valid; 94 $more_inode_free = $mountpoint_valid;
73 $less_inode_free = $mountpoint2_valid; 95 $less_inode_free = $mountpoint2_valid;
74} elsif ($free_inode_on_mp1 < $free_inode_on_mp2) { 96} elsif ($free_inode_percentage_on_mp1 < $free_inode_percentage_on_mp2) {
75 $more_inode_free = $mountpoint2_valid; 97 $more_inode_free = $mountpoint2_valid;
76 $less_inode_free = $mountpoint_valid; 98 $less_inode_free = $mountpoint_valid;
77} else { 99} else {
78 die "Two mountpoints with same inodes free - cannot do rest of test"; 100 die "Two mountpoints with same inodes free - cannot do rest of test";
79} 101}
80if($free_inode_on_mp1 == $avg_inode_free || $free_inode_on_mp2 == $avg_inode_free) { 102if($free_inode_percentage_on_mp1 == $avg_inode_free_percentage || $free_inode_percentage_on_mp2 == $avg_inode_free_percentage) {
81 die "One mountpoints has average inodes free - cannot do rest of test"; 103 die "One mountpoints has average inodes free - cannot do rest of test";
82} 104}
83 105
84# Verify performance data 106# Verify performance data
85# First check absolute thresholds... 107# First check absolute thresholds...
86$result = NPTest->testCmd( 108$result = NPTest->testCmd(
87 "./check_disk -w 20 -c 10 -p $mountpoint_valid" 109 "./check_disk -w 20 -c 10 -p $mountpoint_valid $output_format"
88 ); 110 );
89$_ = $result->perf_output; 111
90my ($warn_absth_data, $crit_absth_data, $total_absth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); 112cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
91# default unit is MiB, but perfdata is always bytes 113
92is ($warn_absth_data, $total_absth_data - (20 * (2 ** 20)), "Wrong warning in perf data using absolute thresholds"); 114my $warn_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'};
93is ($crit_absth_data, $total_absth_data - (10 * (2 ** 20)), "Wrong critical in perf data using absolute thresholds"); 115my $crit_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'};
116my $total_absth_data= $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'};
117
118# print("warn: " .$warn_absth_data . "\n");
119# print("crit: " .$crit_absth_data . "\n");
120# print("total: " .$total_absth_data . "\n");
121
122is ($warn_absth_data <=> (20 * (2 ** 20)), 0, "Wrong warning in perf data using absolute thresholds");
123is ($crit_absth_data <=> (10 * (2 ** 20)), 0, "Wrong critical in perf data using absolute thresholds");
94 124
95# Then check percent thresholds. 125# Then check percent thresholds.
96$result = NPTest->testCmd( 126$result = NPTest->testCmd(
97 "./check_disk -w 20% -c 10% -p $mountpoint_valid" 127 "./check_disk -w 20% -c 10% -p $mountpoint_valid $output_format"
98 ); 128 );
99$_ = $result->perf_output; 129
100my ($warn_percth_data, $crit_percth_data, $total_percth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); 130cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
101is ($warn_percth_data, int((1-20/100)*$total_percth_data), "Wrong warning in perf data using percent thresholds"); 131
102is ($crit_percth_data, int((1-10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds"); 132my $warn_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'};
133my $crit_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'};
134my $total_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'};
135
136print("warn_percth_data: " . $warn_percth_data . "\n");
137print("crit_percth_data: " . $crit_percth_data . "\n");
138
139is (int($warn_percth_data), int((20/100)*$total_percth_data), "Wrong warning in perf data using percent thresholds. Got " . $warn_percth_data . " with total " . $total_percth_data);
140is (int($crit_percth_data), int((10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds. Got " . $crit_percth_data . " with total " . $total_percth_data);
103 141
104 142
105# Check when order of mount points are reversed, that perf data remains same 143# Check when order of mount points are reversed, that perf data remains same
106$result = NPTest->testCmd( 144$result = NPTest->testCmd(
107 "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid" 145 "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid $output_format"
108 ); 146 );
109@_ = sort(split(/ /, $result->perf_output)); 147cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
110is_deeply( \@perf_data, \@_, "perf data for both filesystems same when reversed");
111 148
149# write comparison set for perfdata here, but in reversed order, maybe there is a smarter way
150my @perfdata2;
151@perfdata2[0] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0];
152@perfdata2[1] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0];
153# Decrease precision of numbers since the the fs might be modified between the two runs
154$perfdata2[0]->{'value'}->{'value'} = int($perfdata2[0]->{'value'}->{'value'} / 1000000);
155$perfdata2[1]->{'value'}->{'value'} = int($perfdata2[1]->{'value'}->{'value'} / 1000000);
156is_deeply(\@perfdata, \@perfdata2, "perf data for both filesystems same when reversed");
112 157
113# Basic filesystem checks for sizes 158# Basic filesystem checks for sizes
114$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free" ); 159$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free $output_format");
115cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free"); 160cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
116like ( $result->output, $successOutput, "OK output" ); 161like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free");
117like ( $result->only_output, qr/free space/, "Have free space text");
118like ( $result->only_output, qr/$more_free/, "Have disk name in text");
119 162
120$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free" ); 163$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free $output_format" );
121cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free and $less_free"); 164cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
165like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free and $less_free");
122 166
123$_ = $result->output; 167my $free_mb_on_mp1 =$result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / (1024 * 1024);
124 168my $free_mb_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ (1024 * 1024);
125my ($free_mb_on_mp1, $free_mb_on_mp2) = (m/(\d+)MiB .* (\d+)MiB /g);
126die "Cannot parse output: $_" unless ($free_mb_on_mp1 && $free_mb_on_mp2); 169die "Cannot parse output: $_" unless ($free_mb_on_mp1 && $free_mb_on_mp2);
127 170
128my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2; 171my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2;
129 172
130 173
174$result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free $output_format" );
175cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
131 176
132$result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free" ); 177$result = NPTest->testCmd( "./check_disk 101 101 $more_free" );
133is( $result->only_output, "DISK OK", "No print out of disks with -e for OKs"); 178like($result->output, "/OK/", "OK in Output");
134 179cmp_ok( $result->return_code, '==', 0, "Old syntax okay, output was: ". $result->output . "\n" );
135$result = NPTest->testCmd( "./check_disk 100 100 $more_free" );
136cmp_ok( $result->return_code, '==', 0, "Old syntax okay" );
137 180
138$result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" ); 181$result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" );
139cmp_ok( $result->return_code, "==", 0, "At least 1% free" ); 182cmp_ok( $result->return_code, "==", 0, "At least 1% free" );
140 183
141$result = NPTest->testCmd( 184$result = NPTest->testCmd(
142 "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free" 185 "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free $output_format"
143 ); 186 );
144cmp_ok( $result->return_code, "==", 2, "Get critical on less_free mountpoint $less_free" ); 187cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
145like( $result->output, $failureOutput, "Right output" ); 188like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Get critical on less_free mountpoint $less_free");
146 189
147 190
148$result = NPTest->testCmd( 191$result = NPTest->testCmd(
149 "./check_disk -w $avg_free% -c 0% -p $less_free" 192 "./check_disk -w $avg_free_percent% -c 0% -p $less_free $output_format"
150 ); 193 );
151cmp_ok( $result->return_code, '==', 1, "Get warning on less_free mountpoint, when checking avg_free"); 194cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
195like($result->{'mp_test_result'}->{'state'}, "/WARNING/", "Get warning on less_free mountpoint, when checking avg_free");
152 196
153$result = NPTest->testCmd( 197$result = NPTest->testCmd(
154 "./check_disk -w $avg_free% -c $avg_free% -p $more_free" 198 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
155 ); 199 );
156cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free"); 200cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free");
157 201
158$result = NPTest->testCmd( 202$result = NPTest->testCmd(
159 "./check_disk -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" 203 "./check_disk -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
160 ); 204 );
161cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning"); 205cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning");
162my $all_disks = $result->output; 206my $all_disks = $result->output;
163 207
164$result = NPTest->testCmd( 208$result = NPTest->testCmd(
165 "./check_disk -e -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" 209 "./check_disk -e -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free"
166 ); 210 );
167isnt( $result->output, $all_disks, "-e gives different output"); 211isnt( $result->output, $all_disks, "-e gives different output");
168 212
169# Need spaces around filesystem name in case less_free and more_free are nested 213# Need spaces around filesystem name in case less_free and more_free are nested
170like( $result->output, qr/ $less_free /, "Found problem $less_free"); 214like( $result->output, qr/ $less_free /, "Found problem $less_free");
171unlike( $result->only_output, qr/ $more_free /, "Has ignored $more_free as not a problem"); 215unlike( $result->only_output, qr/ $more_free /, "Has ignored $more_free as not a problem");
172like( $result->perf_output, qr/ $more_free=/, "But $more_free is still in perf data"); 216like( $result->perf_output, qr/'$more_free'=/, "But $more_free is still in perf data");
173 217
174$result = NPTest->testCmd( 218$result = NPTest->testCmd(
175 "./check_disk -w $avg_free% -c 0% -p $more_free" 219 "./check_disk -w $avg_free_percent% -c 0% -p $more_free"
176 ); 220 );
177cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free"); 221cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free");
178 222
179$result = NPTest->testCmd( 223$result = NPTest->testCmd(
180 "./check_disk -w $avg_free% -c $avg_free% -p $less_free" 224 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free"
181 ); 225 );
182cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free"); 226cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free");
183$result = NPTest->testCmd( 227$result = NPTest->testCmd(
184 "./check_disk -w $avg_free% -c 0% -p $more_free -w $avg_free% -c $avg_free% -p $less_free" 228 "./check_disk -w $avg_free_percent% -c 0% -p $more_free -w $avg_free_percent% -c $avg_free_percent% -p $less_free"
185 ); 229 );
186cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical"); 230cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical");
187 231
188$result = NPTest->testCmd( 232$result = NPTest->testCmd(
189 "./check_disk -w $avg_free% -c $avg_free% -p $less_free -w $avg_free% -c 0% -p $more_free" 233 "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free -w $avg_free_percent% -c 0% -p $more_free"
190 ); 234 );
191cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); 235cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference");
192 236
@@ -203,32 +247,32 @@ is( $result->return_code, 2, "Critical requesting 100% free inodes for both moun
203$result = NPTest->testCmd( "./check_disk --iwarning 1% --icritical 1% -p $more_inode_free -K 100% -W 100% -p $less_inode_free" ); 247$result = NPTest->testCmd( "./check_disk --iwarning 1% --icritical 1% -p $more_inode_free -K 100% -W 100% -p $less_inode_free" );
204is( $result->return_code, 2, "Get critical on less_inode_free mountpoint $less_inode_free"); 248is( $result->return_code, 2, "Get critical on less_inode_free mountpoint $less_inode_free");
205 249
206$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free" ); 250$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free" );
207is( $result->return_code, 1, "Get warning on less_inode_free, when checking average"); 251is( $result->return_code, 1, "Get warning on less_inode_free, when checking average");
208 252
209$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free "); 253$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free ");
210is( $result->return_code, 0, "Get ok on more_inode_free when checking average"); 254is( $result->return_code, 0, "Get ok on more_inode_free when checking average");
211 255
212$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free" ); 256$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free" );
213is ($result->return_code, 1, "Combine above two tests, get warning"); 257is ($result->return_code, 1, "Combine above two tests, get warning");
214$all_disks = $result->output; 258$all_disks = $result->output;
215 259
216$result = NPTest->testCmd( "./check_disk -e -W $avg_inode_free% -K 0% -p $less_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free" ); 260$result = NPTest->testCmd( "./check_disk -e -W $avg_inode_free_percentage% -K 0% -p $less_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free" );
217isnt( $result->output, $all_disks, "-e gives different output"); 261isnt( $result->output, $all_disks, "-e gives different output");
218like( $result->output, qr/$less_inode_free/, "Found problem $less_inode_free"); 262like( $result->output, qr/$less_inode_free/, "Found problem $less_inode_free");
219unlike( $result->only_output, qr/$more_inode_free\s/, "Has ignored $more_inode_free as not a problem"); 263unlike( $result->only_output, qr/$more_inode_free\s/, "Has ignored $more_inode_free as not a problem");
220like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data"); 264like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data");
221 265
222$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free" ); 266$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" );
223is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average"); 267is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average");
224 268
225$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" ); 269$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" );
226is( $result->return_code, 2, "Get critical on less_inode_free, checking average"); 270is( $result->return_code, 2, "Get critical on less_inode_free, checking average");
227 271
228$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" ); 272$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" );
229is( $result->return_code, 2, "Combining above two tests, get critical"); 273is( $result->return_code, 2, "Combining above two tests, get critical");
230 274
231$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free -W $avg_inode_free% -K 0% -p $more_inode_free" ); 275$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" );
232cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); 276cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference");
233 277
234 278
@@ -249,9 +293,9 @@ $result = NPTest->testCmd(
249 ); 293 );
250cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" ); 294cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" );
251 295
252$result = NPTest->testCmd( "./check_disk -w 100% -c 100% ".${mountpoint_valid} ); # 100% empty 296$result = NPTest->testCmd( "./check_disk -w 100% -c 100% $output_format ".${mountpoint_valid} ); # 100% empty
253cmp_ok( $result->return_code, "==", 2, "100% empty" ); 297cmp_ok( $result->return_code, "==", 0, "100% empty" );
254like( $result->output, $failureOutput, "Right output" ); 298like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "100% empty");
255 299
256$result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" ); 300$result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" );
257cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" ); 301cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" );
@@ -263,7 +307,8 @@ cmp_ok( $result->return_code, "==", 2, "100 TB empty" );
263# Checking old syntax of check_disk warn crit [fs], with warn/crit at USED% thresholds 307# Checking old syntax of check_disk warn crit [fs], with warn/crit at USED% thresholds
264$result = NPTest->testCmd( "./check_disk 0 0 ".${mountpoint_valid} ); 308$result = NPTest->testCmd( "./check_disk 0 0 ".${mountpoint_valid} );
265cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used"); 309cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used");
266like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments"); 310# like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments");
311# TODO not sure what the above should test, taking it out
267 312
268$result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" ); 313$result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" );
269cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" ); 314cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" );
@@ -311,8 +356,9 @@ $result = NPTest->testCmd( "./check_disk -w 0% -c 0% -p / -p /" );
311unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice"); 356unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice");
312 357
313# are partitions added if -C is given without path selection -p ? 358# are partitions added if -C is given without path selection -p ?
314$result = NPTest->testCmd( "./check_disk -w 0% -c 0% -C -w 0% -c 0% -p $mountpoint_valid" ); 359$result = NPTest->testCmd( "./check_disk -w 0% -c 0% -C -w 0% -c 0% -p $mountpoint_valid $output_format" );
315like( $result->output, '/;.*;\|/', "-C selects partitions if -p is not given"); 360cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK");
361cmp_ok(scalar $result->{'mp_test_result'}->{'checks'}, '>', 1, "-C invokes matchall logic again");
316 362
317# grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit 363# grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit
318$result = NPTest->testCmd( "./check_disk -w ". ($free_mb_on_all + 1) ." -c ". ($free_mb_on_all + 1) ." -g group -p $mountpoint_valid -p $mountpoint2_valid" ); 364$result = NPTest->testCmd( "./check_disk -w ". ($free_mb_on_all + 1) ." -c ". ($free_mb_on_all + 1) ." -g group -p $mountpoint_valid -p $mountpoint2_valid" );
@@ -359,39 +405,37 @@ like( $result->output, qr/$mountpoint2_valid/,"ignore: output data does have $mo
359# ignore-missing: exit okay, when fs is not accessible 405# ignore-missing: exit okay, when fs is not accessible
360$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p /bob"); 406$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p /bob");
361cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for not existing filesystem /bob"); 407cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for not existing filesystem /bob");
362like( $result->output, '/^DISK OK - No disks were found for provided parameters - ignored paths: /bob;.*$/', 'Output OK'); 408like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
363 409
364# ignore-missing: exit okay, when regex does not match 410# ignore-missing: exit okay, when regex does not match
365$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r /bob"); 411$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r /bob");
366cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 412cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
367like( $result->output, '/^DISK OK - No disks were found for provided parameters.*$/', 'Output OK'); 413like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
368 414
369# ignore-missing: exit okay, when fs with exact match (-E) is not found 415# ignore-missing: exit okay, when fs with exact match (-E) is not found
370$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -E -p /etc"); 416$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -E -p /etc");
371cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay when exact match does not find fs"); 417cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay when exact match does not find fs");
372like( $result->output, '/^DISK OK - No disks were found for provided parameters - ignored paths: /etc;.*$/', 'Output OK'); 418like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
373 419
374# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (regex) 420# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (regex)
375$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r '/bob' -r '^/\$'"); 421$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r '/bob' -r '^/\$'");
376cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 422cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
377like( $result->output, '/^DISK OK - free space: \/ .*$/', 'Output OK');
378 423
379# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (path) 424# ignore-missing: exit okay, when checking one existing fs and one non-existing fs (path)
380$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p '/bob' -p '/'"); 425$result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p '/bob' -p '/'");
381cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 426cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
382like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK'); 427# like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK');
383 428
384# ignore-missing: exit okay, when checking one non-existing fs (path) and one ignored 429# ignore-missing: exit okay, when checking one non-existing fs (path) and one ignored
385$result = NPTest->testCmd( "./check_disk -n -w 0% -c 0% -r /dummy -i /dummy2"); 430$result = NPTest->testCmd( "./check_disk -n -w 0% -c 0% -r /dummy -i /dummy2");
386cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 431cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
387like( $result->output, '/^DISK OK - No disks were found for provided parameters\|$/', 'Output OK'); 432like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
388 433
389# ignore-missing: exit okay, when regex match does not find anything 434# ignore-missing: exit okay, when regex match does not find anything
390$result = NPTest->testCmd( "./check_disk -n -e -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); 435$result = NPTest->testCmd( "./check_disk -n -e -l -w 10% -c 5% -W 10% -K 5% -r /dummy");
391cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 436cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
392like( $result->output, '/^DISK OK\|$/', 'Output OK');
393 437
394# ignore-missing: exit okay, when regex match does not find anything 438# ignore-missing: exit okay, when regex match does not find anything
395$result = NPTest->testCmd( "./check_disk -n -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); 439$result = NPTest->testCmd( "./check_disk -n -l -w 10% -c 5% -W 10% -K 5% -r /dummy");
396cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); 440cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching");
397like( $result->output, '/^DISK OK - No disks were found for provided parameters\|$/', 'Output OK'); 441like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK');
diff --git a/plugins/t/check_ftp.t b/plugins/t/check_ftp.t
index 93a7d7c3..a2f79dca 100644
--- a/plugins/t/check_ftp.t
+++ b/plugins/t/check_ftp.t
@@ -15,7 +15,7 @@ my $host_tcp_ftp = getTestParameter("NP_HOST_TCP_FTP", "A host providing t
15my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1"); 15my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1");
16my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); 16my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost");
17 17
18my $successOutput = '/FTP OK -\s+[0-9]?\.?[0-9]+ second response time/'; 18my $successOutput = '/Connection time\s+[0-9]?\.?[0-9]+/';
19 19
20my $t; 20my $t;
21 21
diff --git a/plugins/t/check_jabber.t b/plugins/t/check_jabber.t
index 08cadcbd..dc46f4c3 100644
--- a/plugins/t/check_jabber.t
+++ b/plugins/t/check_jabber.t
@@ -15,11 +15,11 @@ my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname
15my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); 15my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost");
16 16
17 17
18my $jabberOK = '/JABBER OK\s-\s\d+\.\d+\ssecond response time on '.$host_tcp_jabber.' port 5222/'; 18my $jabberOK = '/Connection to '.$host_tcp_jabber.' on port 5222/';
19 19
20my $jabberUnresponsive = '/Socket timeout after\s\d+\sseconds/'; 20my $jabberUnresponsive = '/Socket timeout after\s\d+\sseconds/';
21 21
22my $jabberInvalid = '/JABBER CRITICAL - Invalid hostname, address or socket:\s.+/'; 22my $jabberInvalid = '/Invalid hostname, address or socket:\s.+/';
23 23
24my $r; 24my $r;
25 25
diff --git a/plugins/t/check_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_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_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..52c5ad1c 100755
--- a/plugins/tests/check_curl.t
+++ b/plugins/tests/check_curl.t
@@ -15,6 +15,7 @@
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);
@@ -245,21 +246,21 @@ SKIP: {
245 246
246 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); 247 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" );
247 is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); 248 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" ); 249 like( $result->output, '/.*Certificate \'Monitoring Plugins\' will expire on ' . quotemeta($expiry) . '.*/', "output ok" );
249 250
250 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); 251 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" );
251 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); 252 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" ); 253 like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" );
253 254
254 # Expired cert tests 255 # Expired cert tests
255 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); 256 $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" ); 257 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" ); 258 like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" );
258 259
259 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); 260 $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" ); 261 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" );
261 is( $result->output, 262 like( $result->output,
262 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.', 263 '/.*Certificate \'Monitoring Plugins\' expired on Wed Jan\s+2 12:00:00 2008 \+0000.*/',
263 "output ok" ); 264 "output ok" );
264 265
265} 266}
@@ -274,19 +275,19 @@ SKIP: {
274 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$"; 275 $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 ); 276 $result = NPTest->testCmd( $cmd );
276 is( $result->return_code, 0, $cmd); 277 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 ); 278 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
278 279
279 # http with virtual port (!= 80) 280 # 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\$"; 281 $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 ); 282 $result = NPTest->testCmd( $cmd );
282 is( $result->return_code, 0, $cmd); 283 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 ); 284 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
284 285
285 # http with virtual port (80) 286 # 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\$"; 287 $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 ); 288 $result = NPTest->testCmd( $cmd );
288 is( $result->return_code, 0, $cmd); 289 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 ); 290 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
290} 291}
291 292
292# and the same for SSL 293# and the same for SSL
@@ -296,19 +297,19 @@ SKIP: {
296 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$"; 297 $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 ); 298 $result = NPTest->testCmd( $cmd );
298 is( $result->return_code, 0, $cmd); 299 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 ); 300 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
300 301
301 # https with virtual port (!= 443) 302 # 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\$"; 303 $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 ); 304 $result = NPTest->testCmd( $cmd );
304 is( $result->return_code, 0, $cmd); 305 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 ); 306 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
306 307
307 # https with virtual port (443) 308 # 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\$"; 309 $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 ); 310 $result = NPTest->testCmd( $cmd );
310 is( $result->return_code, 0, $cmd); 311 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 ); 312 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
312} 313}
313 314
314 315
@@ -321,165 +322,165 @@ sub run_common_tests {
321 322
322 $result = NPTest->testCmd( "$command -u /file/root" ); 323 $result = NPTest->testCmd( "$command -u /file/root" );
323 is( $result->return_code, 0, "/file/root"); 324 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" ); 325 like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" );
325 326
326 $result = NPTest->testCmd( "$command -u /file/root -s Root" ); 327 $result = NPTest->testCmd( "$command -u /file/root -s Root" );
327 is( $result->return_code, 0, "/file/root search for string"); 328 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" ); 329 like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" );
329 330
330 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" ); 331 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" );
331 is( $result->return_code, 2, "Missing string check"); 332 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"); 333 like( $result->output, qr%string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
333 334
334 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" ); 335 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" );
335 is( $result->return_code, 2, "Missing string check"); 336 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"); 337 like( $result->output, qr%string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
337 338
338 $result = NPTest->testCmd( "$command -u /header_check -d foo" ); 339 $result = NPTest->testCmd( "$command -u /header_check -d foo" );
339 is( $result->return_code, 0, "header_check search for string"); 340 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" ); 341 like( $result->output, '/.*HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second.*/', "Output correct" );
341 342
342 $result = NPTest->testCmd( "$command -u /header_check -d bar" ); 343 $result = NPTest->testCmd( "$command -u /header_check -d bar" );
343 is( $result->return_code, 2, "Missing header string check"); 344 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"); 345 like( $result->output, qr%header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location");
345 346
346 $result = NPTest->testCmd( "$command -u /header_broken_check" ); 347 $result = NPTest->testCmd( "$command -u /header_broken_check" );
347 is( $result->return_code, 0, "header_check search for string"); 348 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" ); 349 like( $result->output, '/.*HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second.*/', "Output correct" );
349 350
350 my $cmd; 351 my $cmd;
351 $cmd = "$command -u /slow"; 352 $cmd = "$command -u /slow";
352 $result = NPTest->testCmd( $cmd ); 353 $result = NPTest->testCmd( $cmd );
353 is( $result->return_code, 0, "$cmd"); 354 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 ); 355 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
355 $result->output =~ /in ([\d\.]+) second/; 356 $result->output =~ /in ([\d\.]+) second/;
356 cmp_ok( $1, ">", 1, "Time is > 1 second" ); 357 cmp_ok( $1, ">", 1, "Time is > 1 second" );
357 358
358 $cmd = "$command -u /statuscode/200"; 359 $cmd = "$command -u /statuscode/200";
359 $result = NPTest->testCmd( $cmd ); 360 $result = NPTest->testCmd( $cmd );
360 is( $result->return_code, 0, $cmd); 361 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 ); 362 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
362 363
363 $cmd = "$command -u /statuscode/200 -e 200"; 364 $cmd = "$command -u /statuscode/200 -e 200";
364 $result = NPTest->testCmd( $cmd ); 365 $result = NPTest->testCmd( $cmd );
365 is( $result->return_code, 0, $cmd); 366 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 ); 367 like( $result->output, '/.*Status line output matched "200".*/', "Output correct: ".$result->output );
367 368
368 $cmd = "$command -u /statuscode/201"; 369 $cmd = "$command -u /statuscode/201";
369 $result = NPTest->testCmd( $cmd ); 370 $result = NPTest->testCmd( $cmd );
370 is( $result->return_code, 0, $cmd); 371 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 ); 372 like( $result->output, '/.*HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
372 373
373 $cmd = "$command -u /statuscode/201 -e 201"; 374 $cmd = "$command -u /statuscode/201 -e 201";
374 $result = NPTest->testCmd( $cmd ); 375 $result = NPTest->testCmd( $cmd );
375 is( $result->return_code, 0, $cmd); 376 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 ); 377 like( $result->output, '/.*Status line output matched "201".*/', "Output correct: ".$result->output );
377 378
378 $cmd = "$command -u /statuscode/201 -e 200"; 379 $cmd = "$command -u /statuscode/201 -e 200";
379 $result = NPTest->testCmd( $cmd ); 380 $result = NPTest->testCmd( $cmd );
380 is( $result->return_code, 2, $cmd); 381 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 ); 382 like( $result->output, '/.*Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created.*/', "Output correct: ".$result->output );
382 383
383 $cmd = "$command -u /statuscode/200 -e 200,201,202"; 384 $cmd = "$command -u /statuscode/200 -e 200,201,202";
384 $result = NPTest->testCmd( $cmd ); 385 $result = NPTest->testCmd( $cmd );
385 is( $result->return_code, 0, $cmd); 386 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 ); 387 like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output );
387 388
388 $cmd = "$command -u /statuscode/201 -e 200,201,202"; 389 $cmd = "$command -u /statuscode/201 -e 200,201,202";
389 $result = NPTest->testCmd( $cmd ); 390 $result = NPTest->testCmd( $cmd );
390 is( $result->return_code, 0, $cmd); 391 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 ); 392 like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output );
392 393
393 $cmd = "$command -u /statuscode/203 -e 200,201,202"; 394 $cmd = "$command -u /statuscode/203 -e 200,201,202";
394 $result = NPTest->testCmd( $cmd ); 395 $result = NPTest->testCmd( $cmd );
395 is( $result->return_code, 2, $cmd); 396 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 ); 397 like( $result->output, '/.*Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information.*/', "Output correct: ".$result->output );
397 398
398 $cmd = "$command -j HEAD -u /method"; 399 $cmd = "$command -j HEAD -u /method";
399 $result = NPTest->testCmd( $cmd ); 400 $result = NPTest->testCmd( $cmd );
400 is( $result->return_code, 0, $cmd); 401 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 ); 402 like( $result->output, '/.*HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
402 403
403 $cmd = "$command -j POST -u /method"; 404 $cmd = "$command -j POST -u /method";
404 $result = NPTest->testCmd( $cmd ); 405 $result = NPTest->testCmd( $cmd );
405 is( $result->return_code, 0, $cmd); 406 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 ); 407 like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
407 408
408 $cmd = "$command -j GET -u /method"; 409 $cmd = "$command -j GET -u /method";
409 $result = NPTest->testCmd( $cmd ); 410 $result = NPTest->testCmd( $cmd );
410 is( $result->return_code, 0, $cmd); 411 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 ); 412 like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
412 413
413 $cmd = "$command -u /method"; 414 $cmd = "$command -u /method";
414 $result = NPTest->testCmd( $cmd ); 415 $result = NPTest->testCmd( $cmd );
415 is( $result->return_code, 0, $cmd); 416 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 ); 417 like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
417 418
418 $cmd = "$command -P foo -u /method"; 419 $cmd = "$command -P foo -u /method";
419 $result = NPTest->testCmd( $cmd ); 420 $result = NPTest->testCmd( $cmd );
420 is( $result->return_code, 0, $cmd); 421 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 ); 422 like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
422 423
423 $cmd = "$command -j DELETE -u /method"; 424 $cmd = "$command -j DELETE -u /method";
424 $result = NPTest->testCmd( $cmd ); 425 $result = NPTest->testCmd( $cmd );
425 is( $result->return_code, 1, $cmd); 426 is( $result->return_code, 1, $cmd);
426 like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output ); 427 like( $result->output, '/.*HTTP/1.1 405 Method Not Allowed.*/', "Output correct: ".$result->output );
427 428
428 $cmd = "$command -j foo -u /method"; 429 $cmd = "$command -j foo -u /method";
429 $result = NPTest->testCmd( $cmd ); 430 $result = NPTest->testCmd( $cmd );
430 is( $result->return_code, 2, $cmd); 431 is( $result->return_code, 2, $cmd);
431 like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output ); 432 like( $result->output, '/.*HTTP/1.1 501 Not Implemented.*/', "Output correct: ".$result->output );
432 433
433 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude"; 434 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude";
434 $result = NPTest->testCmd( $cmd ); 435 $result = NPTest->testCmd( $cmd );
435 is( $result->return_code, 0, $cmd); 436 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 ); 437 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
437 438
438 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude"; 439 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude";
439 $result = NPTest->testCmd( $cmd ); 440 $result = NPTest->testCmd( $cmd );
440 is( $result->return_code, 0, $cmd); 441 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 ); 442 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
442 443
443 # To confirm that the free doesn't segfault 444 # To confirm that the free doesn't segfault
444 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude"; 445 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude";
445 $result = NPTest->testCmd( $cmd ); 446 $result = NPTest->testCmd( $cmd );
446 is( $result->return_code, 0, $cmd); 447 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 ); 448 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
448 449
449 $cmd = "$command -u /redirect"; 450 $cmd = "$command -u /redirect";
450 $result = NPTest->testCmd( $cmd ); 451 $result = NPTest->testCmd( $cmd );
451 is( $result->return_code, 0, $cmd); 452 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 ); 453 like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
453 454
454 $cmd = "$command -f follow -u /redirect"; 455 $cmd = "$command -f follow -u /redirect";
455 $result = NPTest->testCmd( $cmd ); 456 $result = NPTest->testCmd( $cmd );
456 is( $result->return_code, 0, $cmd); 457 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 ); 458 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
458 459
459 $cmd = "$command -u /redirect -k 'follow: me'"; 460 $cmd = "$command -u /redirect -k 'follow: me'";
460 $result = NPTest->testCmd( $cmd ); 461 $result = NPTest->testCmd( $cmd );
461 is( $result->return_code, 0, $cmd); 462 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 ); 463 like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
463 464
464 $cmd = "$command -f follow -u /redirect -k 'follow: me'"; 465 $cmd = "$command -f follow -u /redirect -k 'follow: me'";
465 $result = NPTest->testCmd( $cmd ); 466 $result = NPTest->testCmd( $cmd );
466 is( $result->return_code, 0, $cmd); 467 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 ); 468 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
468 469
469 $cmd = "$command -f sticky -u /redirect -k 'follow: me'"; 470 $cmd = "$command -f sticky -u /redirect -k 'follow: me'";
470 $result = NPTest->testCmd( $cmd ); 471 $result = NPTest->testCmd( $cmd );
471 is( $result->return_code, 0, $cmd); 472 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 ); 473 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
473 474
474 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'"; 475 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'";
475 $result = NPTest->testCmd( $cmd ); 476 $result = NPTest->testCmd( $cmd );
476 is( $result->return_code, 0, $cmd); 477 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 ); 478 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
478 479
479 $cmd = "$command -f follow -u /redirect_rel -s redirected"; 480 $cmd = "$command -f follow -u /redirect_rel -s redirected";
480 $result = NPTest->testCmd( $cmd ); 481 $result = NPTest->testCmd( $cmd );
481 is( $result->return_code, 0, $cmd); 482 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 ); 483 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
483 484
484 # These tests may block 485 # These tests may block
485 # stickyport - on full urlS port is set back to 80 otherwise 486 # stickyport - on full urlS port is set back to 80 otherwise
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
index b85fb4ad..94d56ce7 100644
--- a/plugins/tests/test_check_swap.c
+++ b/plugins/tests/test_check_swap.c
@@ -5,9 +5,7 @@
5int verbose = 0; 5int verbose = 0;
6 6
7void print_usage(void) {} 7void print_usage(void) {}
8void print_help(swap_config config) { 8void print_help(swap_config config) { (void)config; }
9 (void) config;
10}
11 9
12const char *progname = "test_check_swap"; 10const char *progname = "test_check_swap";
13 11
diff --git a/plugins/urlize.c b/plugins/urlize.c
index 1aa4e425..a8590fae 100644
--- a/plugins/urlize.c
+++ b/plugins/urlize.c
@@ -53,8 +53,10 @@ int main(int argc, char **argv) {
53 53
54 int c; 54 int c;
55 int option = 0; 55 int option = 0;
56 static struct option longopts[] = { 56 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
57 {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"url", required_argument, 0, 'u'}, {0, 0, 0, 0}}; 57 {"version", no_argument, 0, 'V'},
58 {"url", required_argument, 0, 'u'},
59 {0, 0, 0, 0}};
58 60
59 setlocale(LC_ALL, ""); 61 setlocale(LC_ALL, "");
60 bindtextdomain(PACKAGE, LOCALEDIR); 62 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -69,8 +71,9 @@ int main(int argc, char **argv) {
69 while (1) { 71 while (1) {
70 c = getopt_long(argc, argv, "+hVu:", longopts, &option); 72 c = getopt_long(argc, argv, "+hVu:", longopts, &option);
71 73
72 if (c == -1 || c == EOF) 74 if (c == -1 || c == EOF) {
73 break; 75 break;
76 }
74 77
75 switch (c) { 78 switch (c) {
76 case 'h': /* help */ 79 case 'h': /* help */
@@ -90,8 +93,9 @@ int main(int argc, char **argv) {
90 } 93 }
91 } 94 }
92 95
93 if (url == NULL) 96 if (url == NULL) {
94 url = strdup(argv[optind++]); 97 url = strdup(argv[optind++]);
98 }
95 99
96 cmd = strdup(argv[optind++]); 100 cmd = strdup(argv[optind++]);
97 for (c = optind; c < argc; c++) { 101 for (c = optind; c < argc; c++) {
@@ -118,27 +122,32 @@ int main(int argc, char **argv) {
118 strcat(tstr, buf); 122 strcat(tstr, buf);
119 } 123 }
120 124
121 if (!found) 125 if (!found) {
122 die(STATE_UNKNOWN, _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), argv[0], cmd); 126 die(STATE_UNKNOWN, _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), argv[0],
127 cmd);
128 }
123 129
124 /* chop the newline character */ 130 /* chop the newline character */
125 if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) 131 if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) {
126 *nstr = '\0'; 132 *nstr = '\0';
133 }
127 134
128 /* tokenize the string for Perfdata if there is some */ 135 /* tokenize the string for Perfdata if there is some */
129 nstr = strtok(tstr, PERF_CHARACTER); 136 nstr = strtok(tstr, PERF_CHARACTER);
130 printf("%s", nstr); 137 printf("%s", nstr);
131 printf("</A>"); 138 printf("</A>");
132 nstr = strtok(NULL, PERF_CHARACTER); 139 nstr = strtok(NULL, PERF_CHARACTER);
133 if (nstr != NULL) 140 if (nstr != NULL) {
134 printf(" | %s", nstr); 141 printf(" | %s", nstr);
142 }
135 143
136 /* close the pipe */ 144 /* close the pipe */
137 result = spclose(child_process); 145 result = spclose(child_process);
138 146
139 /* WARNING if output found on stderr */ 147 /* WARNING if output found on stderr */
140 if (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) 148 if (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
141 result = max_state(result, STATE_WARNING); 149 result = max_state(result, STATE_WARNING);
150 }
142 151
143 /* close stderr */ 152 /* close stderr */
144 (void)fclose(child_stderr); 153 (void)fclose(child_stderr);
@@ -153,8 +162,10 @@ void print_help(void) {
153 printf(COPYRIGHT, copyright, email); 162 printf(COPYRIGHT, copyright, email);
154 163
155 printf("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>")); 164 printf("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>"));
156 printf("%s\n", _("tags, thus displaying the child plugin's output as a clickable link in compatible")); 165 printf("%s\n",
157 printf("%s\n", _("monitoring status screen. This plugin returns the status of the invoked plugin.")); 166 _("tags, thus displaying the child plugin's output as a clickable link in compatible"));
167 printf("%s\n",
168 _("monitoring status screen. This plugin returns the status of the invoked plugin."));
158 169
159 printf("\n\n"); 170 printf("\n\n");
160 171
@@ -164,7 +175,8 @@ void print_help(void) {
164 175
165 printf("\n"); 176 printf("\n");
166 printf("%s\n", _("Examples:")); 177 printf("%s\n", _("Examples:"));
167 printf("%s\n", _("Pay close attention to quoting to ensure that the shell passes the expected")); 178 printf("%s\n",
179 _("Pay close attention to quoting to ensure that the shell passes the expected"));
168 printf("%s\n\n", _("data to the plugin. For example, in:")); 180 printf("%s\n\n", _("data to the plugin. For example, in:"));
169 printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'")); 181 printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'"));
170 printf(" %s\n", _("the shell will remove the single quotes and urlize will see:")); 182 printf(" %s\n", _("the shell will remove the single quotes and urlize will see:"));
diff --git a/plugins/utils.c b/plugins/utils.c
index 34335c89..41fe5fcf 100644
--- a/plugins/utils.c
+++ b/plugins/utils.c
@@ -285,7 +285,8 @@ double delta_time(struct timeval tv) {
285 struct timeval now; 285 struct timeval now;
286 286
287 gettimeofday(&now, NULL); 287 gettimeofday(&now, NULL);
288 return ((double)(now.tv_sec - tv.tv_sec) + (double)(now.tv_usec - tv.tv_usec) / (double)1000000); 288 return ((double)(now.tv_sec - tv.tv_sec) +
289 (double)(now.tv_usec - tv.tv_usec) / (double)1000000);
289} 290}
290 291
291long deltime(struct timeval tv) { 292long deltime(struct timeval tv) {
@@ -507,8 +508,8 @@ int xasprintf(char **strp, const char *fmt, ...) {
507 * 508 *
508 ******************************************************************************/ 509 ******************************************************************************/
509 510
510char *perfdata(const char *label, long int val, const char *uom, bool warnp, long int warn, bool critp, long int crit, bool minp, 511char *perfdata(const char *label, long int val, const char *uom, bool warnp, long int warn,
511 long int minv, bool maxp, long int maxv) { 512 bool critp, long int crit, bool minp, long int minv, bool maxp, long int maxv) {
512 char *data = NULL; 513 char *data = NULL;
513 514
514 if (strpbrk(label, "'= ")) { 515 if (strpbrk(label, "'= ")) {
@@ -542,10 +543,11 @@ char *perfdata(const char *label, long int val, const char *uom, bool warnp, lon
542 return data; 543 return data;
543} 544}
544 545
545char *perfdata_uint64(const char *label, uint64_t val, const char *uom, bool warnp, /* Warning present */ 546char *perfdata_uint64(const char *label, uint64_t val, const char *uom,
546 uint64_t warn, bool critp, /* Critical present */ 547 bool warnp, /* Warning present */
547 uint64_t crit, bool minp, /* Minimum present */ 548 uint64_t warn, bool critp, /* Critical present */
548 uint64_t minv, bool maxp, /* Maximum present */ 549 uint64_t crit, bool minp, /* Minimum present */
550 uint64_t minv, bool maxp, /* Maximum present */
549 uint64_t maxv) { 551 uint64_t maxv) {
550 char *data = NULL; 552 char *data = NULL;
551 553
@@ -580,10 +582,11 @@ char *perfdata_uint64(const char *label, uint64_t val, const char *uom, bool war
580 return data; 582 return data;
581} 583}
582 584
583char *perfdata_int64(const char *label, int64_t val, const char *uom, bool warnp, /* Warning present */ 585char *perfdata_int64(const char *label, int64_t val, const char *uom,
584 int64_t warn, bool critp, /* Critical present */ 586 bool warnp, /* Warning present */
585 int64_t crit, bool minp, /* Minimum present */ 587 int64_t warn, bool critp, /* Critical present */
586 int64_t minv, bool maxp, /* Maximum present */ 588 int64_t crit, bool minp, /* Minimum present */
589 int64_t minv, bool maxp, /* Maximum present */
587 int64_t maxv) { 590 int64_t maxv) {
588 char *data = NULL; 591 char *data = NULL;
589 592
@@ -618,8 +621,8 @@ char *perfdata_int64(const char *label, int64_t val, const char *uom, bool warnp
618 return data; 621 return data;
619} 622}
620 623
621char *fperfdata(const char *label, double val, const char *uom, bool warnp, double warn, bool critp, double crit, bool minp, double minv, 624char *fperfdata(const char *label, double val, const char *uom, bool warnp, double warn, bool critp,
622 bool maxp, double maxv) { 625 double crit, bool minp, double minv, bool maxp, double maxv) {
623 char *data = NULL; 626 char *data = NULL;
624 627
625 if (strpbrk(label, "'= ")) { 628 if (strpbrk(label, "'= ")) {
@@ -655,7 +658,8 @@ char *fperfdata(const char *label, double val, const char *uom, bool warnp, doub
655 return data; 658 return data;
656} 659}
657 660
658char *sperfdata(const char *label, double val, const char *uom, char *warn, char *crit, bool minp, double minv, bool maxp, double maxv) { 661char *sperfdata(const char *label, double val, const char *uom, char *warn, char *crit, bool minp,
662 double minv, bool maxp, double maxv) {
659 char *data = NULL; 663 char *data = NULL;
660 if (strpbrk(label, "'= ")) { 664 if (strpbrk(label, "'= ")) {
661 xasprintf(&data, "'%s'=", label); 665 xasprintf(&data, "'%s'=", label);
@@ -690,7 +694,8 @@ char *sperfdata(const char *label, double val, const char *uom, char *warn, char
690 return data; 694 return data;
691} 695}
692 696
693char *sperfdata_int(const char *label, int val, const char *uom, char *warn, char *crit, bool minp, int minv, bool maxp, int maxv) { 697char *sperfdata_int(const char *label, int val, const char *uom, char *warn, char *crit, bool minp,
698 int minv, bool maxp, int maxv) {
694 char *data = NULL; 699 char *data = NULL;
695 if (strpbrk(label, "'= ")) { 700 if (strpbrk(label, "'= ")) {
696 xasprintf(&data, "'%s'=", label); 701 xasprintf(&data, "'%s'=", label);
diff --git a/plugins/utils.h b/plugins/utils.h
index 92a6c115..1f0e021b 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -76,7 +76,7 @@ char *strnl(char *);
76char *strpcpy(char *, const char *, const char *); 76char *strpcpy(char *, const char *, const char *);
77char *strpcat(char *, const char *, const char *); 77char *strpcat(char *, const char *, const char *);
78int xvasprintf(char **strp, const char *fmt, va_list ap); 78int xvasprintf(char **strp, const char *fmt, va_list ap);
79int xasprintf(char **strp, const char *fmt, ...); 79int xasprintf(char **strp, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
80 80
81void usage(const char *) __attribute__((noreturn)); 81void usage(const char *) __attribute__((noreturn));
82void usage2(const char *, const char *) __attribute__((noreturn)); 82void usage2(const char *, const char *) __attribute__((noreturn));
@@ -88,13 +88,17 @@ void usage_va(const char *fmt, ...) __attribute__((noreturn));
88#define max(a, b) (((a) > (b)) ? (a) : (b)) 88#define max(a, b) (((a) > (b)) ? (a) : (b))
89#define min(a, b) (((a) < (b)) ? (a) : (b)) 89#define min(a, b) (((a) < (b)) ? (a) : (b))
90 90
91char *perfdata(const char *, long int, const char *, bool, long int, bool, long int, bool, long int, bool, long int); 91char *perfdata(const char *, long int, const char *, bool, long int, bool, long int, bool, long int,
92 bool, long int);
92 93
93char *perfdata_uint64(const char *, uint64_t, const char *, bool, uint64_t, bool, uint64_t, bool, uint64_t, bool, uint64_t); 94char *perfdata_uint64(const char *, uint64_t, const char *, bool, uint64_t, bool, uint64_t, bool,
95 uint64_t, bool, uint64_t);
94 96
95char *perfdata_int64(const char *, int64_t, const char *, bool, int64_t, bool, int64_t, bool, int64_t, bool, int64_t); 97char *perfdata_int64(const char *, int64_t, const char *, bool, int64_t, bool, int64_t, bool,
98 int64_t, bool, int64_t);
96 99
97char *fperfdata(const char *, double, const char *, bool, double, bool, double, bool, double, bool, double); 100char *fperfdata(const char *, double, const char *, bool, double, bool, double, bool, double, bool,
101 double);
98 102
99char *sperfdata(const char *, double, const char *, char *, char *, bool, double, bool, double); 103char *sperfdata(const char *, double, const char *, char *, char *, bool, double, bool, double);
100 104
@@ -104,21 +108,22 @@ char *sperfdata_int(const char *, int, const char *, char *, char *, bool, int,
104 most will or should. Therefore, for consistency, these very common 108 most will or should. Therefore, for consistency, these very common
105 options should have only these meanings throughout the overall suite */ 109 options should have only these meanings throughout the overall suite */
106 110
107#define STD_LONG_OPTS \ 111#define STD_LONG_OPTS \
108 {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, \ 112 {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, \
109 {"timeout", required_argument, 0, 't'}, {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, \ 113 {"help", no_argument, 0, 'h'}, {"timeout", required_argument, 0, 't'}, \
114 {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, \
110 {"hostname", required_argument, 0, 'H'} 115 {"hostname", required_argument, 0, 'H'}
111 116
112#define COPYRIGHT \ 117#define COPYRIGHT \
113 "Copyright (c) %s Monitoring Plugins Development Team\n\ 118 "Copyright (c) %s Monitoring Plugins Development Team\n\
114\t<%s>\n\n" 119\t<%s>\n\n"
115 120
116#define UT_HLP_VRS \ 121#define UT_HLP_VRS \
117 _("\ 122 _("\
118 %s (-h | --help) for detailed help\n\ 123 %s (-h | --help) for detailed help\n\
119 %s (-V | --version) for version information\n") 124 %s (-V | --version) for version information\n")
120 125
121#define UT_HELP_VRSN \ 126#define UT_HELP_VRSN \
122 _("\ 127 _("\
123\nOptions:\n\ 128\nOptions:\n\
124 -h, --help\n\ 129 -h, --help\n\
@@ -126,52 +131,52 @@ char *sperfdata_int(const char *, int, const char *, char *, char *, bool, int,
126 -V, --version\n\ 131 -V, --version\n\
127 Print version information\n") 132 Print version information\n")
128 133
129#define UT_HOST_PORT \ 134#define UT_HOST_PORT \
130 _("\ 135 _("\
131 -H, --hostname=ADDRESS\n\ 136 -H, --hostname=ADDRESS\n\
132 Host name, IP Address, or unix socket (must be an absolute path)\n\ 137 Host name, IP Address, or unix socket (must be an absolute path)\n\
133 -%c, --port=INTEGER\n\ 138 -%c, --port=INTEGER\n\
134 Port number (default: %s)\n") 139 Port number (default: %s)\n")
135 140
136#define UT_IPv46 \ 141#define UT_IPv46 \
137 _("\ 142 _("\
138 -4, --use-ipv4\n\ 143 -4, --use-ipv4\n\
139 Use IPv4 connection\n\ 144 Use IPv4 connection\n\
140 -6, --use-ipv6\n\ 145 -6, --use-ipv6\n\
141 Use IPv6 connection\n") 146 Use IPv6 connection\n")
142 147
143#define UT_VERBOSE \ 148#define UT_VERBOSE \
144 _("\ 149 _("\
145 -v, --verbose\n\ 150 -v, --verbose\n\
146 Show details for command-line debugging (output may be truncated by\n\ 151 Show details for command-line debugging (output may be truncated by\n\
147 the monitoring system)\n") 152 the monitoring system)\n")
148 153
149#define UT_WARN_CRIT \ 154#define UT_WARN_CRIT \
150 _("\ 155 _("\
151 -w, --warning=DOUBLE\n\ 156 -w, --warning=DOUBLE\n\
152 Response time to result in warning status (seconds)\n\ 157 Response time to result in warning status (seconds)\n\
153 -c, --critical=DOUBLE\n\ 158 -c, --critical=DOUBLE\n\
154 Response time to result in critical status (seconds)\n") 159 Response time to result in critical status (seconds)\n")
155 160
156#define UT_WARN_CRIT_RANGE \ 161#define UT_WARN_CRIT_RANGE \
157 _("\ 162 _("\
158 -w, --warning=RANGE\n\ 163 -w, --warning=RANGE\n\
159 Warning range (format: start:end). Alert if outside this range\n\ 164 Warning range (format: start:end). Alert if outside this range\n\
160 -c, --critical=RANGE\n\ 165 -c, --critical=RANGE\n\
161 Critical range\n") 166 Critical range\n")
162 167
163#define UT_CONN_TIMEOUT \ 168#define UT_CONN_TIMEOUT \
164 _("\ 169 _("\
165 -t, --timeout=INTEGER\n\ 170 -t, --timeout=INTEGER\n\
166 Seconds before connection times out (default: %d)\n") 171 Seconds before connection times out (default: %d)\n")
167 172
168#define UT_PLUG_TIMEOUT \ 173#define UT_PLUG_TIMEOUT \
169 _("\ 174 _("\
170 -t, --timeout=INTEGER\n\ 175 -t, --timeout=INTEGER\n\
171 Seconds before plugin times out (default: %d)\n") 176 Seconds before plugin times out (default: %d)\n")
172 177
173#ifdef NP_EXTRA_OPTS 178#ifdef NP_EXTRA_OPTS
174# define UT_EXTRA_OPTS \ 179# define UT_EXTRA_OPTS \
175 _("\ 180 _("\
176 --extra-opts=[section][@file]\n\ 181 --extra-opts=[section][@file]\n\
177 Read options from an ini file. See\n\ 182 Read options from an ini file. See\n\
@@ -181,25 +186,25 @@ char *sperfdata_int(const char *, int, const char *, char *, char *, bool, int,
181# define UT_EXTRA_OPTS " \b" 186# define UT_EXTRA_OPTS " \b"
182#endif 187#endif
183 188
184#define UT_THRESHOLDS_NOTES \ 189#define UT_THRESHOLDS_NOTES \
185 _("\ 190 _("\
186 See:\n\ 191 See:\n\
187 https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT\n\ 192 https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT\n\
188 for THRESHOLD format and examples.\n") 193 for THRESHOLD format and examples.\n")
189 194
190#define UT_SUPPORT \ 195#define UT_SUPPORT \
191 _("\n\ 196 _("\n\
192Send email to help@monitoring-plugins.org if you have questions regarding\n\ 197Send 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\ 198use of this software. To submit patches or suggest improvements, send email\n\
194to devel@monitoring-plugins.org\n\n") 199to devel@monitoring-plugins.org\n\n")
195 200
196#define UT_NOWARRANTY \ 201#define UT_NOWARRANTY \
197 _("\n\ 202 _("\n\
198The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\ 203The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\
199copies of the plugins under the terms of the GNU General Public License.\n\ 204copies of the plugins under the terms of the GNU General Public License.\n\
200For more information about these matters, see the file named COPYING.\n") 205For more information about these matters, see the file named COPYING.\n")
201 206
202#define UT_OUTPUT_FORMAT \ 207#define UT_OUTPUT_FORMAT \
203 _("\ 208 _("\
204 --output-format=OUTPUT_FORMAT\n\ 209 --output-format=OUTPUT_FORMAT\n\
205 Select output format. Valid values: \"multi-line\", \"mp-test-json\"\n") 210 Select output format. Valid values: \"multi-line\", \"mp-test-json\"\n")