summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am93
-rw-r--r--plugins/check_apt.c824
-rw-r--r--plugins/check_apt.d/config.h46
-rw-r--r--plugins/check_by_ssh.c699
-rw-r--r--plugins/check_by_ssh.d/config.h58
-rw-r--r--plugins/check_cluster.c351
-rw-r--r--plugins/check_cluster.d/config.h33
-rw-r--r--plugins/check_curl.c4203
-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.c1080
-rw-r--r--plugins/check_dbi.d/config.h63
-rw-r--r--plugins/check_dig.c667
-rw-r--r--plugins/check_dig.d/config.h40
-rw-r--r--plugins/check_disk.c2341
-rw-r--r--plugins/check_disk.d/utils_disk.c528
-rw-r--r--plugins/check_disk.d/utils_disk.h160
-rw-r--r--plugins/check_dns.c1205
-rw-r--r--plugins/check_dns.d/config.h34
-rw-r--r--plugins/check_dummy.c198
-rw-r--r--plugins/check_fping.c1050
-rw-r--r--plugins/check_fping.d/config.h81
-rw-r--r--plugins/check_game.c604
-rw-r--r--plugins/check_game.d/config.h30
-rw-r--r--plugins/check_hpjd.c543
-rw-r--r--plugins/check_hpjd.d/config.h25
-rw-r--r--plugins/check_http.c3583
-rw-r--r--plugins/check_ide_smart.c580
-rw-r--r--plugins/check_ldap.c618
-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.c552
-rw-r--r--plugins/check_mrtg.d/config.h36
-rw-r--r--plugins/check_mrtgtraf.c511
-rw-r--r--plugins/check_mrtgtraf.d/config.h30
-rw-r--r--plugins/check_mysql.c838
-rw-r--r--plugins/check_mysql.d/config.h58
-rw-r--r--plugins/check_mysql_query.c470
-rw-r--r--plugins/check_mysql_query.d/config.h36
-rw-r--r--plugins/check_nagios.c422
-rw-r--r--plugins/check_nagios.d/config.h19
-rw-r--r--plugins/check_nt.c1218
-rw-r--r--plugins/check_nt.d/config.h53
-rw-r--r--plugins/check_ntp.c762
-rw-r--r--plugins/check_ntp_peer.c869
-rw-r--r--plugins/check_ntp_peer.d/config.h67
-rw-r--r--plugins/check_ntp_time.c711
-rw-r--r--plugins/check_ntp_time.d/config.h28
-rw-r--r--plugins/check_nwstat.c1740
-rw-r--r--plugins/check_overcr.c469
-rw-r--r--plugins/check_pgsql.c794
-rw-r--r--plugins/check_pgsql.d/config.h61
-rw-r--r--plugins/check_ping.c903
-rw-r--r--plugins/check_ping.d/config.h46
-rw-r--r--plugins/check_procs.c1186
-rw-r--r--plugins/check_procs.d/config.h75
-rw-r--r--plugins/check_radius.c557
-rw-r--r--plugins/check_radius.d/config.h42
-rw-r--r--plugins/check_real.c579
-rw-r--r--plugins/check_real.d/config.h37
-rw-r--r--plugins/check_smtp.c1196
-rw-r--r--plugins/check_smtp.d/config.h92
-rw-r--r--plugins/check_snmp.c2074
-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.c523
-rw-r--r--plugins/check_ssh.d/config.h29
-rw-r--r--plugins/check_swap.c844
-rw-r--r--plugins/check_swap.d/check_swap.h49
-rw-r--r--plugins/check_swap.d/swap.c471
-rw-r--r--plugins/check_tcp.c1124
-rw-r--r--plugins/check_tcp.d/config.h84
-rw-r--r--plugins/check_time.c527
-rw-r--r--plugins/check_time.d/config.h42
-rw-r--r--plugins/check_ups.c377
-rw-r--r--plugins/check_ups.d/config.h54
-rw-r--r--plugins/check_users.c433
-rw-r--r--plugins/check_users.d/config.h20
-rw-r--r--plugins/check_users.d/users.c169
-rw-r--r--plugins/check_users.d/users.h18
-rw-r--r--plugins/common.h200
-rw-r--r--plugins/negate.c364
-rw-r--r--plugins/negate.d/config.h24
-rw-r--r--plugins/netutils.c447
-rw-r--r--plugins/netutils.h152
-rw-r--r--plugins/picohttpparser/picohttpparser.c1140
-rw-r--r--plugins/picohttpparser/picohttpparser.h31
-rw-r--r--plugins/popen.c322
-rw-r--r--plugins/popen.h20
-rw-r--r--plugins/runcmd.c278
-rw-r--r--plugins/runcmd.h47
-rw-r--r--plugins/sslutils.c444
-rw-r--r--plugins/t/check_apt.t14
-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_http.t2
-rw-r--r--plugins/t/check_jabber.t6
-rw-r--r--plugins/t/check_ldap.t2
-rw-r--r--plugins/t/check_load.t18
-rw-r--r--plugins/t/check_mysql.t32
-rw-r--r--plugins/t/check_ntp.t2
-rw-r--r--plugins/t/check_smtp.t3
-rw-r--r--plugins/t/check_snmp.t103
-rw-r--r--plugins/t/check_ssh.t114
-rw-r--r--plugins/t/check_swap.t58
-rw-r--r--plugins/t/check_tcp.t12
-rw-r--r--plugins/t/check_udp.t4
-rw-r--r--plugins/t/check_users.t4
-rwxr-xr-xplugins/tests/check_curl.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.c21
-rwxr-xr-xplugins/tests/test_check_swap.t6
-rw-r--r--plugins/tests/var/proc_meminfo55
-rw-r--r--plugins/urlize.c219
-rw-r--r--plugins/utils.c729
-rw-r--r--plugins/utils.h159
126 files changed, 26937 insertions, 23471 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 49086b7a..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,30 +33,82 @@ MATHLIBS = @MATHLIBS@
27#AM_CFLAGS = -Wall 33#AM_CFLAGS = -Wall
28 34
29libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ 35libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \
30 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_nwstat check_overcr check_ping \ 36 check_mrtg check_mrtgtraf check_ntp 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 \
49 \
50 tests/test_check_swap \
51 tests/test_check_snmp \
52 tests/test_check_disk
42 53
43SUBDIRS = picohttpparser 54SUBDIRS = picohttpparser
44 55
45EXTRA_DIST = t tests 56np_test_scripts = tests/test_check_swap.t \
57 tests/test_check_snmp.t \
58 tests/test_check_disk.t
59
60EXTRA_DIST = t \
61 tests \
62 $(np_test_scripts) \
63 negate.d \
64 check_swap.d \
65 check_ldap.d \
66 check_hpjd.d \
67 check_game.d \
68 check_radius.d \
69 check_curl.d \
70 check_disk.d \
71 check_time.d \
72 check_users.d \
73 check_load.d \
74 check_nagios.d \
75 check_dbi.d \
76 check_tcp.d \
77 check_real.d \
78 check_ssh.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
46 100
47PLUGINHDRS = common.h 101PLUGINHDRS = common.h
48 102
49noinst_LIBRARIES = libnpcommon.a 103noinst_LIBRARIES = libnpcommon.a
104noinst_PROGRAMS = @EXTRA_PLUGIN_TESTS@
105# These two lines support "make check", but we use "make test"
106check_PROGRAMS = @EXTRA_PLUGIN_TESTS@
50 107
51libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \ 108libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \
52 popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h 109 popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h
53 110
111
54BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a $(LIB_CRYPTO) 112BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a $(LIB_CRYPTO)
55NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS) 113NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS)
56NETLIBS = $(NETOBJS) $(SOCKETLIBS) 114NETLIBS = $(NETOBJS) $(SOCKETLIBS)
@@ -58,7 +116,10 @@ SSLOBJS = $(BASEOBJS) $(NETLIBS) $(SSLLIBS) $(LIB_CRYPTO)
58 116
59TESTS_ENVIRONMENT = perl -I $(top_builddir) -I $(top_srcdir) 117TESTS_ENVIRONMENT = perl -I $(top_builddir) -I $(top_srcdir)
60 118
61TESTS = @PLUGIN_TEST@ 119tap_ldflags = -L$(top_srcdir)/tap
120
121TESTS = @PLUGIN_TEST@ @EXTRA_PLUGIN_TESTS@
122
62 123
63test: 124test:
64 perl -I $(top_builddir) -I $(top_srcdir) ../test.pl 125 perl -I $(top_builddir) -I $(top_srcdir) ../test.pl
@@ -74,9 +135,11 @@ check_cluster_LDADD = $(BASEOBJS)
74check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 135check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
75check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 136check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
76check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a 137check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a
138check_curl_SOURCES = check_curl.c check_curl.d/check_curl_helpers.c
77check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 139check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
78check_dig_LDADD = $(NETLIBS) 140check_dig_LDADD = $(NETLIBS)
79check_disk_LDADD = $(BASEOBJS) 141check_disk_LDADD = $(BASEOBJS)
142check_disk_SOURCES = check_disk.c check_disk.d/utils_disk.c
80check_dns_LDADD = $(NETLIBS) 143check_dns_LDADD = $(NETLIBS)
81check_dummy_LDADD = $(BASEOBJS) 144check_dummy_LDADD = $(BASEOBJS)
82check_fping_LDADD = $(NETLIBS) 145check_fping_LDADD = $(NETLIBS)
@@ -97,21 +160,24 @@ check_nagios_LDADD = $(BASEOBJS)
97check_nt_LDADD = $(NETLIBS) 160check_nt_LDADD = $(NETLIBS)
98check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) 161check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
99check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 162check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
100check_nwstat_LDADD = $(NETLIBS)
101check_overcr_LDADD = $(NETLIBS)
102check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) 163check_pgsql_LDADD = $(NETLIBS) $(PGLIBS)
103check_ping_LDADD = $(NETLIBS) 164check_ping_LDADD = $(NETLIBS)
104check_procs_LDADD = $(BASEOBJS) 165check_procs_LDADD = $(BASEOBJS)
105check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) 166check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS)
106check_real_LDADD = $(NETLIBS) 167check_real_LDADD = $(NETLIBS)
168check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c
107check_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`
108check_smtp_LDADD = $(SSLOBJS) 172check_smtp_LDADD = $(SSLOBJS)
109check_ssh_LDADD = $(NETLIBS) 173check_ssh_LDADD = $(NETLIBS)
174check_swap_SOURCES = check_swap.c check_swap.d/swap.c
110check_swap_LDADD = $(MATHLIBS) $(BASEOBJS) 175check_swap_LDADD = $(MATHLIBS) $(BASEOBJS)
111check_tcp_LDADD = $(SSLOBJS) 176check_tcp_LDADD = $(SSLOBJS)
112check_time_LDADD = $(NETLIBS) 177check_time_LDADD = $(NETLIBS)
113check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) 178check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS)
114check_ups_LDADD = $(NETLIBS) 179check_ups_LDADD = $(NETLIBS)
180check_users_SOURCES = check_users.c check_users.d/users.c
115check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) 181check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS)
116check_by_ssh_LDADD = $(NETLIBS) 182check_by_ssh_LDADD = $(NETLIBS)
117check_ide_smart_LDADD = $(BASEOBJS) 183check_ide_smart_LDADD = $(BASEOBJS)
@@ -122,6 +188,13 @@ if !HAVE_UTMPX
122check_users_LDADD += popen.o 188check_users_LDADD += popen.o
123endif 189endif
124 190
191tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
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
197
125############################################################################## 198##############################################################################
126# secondary dependencies 199# secondary dependencies
127 200
diff --git a/plugins/check_apt.c b/plugins/check_apt.c
index 5c0f6e28..9ed5b6cf 100644
--- a/plugins/check_apt.c
+++ b/plugins/check_apt.c
@@ -1,188 +1,250 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_apt plugin 3 * Monitoring check_apt plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
7* 7 *
8* Original author: Sean Finney 8 * Original author: Sean Finney
9* 9 *
10* Description: 10 * Description:
11* 11 *
12* This file contains the check_apt plugin 12 * This file contains the check_apt plugin
13* 13 *
14* Check for available updates in apt package management systems 14 * Check for available updates in apt package management systems
15* 15 *
16* 16 *
17* This program is free software: you can redistribute it and/or modify 17 * This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by 18 * it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or 19 * the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version. 20 * (at your option) any later version.
21* 21 *
22* This program is distributed in the hope that it will be useful, 22 * This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details. 25 * GNU General Public License for more details.
26* 26 *
27* You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>. 28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "perfdata.h"
32const char *progname = "check_apt"; 33const char *progname = "check_apt";
33const char *copyright = "2006-2008"; 34const char *copyright = "2006-2024";
34const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
35 36
37#include "states.h"
38#include "output.h"
36#include "common.h" 39#include "common.h"
37#include "runcmd.h" 40#include "runcmd.h"
38#include "utils.h" 41#include "utils.h"
39#include "regex.h" 42#include "regex.h"
43#include "check_apt.d/config.h"
40 44
41/* some constants */
42typedef enum { UPGRADE, DIST_UPGRADE, NO_UPGRADE } upgrade_type;
43
44/* Character for hidden input file option (for testing). */
45#define INPUT_FILE_OPT CHAR_MAX+1
46/* the default opts can be overridden via the cmdline */ 45/* the default opts can be overridden via the cmdline */
47#define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" 46const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq";
48#define UPDATE_DEFAULT_OPTS "-q" 47const char *UPDATE_DEFAULT_OPTS = "-q";
48
49/* until i commit the configure.in patch which gets this, i'll define 49/* until i commit the configure.in patch which gets this, i'll define
50 * it here as well */ 50 * it here as well */
51#ifndef PATH_TO_APTGET 51#ifndef PATH_TO_APTGET
52# define PATH_TO_APTGET "/usr/bin/apt-get" 52# define PATH_TO_APTGET "/usr/bin/apt-get"
53#endif /* PATH_TO_APTGET */ 53#endif /* PATH_TO_APTGET */
54
54/* String found at the beginning of the apt output lines we're interested in */ 55/* String found at the beginning of the apt output lines we're interested in */
55#define PKGINST_PREFIX "Inst " 56const char *PKGINST_PREFIX = "Inst ";
56/* the RE that catches security updates */ 57/* the RE that catches security updates */
57#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" 58const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)";
58 59
59/* some standard functions */ 60/* some standard functions */
60int process_arguments(int, char **); 61typedef struct {
61void print_help(void); 62 int errorcode;
63 check_apt_config config;
64} check_apt_config_wrapper;
65static check_apt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
66static void print_help(void);
62void print_usage(void); 67void print_usage(void);
63 68
64/* construct the appropriate apt-get cmdline */ 69/* construct the appropriate apt-get cmdline */
65char* construct_cmdline(upgrade_type u, const char *opts); 70static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/);
71
66/* run an apt-get update */ 72/* run an apt-get update */
67int run_update(void); 73typedef struct {
74 mp_subcheck sc;
75 bool stderr_warning;
76 bool exec_warning;
77} run_update_result;
78static run_update_result run_update(char *update_opts);
79
80typedef struct {
81 int errorcode;
82 size_t package_count;
83 size_t security_package_count;
84 char **packages_list;
85 char **secpackages_list;
86 bool exec_warning;
87} run_upgrade_result;
88
68/* run an apt-get upgrade */ 89/* run an apt-get upgrade */
69int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkglist); 90run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude,
91 const char *do_critical, const char *upgrade_opts,
92 const char *input_filename);
93
70/* add another clause to a regexp */ 94/* add another clause to a regexp */
71char* add_to_regexp(char *expr, const char *next); 95static char *add_to_regexp(char * /*expr*/, const char * /*next*/);
72/* extract package name from Inst line */ 96/* extract package name from Inst line */
73char* pkg_name(char *line); 97static char *pkg_name(char * /*line*/);
74/* string comparison function for qsort */ 98/* string comparison function for qsort */
75int cmpstringp(const void *p1, const void *p2); 99static int cmpstringp(const void * /*p1*/, const void * /*p2*/);
76 100
77/* configuration variables */ 101/* configuration variables */
78static int verbose = 0; /* -v */ 102static int verbose = 0; /* -v */
79static bool list = false; /* list packages available for upgrade */
80static bool do_update = false; /* whether to call apt-get update */
81static bool only_critical = false; /* whether to warn about non-critical updates */
82static upgrade_type upgrade = UPGRADE; /* which type of upgrade to do */
83static char *upgrade_opts = NULL; /* options to override defaults for upgrade */
84static char *update_opts = NULL; /* options to override defaults for update */
85static char *do_include = NULL; /* regexp to only include certain packages */
86static char *do_exclude = NULL; /* regexp to only exclude certain packages */
87static char *do_critical = NULL; /* regexp specifying critical packages */
88static char *input_filename = NULL; /* input filename for testing */
89/* number of packages available for upgrade to return WARNING status */
90static int packages_warning = 1;
91 103
92/* other global variables */ 104/* other global variables */
93static int stderr_warning = 0; /* if a cmd issued output on stderr */ 105static bool stderr_warning = false; /* if a cmd issued output on stderr */
94static int exec_warning = 0; /* if a cmd exited non-zero */ 106static bool exec_warning = false; /* if a cmd exited non-zero */
95
96int main (int argc, char **argv) {
97 int result=STATE_UNKNOWN, packages_available=0, sec_count=0;
98 char **packages_list=NULL, **secpackages_list=NULL;
99 107
108int main(int argc, char **argv) {
100 /* Parse extra opts if any */ 109 /* Parse extra opts if any */
101 argv=np_extra_opts(&argc, argv, progname); 110 argv = np_extra_opts(&argc, argv, progname);
102 111
103 if (process_arguments(argc, argv) == ERROR) 112 check_apt_config_wrapper tmp_config = process_arguments(argc, argv);
113
114 if (tmp_config.errorcode == ERROR) {
104 usage_va(_("Could not parse arguments")); 115 usage_va(_("Could not parse arguments"));
116 }
117
118 const check_apt_config config = tmp_config.config;
119
120 if (config.output_format_is_set) {
121 mp_set_format(config.output_format);
122 }
105 123
106 /* Set signal handling and alarm timeout */ 124 /* Set signal handling and alarm timeout */
107 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 125 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
108 usage_va(_("Cannot catch SIGALRM")); 126 usage_va(_("Cannot catch SIGALRM"));
109 } 127 }
110 128
111 /* handle timeouts gracefully... */ 129 /* handle timeouts gracefully... */
112 alarm (timeout_interval); 130 alarm(timeout_interval);
113 131
132 mp_check overall = mp_check_init();
114 /* if they want to run apt-get update first... */ 133 /* if they want to run apt-get update first... */
115 if(do_update) result = run_update(); 134 if (config.do_update) {
135 run_update_result update_result = run_update(config.update_opts);
136
137 mp_add_subcheck_to_check(&overall, update_result.sc);
138 }
116 139
117 /* apt-get upgrade */ 140 /* apt-get upgrade */
118 result = max_state(result, run_upgrade(&packages_available, &sec_count, &packages_list, &secpackages_list)); 141 run_upgrade_result upgrad_res =
119 142 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical,
120 if(sec_count > 0){ 143 config.upgrade_opts, config.input_filename);
121 result = max_state(result, STATE_CRITICAL); 144
122 } else if(packages_available >= packages_warning && only_critical == false){ 145 mp_subcheck sc_run_upgrade = mp_subcheck_init();
123 result = max_state(result, STATE_WARNING); 146 if (upgrad_res.errorcode == OK) {
124 } else if(result > STATE_UNKNOWN){ 147 sc_run_upgrade = mp_set_subcheck_state(sc_run_upgrade, STATE_OK);
125 result = STATE_UNKNOWN; 148 }
149 xasprintf(&sc_run_upgrade.output, "Executed apt upgrade (dry run)");
150
151 mp_add_subcheck_to_check(&overall, sc_run_upgrade);
152
153 size_t packages_available = upgrad_res.package_count;
154 size_t number_of_security_updates = upgrad_res.security_package_count;
155 char **packages_list = upgrad_res.packages_list;
156 char **secpackages_list = upgrad_res.secpackages_list;
157
158 mp_perfdata pd_security_updates = perfdata_init();
159 pd_security_updates.value = mp_create_pd_value(number_of_security_updates);
160 pd_security_updates.label = "critical_updates";
161
162 mp_subcheck sc_security_updates = mp_subcheck_init();
163 xasprintf(&sc_security_updates.output, "Security updates available: %zu",
164 number_of_security_updates);
165 mp_add_perfdata_to_subcheck(&sc_security_updates, pd_security_updates);
166
167 if (number_of_security_updates > 0) {
168 sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_CRITICAL);
169 } else {
170 sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_OK);
171 }
172
173 mp_perfdata pd_other_updates = perfdata_init();
174 pd_other_updates.value = mp_create_pd_value(packages_available);
175 pd_other_updates.label = "available_upgrades";
176
177 mp_subcheck sc_other_updates = mp_subcheck_init();
178
179 xasprintf(&sc_other_updates.output, "Updates available: %zu", packages_available);
180 sc_other_updates = mp_set_subcheck_default_state(sc_other_updates, STATE_OK);
181 mp_add_perfdata_to_subcheck(&sc_other_updates, pd_other_updates);
182
183 if (packages_available >= config.packages_warning && !config.only_critical) {
184 sc_other_updates = mp_set_subcheck_state(sc_other_updates, STATE_WARNING);
126 } 185 }
127 186
128 printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), 187 if (config.list) {
129 state_text(result), 188 qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp);
130 packages_available, 189 qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *),
131 (upgrade==DIST_UPGRADE)?"dist-upgrade":"upgrade", 190 cmpstringp);
132 sec_count, 191
133 (stderr_warning)?" warnings detected":"", 192 for (size_t i = 0; i < number_of_security_updates; i++) {
134 (stderr_warning && exec_warning)?",":"", 193 xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output,
135 (exec_warning)?" errors detected":"", 194 secpackages_list[i]);
136 (stderr_warning||exec_warning)?".":"", 195 }
137 packages_available, 196
138 sec_count 197 if (!config.only_critical) {
139 ); 198 for (size_t i = 0; i < packages_available - number_of_security_updates; i++) {
140 199 xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output,
141 if(list) { 200 packages_list[i]);
142 qsort(secpackages_list, sec_count, sizeof(char*), cmpstringp); 201 }
143 qsort(packages_list, packages_available-sec_count, sizeof(char*), cmpstringp);
144
145 for(int i = 0; i < sec_count; i++)
146 printf("%s (security)\n", secpackages_list[i]);
147
148 if (only_critical == false) {
149 for(int i = 0; i < packages_available - sec_count; i++)
150 printf("%s\n", packages_list[i]);
151 } 202 }
152 } 203 }
204 mp_add_subcheck_to_check(&overall, sc_security_updates);
205 mp_add_subcheck_to_check(&overall, sc_other_updates);
153 206
154 return result; 207 mp_exit(overall);
155} 208}
156 209
157/* process command-line arguments */ 210/* process command-line arguments */
158int process_arguments (int argc, char **argv) { 211check_apt_config_wrapper process_arguments(int argc, char **argv) {
159 int c; 212 enum {
160 213 /* Character for hidden input file option (for testing). */
161 static struct option longopts[] = { 214 INPUT_FILE_OPT = CHAR_MAX + 1,
162 {"version", no_argument, 0, 'V'}, 215 output_format_index,
163 {"help", no_argument, 0, 'h'}, 216 };
164 {"verbose", no_argument, 0, 'v'}, 217 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
165 {"timeout", required_argument, 0, 't'}, 218 {"help", no_argument, 0, 'h'},
166 {"update", optional_argument, 0, 'u'}, 219 {"verbose", no_argument, 0, 'v'},
167 {"upgrade", optional_argument, 0, 'U'}, 220 {"timeout", required_argument, 0, 't'},
168 {"no-upgrade", no_argument, 0, 'n'}, 221 {"update", optional_argument, 0, 'u'},
169 {"dist-upgrade", optional_argument, 0, 'd'}, 222 {"upgrade", optional_argument, 0, 'U'},
170 {"list", no_argument, false, 'l'}, 223 {"no-upgrade", no_argument, 0, 'n'},
171 {"include", required_argument, 0, 'i'}, 224 {"dist-upgrade", optional_argument, 0, 'd'},
172 {"exclude", required_argument, 0, 'e'}, 225 {"list", no_argument, 0, 'l'},
173 {"critical", required_argument, 0, 'c'}, 226 {"include", required_argument, 0, 'i'},
174 {"only-critical", no_argument, 0, 'o'}, 227 {"exclude", required_argument, 0, 'e'},
175 {"input-file", required_argument, 0, INPUT_FILE_OPT}, 228 {"critical", required_argument, 0, 'c'},
176 {"packages-warning", required_argument, 0, 'w'}, 229 {"only-critical", no_argument, 0, 'o'},
177 {0, 0, 0, 0} 230 {"input-file", required_argument, 0, INPUT_FILE_OPT},
231 {"packages-warning", required_argument, 0, 'w'},
232 {"output-format", required_argument, 0, output_format_index},
233 {0, 0, 0, 0}};
234
235 check_apt_config_wrapper result = {
236 .errorcode = OK,
237 .config = check_apt_config_init(),
178 }; 238 };
179 239
180 while(1) { 240 while (true) {
181 c = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL); 241 int option_char = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL);
182 242
183 if(c == -1 || c == EOF || c == 1) break; 243 if (option_char == -1 || option_char == EOF || option_char == 1) {
244 break;
245 }
184 246
185 switch(c) { 247 switch (option_char) {
186 case 'h': 248 case 'h':
187 print_help(); 249 print_help();
188 exit(STATE_UNKNOWN); 250 exit(STATE_UNKNOWN);
@@ -193,124 +255,153 @@ int process_arguments (int argc, char **argv) {
193 verbose++; 255 verbose++;
194 break; 256 break;
195 case 't': 257 case 't':
196 timeout_interval=atoi(optarg); 258 timeout_interval = atoi(optarg);
197 break; 259 break;
198 case 'd': 260 case 'd':
199 upgrade=DIST_UPGRADE; 261 result.config.upgrade = DIST_UPGRADE;
200 if(optarg!=NULL){ 262 if (optarg != NULL) {
201 upgrade_opts=strdup(optarg); 263 result.config.upgrade_opts = strdup(optarg);
202 if(upgrade_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 264 if (result.config.upgrade_opts == NULL) {
265 die(STATE_UNKNOWN, "strdup failed");
266 }
203 } 267 }
204 break; 268 break;
205 case 'U': 269 case 'U':
206 upgrade=UPGRADE; 270 result.config.upgrade = UPGRADE;
207 if(optarg!=NULL){ 271 if (optarg != NULL) {
208 upgrade_opts=strdup(optarg); 272 result.config.upgrade_opts = strdup(optarg);
209 if(upgrade_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 273 if (result.config.upgrade_opts == NULL) {
274 die(STATE_UNKNOWN, "strdup failed");
275 }
210 } 276 }
211 break; 277 break;
212 case 'n': 278 case 'n':
213 upgrade=NO_UPGRADE; 279 result.config.upgrade = NO_UPGRADE;
214 break; 280 break;
215 case 'u': 281 case 'u':
216 do_update=true; 282 result.config.do_update = true;
217 if(optarg!=NULL){ 283 if (optarg != NULL) {
218 update_opts=strdup(optarg); 284 result.config.update_opts = strdup(optarg);
219 if(update_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 285 if (result.config.update_opts == NULL) {
286 die(STATE_UNKNOWN, "strdup failed");
287 }
220 } 288 }
221 break; 289 break;
222 case 'l': 290 case 'l':
223 list=true; 291 result.config.list = true;
224 break; 292 break;
225 case 'i': 293 case 'i':
226 do_include=add_to_regexp(do_include, optarg); 294 result.config.do_include = add_to_regexp(result.config.do_include, optarg);
227 break; 295 break;
228 case 'e': 296 case 'e':
229 do_exclude=add_to_regexp(do_exclude, optarg); 297 result.config.do_exclude = add_to_regexp(result.config.do_exclude, optarg);
230 break; 298 break;
231 case 'c': 299 case 'c':
232 do_critical=add_to_regexp(do_critical, optarg); 300 result.config.do_critical = add_to_regexp(result.config.do_critical, optarg);
233 break; 301 break;
234 case 'o': 302 case 'o':
235 only_critical=true; 303 result.config.only_critical = true;
236 break; 304 break;
237 case INPUT_FILE_OPT: 305 case INPUT_FILE_OPT:
238 input_filename = optarg; 306 result.config.input_filename = optarg;
239 break; 307 break;
240 case 'w': 308 case 'w':
241 packages_warning = atoi(optarg); 309 result.config.packages_warning = atoi(optarg);
310 break;
311 case output_format_index: {
312 parsed_output_format parser = mp_parse_output_format(optarg);
313 if (!parser.parsing_success) {
314 // TODO List all available formats here, maybe add anothoer usage function
315 printf("Invalid output format: %s\n", optarg);
316 exit(STATE_UNKNOWN);
317 }
318
319 result.config.output_format_is_set = true;
320 result.config.output_format = parser.output_format;
242 break; 321 break;
322 }
243 default: 323 default:
244 /* print short usage statement if args not parsable */ 324 /* print short usage statement if args not parsable */
245 usage5(); 325 usage5();
246 } 326 }
247 } 327 }
248 328
249 return OK; 329 return result;
250} 330}
251 331
252
253/* run an apt-get upgrade */ 332/* run an apt-get upgrade */
254int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkglist){ 333run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include,
255 int result=STATE_UNKNOWN, regres=0, pc=0, spc=0; 334 const char *do_exclude, const char *do_critical,
256 struct output chld_out, chld_err; 335 const char *upgrade_opts, const char *input_filename) {
257 regex_t ireg, ereg, sreg; 336 regex_t exclude_regex;
258 char *cmdline=NULL, rerrbuf[64];
259
260 /* initialize ereg as it is possible it is printed while uninitialized */ 337 /* initialize ereg as it is possible it is printed while uninitialized */
261 memset(&ereg, '\0', sizeof(ereg.buffer)); 338 memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer));
262 339
263 if(upgrade==NO_UPGRADE) return STATE_OK; 340 run_upgrade_result result = {
341 .errorcode = OK,
342 };
264 343
344 if (upgrade == NO_UPGRADE) {
345 result.errorcode = OK;
346 return result;
347 }
348
349 int regres = 0;
350 regex_t include_regex;
351 char rerrbuf[64];
265 /* compile the regexps */ 352 /* compile the regexps */
266 if (do_include != NULL) { 353 if (do_include != NULL) {
267 regres=regcomp(&ireg, do_include, REG_EXTENDED); 354 regres = regcomp(&include_regex, do_include, REG_EXTENDED);
268 if (regres!=0) { 355 if (regres != 0) {
269 regerror(regres, &ireg, rerrbuf, 64); 356 regerror(regres, &include_regex, rerrbuf, 64);
270 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 357 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
271 } 358 }
272 } 359 }
273 360
274 if(do_exclude!=NULL){ 361 if (do_exclude != NULL) {
275 regres=regcomp(&ereg, do_exclude, REG_EXTENDED); 362 regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED);
276 if(regres!=0) { 363 if (regres != 0) {
277 regerror(regres, &ereg, rerrbuf, 64); 364 regerror(regres, &exclude_regex, rerrbuf, 64);
278 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), 365 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
279 progname, rerrbuf);
280 } 366 }
281 } 367 }
282 368
369 regex_t sreg;
283 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; 370 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE;
284 regres=regcomp(&sreg, crit_ptr, REG_EXTENDED); 371 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED);
285 if(regres!=0) { 372 if (regres != 0) {
286 regerror(regres, &ereg, rerrbuf, 64); 373 regerror(regres, &exclude_regex, rerrbuf, 64);
287 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), 374 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
288 progname, rerrbuf);
289 } 375 }
290 376
291 cmdline=construct_cmdline(upgrade, upgrade_opts); 377 output chld_out;
378 output chld_err;
379 char *cmdline = NULL;
380 cmdline = construct_cmdline(upgrade, upgrade_opts);
292 if (input_filename != NULL) { 381 if (input_filename != NULL) {
293 /* read input from a file for testing */ 382 /* read input from a file for testing */
294 result = cmd_file_read(input_filename, &chld_out, 0); 383 result.errorcode = cmd_file_read(input_filename, &chld_out, 0);
295 } else { 384 } else {
296 /* run the upgrade */ 385 /* run the upgrade */
297 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 386 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0);
298 } 387 }
299 388
300 /* apt-get upgrade only changes exit status if there is an 389 // apt-get upgrade only changes exit status if there is an
301 * internal error when run in dry-run mode. therefore we will 390 // internal error when run in dry-run mode.
302 * treat such an error as UNKNOWN */ 391 if (result.errorcode != 0) {
303 if(result != 0){ 392 result.exec_warning = true;
304 exec_warning=1; 393 result.errorcode = ERROR;
305 result = STATE_UNKNOWN; 394 // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
306 fprintf(stderr, _("'%s' exited with non-zero status.\n"),
307 cmdline);
308 } 395 }
309 396
310 *pkglist=malloc(sizeof(char *) * chld_out.lines); 397 char **pkglist = malloc(sizeof(char *) * chld_out.lines);
311 if(!pkglist) die(STATE_UNKNOWN, "malloc failed!\n"); 398 if (!pkglist) {
312 *secpkglist=malloc(sizeof(char *) * chld_out.lines); 399 die(STATE_UNKNOWN, "malloc failed!\n");
313 if(!secpkglist) die(STATE_UNKNOWN, "malloc failed!\n"); 400 }
401 char **secpkglist = malloc(sizeof(char *) * chld_out.lines);
402 if (!secpkglist) {
403 die(STATE_UNKNOWN, "malloc failed!\n");
404 }
314 405
315 /* parse the output, which should only consist of lines like 406 /* parse the output, which should only consist of lines like
316 * 407 *
@@ -321,242 +412,293 @@ int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkg
321 * we may need to switch to the --print-uris output format, 412 * we may need to switch to the --print-uris output format,
322 * in which case the logic here will slightly change. 413 * in which case the logic here will slightly change.
323 */ 414 */
324 for(size_t i = 0; i < chld_out.lines; i++) { 415 size_t package_counter = 0;
325 if(verbose){ 416 size_t security_package_counter = 0;
417 for (size_t i = 0; i < chld_out.lines; i++) {
418 if (verbose) {
326 printf("%s\n", chld_out.line[i]); 419 printf("%s\n", chld_out.line[i]);
327 } 420 }
421
328 /* if it is a package we care about */ 422 /* if it is a package we care about */
329 if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 && 423 if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 &&
330 (do_include == NULL || regexec(&ireg, chld_out.line[i], 0, NULL, 0) == 0)) { 424 (do_include == NULL || regexec(&include_regex, chld_out.line[i], 0, NULL, 0) == 0)) {
331 /* if we're not excluding, or it's not in the 425 /* if we're not excluding, or it's not in the
332 * list of stuff to exclude */ 426 * list of stuff to exclude */
333 if(do_exclude==NULL || 427 if (do_exclude == NULL || regexec(&exclude_regex, chld_out.line[i], 0, NULL, 0) != 0) {
334 regexec(&ereg, chld_out.line[i], 0, NULL, 0)!=0){ 428 package_counter++;
335 pc++; 429 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) {
336 if(regexec(&sreg, chld_out.line[i], 0, NULL, 0)==0){ 430 security_package_counter++;
337 spc++; 431
338 if(verbose) printf("*"); 432 if (verbose) {
339 (*secpkglist)[spc-1] = pkg_name(chld_out.line[i]); 433 printf("*");
434 }
435
436 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]);
340 } else { 437 } else {
341 (*pkglist)[pc-spc-1] = pkg_name(chld_out.line[i]); 438 (pkglist)[package_counter - security_package_counter - 1] =
439 pkg_name(chld_out.line[i]);
342 } 440 }
343 if(verbose){ 441 if (verbose) {
344 printf("*%s\n", chld_out.line[i]); 442 printf("*%s\n", chld_out.line[i]);
345 } 443 }
346 } 444 }
347 } 445 }
348 } 446 }
349 *pkgcount=pc; 447
350 *secpkgcount=spc; 448 result.package_count = package_counter;
449 result.security_package_count = security_package_counter;
450 result.packages_list = pkglist;
451 result.secpackages_list = secpkglist;
351 452
352 /* If we get anything on stderr, at least set warning */ 453 /* If we get anything on stderr, at least set warning */
353 if (input_filename == NULL && chld_err.buflen) { 454 if (input_filename == NULL && chld_err.buflen) {
354 stderr_warning=1; 455 stderr_warning = true;
355 result = max_state(result, STATE_WARNING); 456 result.errorcode = ERROR;
356 if(verbose){ 457
357 for(size_t i = 0; i < chld_err.lines; i++) { 458 if (verbose) {
459 for (size_t i = 0; i < chld_err.lines; i++) {
358 fprintf(stderr, "%s\n", chld_err.line[i]); 460 fprintf(stderr, "%s\n", chld_err.line[i]);
359 } 461 }
360 } 462 }
361 } 463 }
362 if (do_include != NULL) regfree(&ireg); 464
465 if (do_include != NULL) {
466 regfree(&include_regex);
467 }
468
363 regfree(&sreg); 469 regfree(&sreg);
364 if(do_exclude!=NULL) regfree(&ereg); 470
471 if (do_exclude != NULL) {
472 regfree(&exclude_regex);
473 }
474
365 free(cmdline); 475 free(cmdline);
476
366 return result; 477 return result;
367} 478}
368 479
369/* run an apt-get update (needs root) */ 480/* run an apt-get update (needs root) */
370int run_update(void){ 481run_update_result run_update(char *update_opts) {
371 int result=STATE_UNKNOWN;
372 struct output chld_out, chld_err;
373 char *cmdline; 482 char *cmdline;
374
375 /* run the update */ 483 /* run the update */
376 cmdline = construct_cmdline(NO_UPGRADE, update_opts); 484 cmdline = construct_cmdline(NO_UPGRADE, update_opts);
377 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 485
486 run_update_result result = {
487 .exec_warning = false,
488 .stderr_warning = false,
489 .sc = mp_subcheck_init(),
490 };
491
492 result.sc = mp_set_subcheck_default_state(result.sc, STATE_OK);
493 xasprintf(&result.sc.output, "executing '%s' first", cmdline);
494
495 output chld_out;
496 output chld_err;
497 int cmd_error = np_runcmd(cmdline, &chld_out, &chld_err, 0);
378 /* apt-get update changes exit status if it can't fetch packages. 498 /* apt-get update changes exit status if it can't fetch packages.
379 * since we were explicitly asked to do so, this is treated as 499 * since we were explicitly asked to do so, this is treated as
380 * a critical error. */ 500 * a critical error. */
381 if(result != 0){ 501 if (cmd_error != 0) {
382 exec_warning=1; 502 exec_warning = true;
383 result = STATE_CRITICAL; 503 result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL);
384 fprintf(stderr, _("'%s' exited with non-zero status.\n"), 504 xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline);
385 cmdline);
386 } 505 }
387 506
388 if(verbose){ 507 if (verbose) {
389 for(size_t i = 0; i < chld_out.lines; i++) { 508 for (size_t i = 0; i < chld_out.lines; i++) {
390 printf("%s\n", chld_out.line[i]); 509 printf("%s\n", chld_out.line[i]);
391 } 510 }
392 } 511 }
393 512
394 /* If we get anything on stderr, at least set warning */ 513 /* If we get anything on stderr, at least set warning */
395 if(chld_err.buflen){ 514 if (chld_err.buflen) {
396 stderr_warning=1; 515 stderr_warning = true;
397 result = max_state(result, STATE_WARNING); 516 result.sc = mp_set_subcheck_state(
398 if(verbose){ 517 result.sc, max_state(mp_compute_subcheck_state(result.sc), STATE_WARNING));
399 for(size_t i = 0; i < chld_err.lines; i++) { 518 if (verbose) {
519 for (size_t i = 0; i < chld_err.lines; i++) {
400 fprintf(stderr, "%s\n", chld_err.line[i]); 520 fprintf(stderr, "%s\n", chld_err.line[i]);
401 } 521 }
402 } 522 }
403 } 523 }
524
404 free(cmdline); 525 free(cmdline);
526
405 return result; 527 return result;
406} 528}
407 529
408char* pkg_name(char *line){ 530char *pkg_name(char *line) {
409 char *start=NULL, *space=NULL, *pkg=NULL; 531 char *start = line + strlen(PKGINST_PREFIX);
410 int len=0;
411 532
412 start = line + strlen(PKGINST_PREFIX); 533 size_t len = strlen(start);
413 len = strlen(start);
414 534
415 space = index(start, ' '); 535 char *space = index(start, ' ');
416 if(space!=NULL){ 536 if (space != NULL) {
417 len = space - start; 537 len = space - start;
418 } 538 }
419 539
420 pkg=malloc(sizeof(char)*(len+1)); 540 char *pkg = malloc(sizeof(char) * (len + 1));
421 if(!pkg) die(STATE_UNKNOWN, "malloc failed!\n"); 541 if (!pkg) {
542 die(STATE_UNKNOWN, "malloc failed!\n");
543 }
422 544
423 strncpy(pkg, start, len); 545 strncpy(pkg, start, len);
424 pkg[len]='\0'; 546 pkg[len] = '\0';
425 547
426 return pkg; 548 return pkg;
427} 549}
428 550
429int cmpstringp(const void *p1, const void *p2){ 551int cmpstringp(const void *left_string, const void *right_string) {
430 return strcmp(* (char * const *) p1, * (char * const *) p2); 552 return strcmp(*(char *const *)left_string, *(char *const *)right_string);
431} 553}
432 554
433char* add_to_regexp(char *expr, const char *next){ 555char *add_to_regexp(char *expr, const char *next) {
434 char *re=NULL; 556 char *regex_string = NULL;
435 557
436 if(expr==NULL){ 558 if (expr == NULL) {
437 re=malloc(sizeof(char)*(strlen("()")+strlen(next)+1)); 559 regex_string = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1));
438 if(!re) die(STATE_UNKNOWN, "malloc failed!\n"); 560 if (!regex_string) {
439 sprintf(re, "(%s)", next); 561 die(STATE_UNKNOWN, "malloc failed!\n");
562 }
563 sprintf(regex_string, "(%s)", next);
440 } else { 564 } else {
441 /* resize it, adding an extra char for the new '|' separator */ 565 /* resize it, adding an extra char for the new '|' separator */
442 re=realloc(expr, sizeof(char)*(strlen(expr)+1+strlen(next)+1)); 566 regex_string = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1));
443 if(!re) die(STATE_UNKNOWN, "realloc failed!\n"); 567 if (!regex_string) {
568 die(STATE_UNKNOWN, "realloc failed!\n");
569 }
444 /* append it starting at ')' in the old re */ 570 /* append it starting at ')' in the old re */
445 sprintf((char*)(re+strlen(re)-1), "|%s)", next); 571 sprintf((char *)(regex_string + strlen(regex_string) - 1), "|%s)", next);
446 } 572 }
447 573
448 return re; 574 return regex_string;
449} 575}
450 576
451char* construct_cmdline(upgrade_type u, const char *opts){ 577char *construct_cmdline(upgrade_type upgrade, const char *opts) {
452 int len=0; 578 const char *opts_ptr = NULL;
453 const char *opts_ptr=NULL, *aptcmd=NULL; 579 const char *aptcmd = NULL;
454 char *cmd=NULL;
455 580
456 switch(u){ 581 switch (upgrade) {
457 case UPGRADE: 582 case UPGRADE:
458 if(opts==NULL) opts_ptr=UPGRADE_DEFAULT_OPTS; 583 if (opts == NULL) {
459 else opts_ptr=opts; 584 opts_ptr = UPGRADE_DEFAULT_OPTS;
460 aptcmd="upgrade"; 585 } else {
586 opts_ptr = opts;
587 }
588 aptcmd = "upgrade";
461 break; 589 break;
462 case DIST_UPGRADE: 590 case DIST_UPGRADE:
463 if(opts==NULL) opts_ptr=UPGRADE_DEFAULT_OPTS; 591 if (opts == NULL) {
464 else opts_ptr=opts; 592 opts_ptr = UPGRADE_DEFAULT_OPTS;
465 aptcmd="dist-upgrade"; 593 } else {
594 opts_ptr = opts;
595 }
596 aptcmd = "dist-upgrade";
466 break; 597 break;
467 case NO_UPGRADE: 598 case NO_UPGRADE:
468 if(opts==NULL) opts_ptr=UPDATE_DEFAULT_OPTS; 599 if (opts == NULL) {
469 else opts_ptr=opts; 600 opts_ptr = UPDATE_DEFAULT_OPTS;
470 aptcmd="update"; 601 } else {
602 opts_ptr = opts;
603 }
604 aptcmd = "update";
471 break; 605 break;
472 } 606 }
473 607
474 len+=strlen(PATH_TO_APTGET)+1; /* "/usr/bin/apt-get " */ 608 size_t len = 0;
475 len+=strlen(opts_ptr)+1; /* "opts " */ 609 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */
476 len+=strlen(aptcmd)+1; /* "upgrade\0" */ 610 len += strlen(opts_ptr) + 1; /* "opts " */
611 len += strlen(aptcmd) + 1; /* "upgrade\0" */
477 612
478 cmd=(char*)malloc(sizeof(char)*len); 613 char *cmd = (char *)malloc(sizeof(char) * len);
479 if(cmd==NULL) die(STATE_UNKNOWN, "malloc failed"); 614 if (cmd == NULL) {
615 die(STATE_UNKNOWN, "malloc failed");
616 }
480 sprintf(cmd, "%s %s %s", PATH_TO_APTGET, opts_ptr, aptcmd); 617 sprintf(cmd, "%s %s %s", PATH_TO_APTGET, opts_ptr, aptcmd);
481 return cmd; 618 return cmd;
482} 619}
483 620
484/* informative help message */ 621/* informative help message */
485void 622void print_help(void) {
486print_help (void) 623 print_revision(progname, NP_VERSION);
487{ 624
488 print_revision(progname, NP_VERSION); 625 printf(_(COPYRIGHT), copyright, email);
489 626
490 printf(_(COPYRIGHT), copyright, email); 627 printf("%s\n", _("This plugin checks for software updates on systems that use"));
491 628 printf("%s\n", _("package management systems based on the apt-get(8) command"));
492 printf("%s\n", _("This plugin checks for software updates on systems that use")); 629 printf("%s\n", _("found in Debian GNU/Linux"));
493 printf("%s\n", _("package management systems based on the apt-get(8) command")); 630
494 printf("%s\n", _("found in Debian GNU/Linux")); 631 printf("\n\n");
495 632
496 printf ("\n\n"); 633 print_usage();
497 634
498 print_usage(); 635 printf(UT_HELP_VRSN);
499 636 printf(UT_EXTRA_OPTS);
500 printf(UT_HELP_VRSN); 637
501 printf(UT_EXTRA_OPTS); 638 printf(UT_PLUG_TIMEOUT, timeout_interval);
502 639
503 printf(UT_PLUG_TIMEOUT, timeout_interval); 640 printf(" %s\n", "-n, --no-upgrade");
504 641 printf(" %s\n", _("Do not run the upgrade. Probably not useful (without -u at least)."));
505 printf (" %s\n", "-n, --no-upgrade"); 642 printf(" %s\n", "-l, --list");
506 printf (" %s\n", _("Do not run the upgrade. Probably not useful (without -u at least).")); 643 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by"));
507 printf (" %s\n", "-l, --list"); 644 printf(" %s\n", _("name with security packages listed first."));
508 printf (" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); 645 printf(" %s\n", "-i, --include=REGEXP");
509 printf (" %s\n", _("name with security packages listed first.")); 646 printf(" %s\n",
510 printf (" %s\n", "-i, --include=REGEXP"); 647 _("Include only packages matching REGEXP. Can be specified multiple times"));
511 printf (" %s\n", _("Include only packages matching REGEXP. Can be specified multiple times")); 648 printf(" %s\n", _("the values will be combined together. Any packages matching this list"));
512 printf (" %s\n", _("the values will be combined together. Any packages matching this list")); 649 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored."));
513 printf (" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); 650 printf(" %s\n", _("Default is to include all packages."));
514 printf (" %s\n", _("Default is to include all packages.")); 651 printf(" %s\n", "-e, --exclude=REGEXP");
515 printf (" %s\n", "-e, --exclude=REGEXP"); 652 printf(" %s\n", _("Exclude packages matching REGEXP from the list of packages that would"));
516 printf (" %s\n", _("Exclude packages matching REGEXP from the list of packages that would")); 653 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values"));
517 printf (" %s\n", _("otherwise be included. Can be specified multiple times; the values")); 654 printf(" %s\n", _("will be combined together. Default is to exclude no packages."));
518 printf (" %s\n", _("will be combined together. Default is to exclude no packages.")); 655 printf(" %s\n", "-c, --critical=REGEXP");
519 printf (" %s\n", "-c, --critical=REGEXP"); 656 printf(" %s\n",
520 printf (" %s\n", _("If the full package information of any of the upgradable packages match")); 657 _("If the full package information of any of the upgradable packages match"));
521 printf (" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified")); 658 printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified"));
522 printf (" %s\n", _("multiple times like above. Default is a regexp matching security")); 659 printf(" %s\n", _("multiple times like above. Default is a regexp matching security"));
523 printf (" %s\n", _("upgrades for Debian and Ubuntu:")); 660 printf(" %s\n", _("upgrades for Debian and Ubuntu:"));
524 printf (" \t%s\n", SECURITY_RE); 661 printf(" \t%s\n", SECURITY_RE);
525 printf (" %s\n", _("Note that the package must first match the include list before its")); 662 printf(" %s\n", _("Note that the package must first match the include list before its"));
526 printf (" %s\n", _("information is compared against the critical list.")); 663 printf(" %s\n", _("information is compared against the critical list."));
527 printf (" %s\n", "-o, --only-critical"); 664 printf(" %s\n", "-o, --only-critical");
528 printf (" %s\n", _("Only warn about upgrades matching the critical list. The total number")); 665 printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number"));
529 printf (" %s\n", _("of upgrades will be printed, but any non-critical upgrades will not cause")); 666 printf(" %s\n",
530 printf (" %s\n", _("the plugin to return WARNING status.")); 667 _("of upgrades will be printed, but any non-critical upgrades will not cause"));
531 printf (" %s\n", "-w, --packages-warning"); 668 printf(" %s\n", _("the plugin to return WARNING status."));
532 printf (" %s\n", _("Minimum number of packages available for upgrade to return WARNING status.")); 669 printf(" %s\n", "-w, --packages-warning");
533 printf (" %s\n\n", _("Default is 1 package.")); 670 printf(" %s\n",
534 671 _("Minimum number of packages available for upgrade to return WARNING status."));
535 printf ("%s\n\n", _("The following options require root privileges and should be used with care:")); 672 printf(" %s\n\n", _("Default is 1 package."));
536 printf (" %s\n", "-u, --update=OPTS"); 673
537 printf (" %s\n", _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); 674 printf(UT_OUTPUT_FORMAT);
538 printf (" %s\n", _("the default options. Note: you may also need to adjust the global")); 675
539 printf (" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); 676 printf("%s\n\n",
540 printf (" %s\n", _("upgrade is expected to take longer than the default timeout.")); 677 _("The following options require root privileges and should be used with care:"));
541 printf (" %s\n", "-U, --upgrade=OPTS"); 678 printf(" %s\n", "-u, --update=OPTS");
542 printf (" %s\n", _("Perform an upgrade. If an optional OPTS argument is provided,")); 679 printf(" %s\n",
543 printf (" %s\n", _("apt-get will be run with these command line options instead of the")); 680 _("First perform an 'apt-get update'. An optional OPTS parameter overrides"));
544 printf (" %s", _("default ")); 681 printf(" %s\n", _("the default options. Note: you may also need to adjust the global"));
545 printf ("(%s).\n", UPGRADE_DEFAULT_OPTS); 682 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get"));
546 printf (" %s\n", _("Note that you may be required to have root privileges if you do not use")); 683 printf(" %s\n", _("upgrade is expected to take longer than the default timeout."));
547 printf (" %s\n", _("the default options, which will only run a simulation and NOT perform the upgrade")); 684 printf(" %s\n", "-U, --upgrade=OPTS");
548 printf (" %s\n", "-d, --dist-upgrade=OPTS"); 685 printf(" %s\n", _("Perform an upgrade. If an optional OPTS argument is provided,"));
549 printf (" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); 686 printf(" %s\n", _("apt-get will be run with these command line options instead of the"));
550 printf (" %s\n", _("can be provided to override the default options.")); 687 printf(" %s", _("default "));
551 688 printf("(%s).\n", UPGRADE_DEFAULT_OPTS);
552 printf(UT_SUPPORT); 689 printf(" %s\n",
690 _("Note that you may be required to have root privileges if you do not use"));
691 printf(" %s\n",
692 _("the default options, which will only run a simulation and NOT perform the upgrade"));
693 printf(" %s\n", "-d, --dist-upgrade=OPTS");
694 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS"));
695 printf(" %s\n", _("can be provided to override the default options."));
696
697 printf(UT_SUPPORT);
553} 698}
554 699
555
556/* simple usage heading */ 700/* simple usage heading */
557void 701void print_usage(void) {
558print_usage(void) 702 printf("%s\n", _("Usage:"));
559{ 703 printf("%s [[-d|-u|-U]opts] [-n] [-l] [-t timeout] [-w packages-warning]\n", progname);
560 printf ("%s\n", _("Usage:"));
561 printf ("%s [[-d|-u|-U]opts] [-n] [-l] [-t timeout] [-w packages-warning]\n", progname);
562} 704}
diff --git a/plugins/check_apt.d/config.h b/plugins/check_apt.d/config.h
new file mode 100644
index 00000000..e4d622f1
--- /dev/null
+++ b/plugins/check_apt.d/config.h
@@ -0,0 +1,46 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include "../lib/output.h"
6
7/* some constants */
8typedef enum {
9 UPGRADE,
10 DIST_UPGRADE,
11 NO_UPGRADE
12} upgrade_type;
13
14typedef struct {
15 bool do_update; /* whether to call apt-get update */
16 upgrade_type upgrade; /* which type of upgrade to do */
17 bool only_critical; /* whether to warn about non-critical updates */
18 bool list; /* list packages available for upgrade */
19 /* number of packages available for upgrade to return WARNING status */
20 size_t packages_warning;
21
22 char *upgrade_opts; /* options to override defaults for upgrade */
23 char *update_opts; /* options to override defaults for update */
24 char *do_include; /* regexp to only include certain packages */
25 char *do_exclude; /* regexp to only exclude certain packages */
26 char *do_critical; /* regexp specifying critical packages */
27 char *input_filename; /* input filename for testing */
28
29 bool output_format_is_set;
30 mp_output_format output_format;
31} check_apt_config;
32
33check_apt_config check_apt_config_init() {
34 check_apt_config tmp = {.do_update = false,
35 .upgrade = UPGRADE,
36 .only_critical = false,
37 .list = false,
38 .packages_warning = 1,
39 .update_opts = NULL,
40 .do_include = NULL,
41 .do_exclude = NULL,
42 .do_critical = NULL,
43 .input_filename = NULL,
44 .output_format_is_set = false};
45 return tmp;
46}
diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c
index 2a23b397..a43c0d34 100644
--- a/plugins/check_by_ssh.c
+++ b/plugins/check_by_ssh.c
@@ -1,186 +1,183 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_by_ssh plugin 3 * Monitoring check_by_ssh plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_by_ssh plugin 10 * This file contains the check_by_ssh plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 13 * This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14 * it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 23 * You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29const char *progname = "check_by_ssh"; 29const char *progname = "check_by_ssh";
30const char *copyright = "2000-2008"; 30const char *copyright = "2000-2024";
31const char *email = "devel@monitoring-plugins.org"; 31const 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
42int process_arguments (int, char **); 43typedef struct {
43int validate_arguments (void); 44 int errorcode;
44void comm_append (const char *); 45 check_by_ssh_config config;
45void print_help (void); 46} check_by_ssh_config_wrapper;
46void print_usage (void); 47static check_by_ssh_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
47 48static check_by_ssh_config_wrapper
48unsigned int commands = 0; 49 validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/);
49unsigned int services = 0;
50int skip_stdout = 0;
51int skip_stderr = 0;
52int warn_on_stderr = 0;
53bool unknown_timeout = false;
54char *remotecmd = NULL;
55char **commargv = NULL;
56int commargc = 0;
57char *hostname = NULL;
58char *outputfile = NULL;
59char *host_shortname = NULL;
60char **service;
61bool passive = false;
62bool verbose = false;
63
64int
65main (int argc, char **argv)
66{
67 50
68 char *status_text; 51static command_construct comm_append(command_construct /*cmd*/, const char * /*str*/);
69 int cresult; 52static void print_help(void);
70 int result = STATE_UNKNOWN; 53void print_usage(void);
71 time_t local_time;
72 FILE *fp = NULL;
73 output chld_out, chld_err;
74 54
75 remotecmd = ""; 55static bool verbose = false;
76 comm_append(SSH_COMMAND);
77 56
78 setlocale (LC_ALL, ""); 57int main(int argc, char **argv) {
79 bindtextdomain (PACKAGE, LOCALEDIR); 58 setlocale(LC_ALL, "");
80 textdomain (PACKAGE); 59 bindtextdomain(PACKAGE, LOCALEDIR);
60 textdomain(PACKAGE);
81 61
82 /* Parse extra opts if any */ 62 /* Parse extra opts if any */
83 argv=np_extra_opts (&argc, argv, progname); 63 argv = np_extra_opts(&argc, argv, progname);
64
65 check_by_ssh_config_wrapper tmp_config = process_arguments(argc, argv);
84 66
85 /* process arguments */ 67 /* process arguments */
86 if (process_arguments (argc, argv) == ERROR) 68 if (tmp_config.errorcode == ERROR) {
87 usage_va(_("Could not parse arguments")); 69 usage_va(_("Could not parse arguments"));
70 }
71
72 const check_by_ssh_config config = tmp_config.config;
88 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"));
92 } 77 }
93 alarm (timeout_interval); 78 alarm(timeout_interval);
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]);
85 }
100 } 86 }
101 87
102 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);
103 91
104 /* 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 */
105 if (result == 255 && unknown_timeout) { 93 if (result == 255 && config.unknown_timeout) {
106 printf (_("SSH connection failed: %s\n"), 94 printf(_("SSH connection failed: %s\n"),
107 chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); 95 chld_err.lines > 0 ? chld_err.line[0] : "(no error output)");
108 return STATE_UNKNOWN; 96 return STATE_UNKNOWN;
109 } 97 }
110 98
111 if (verbose) { 99 if (verbose) {
112 for(size_t i = 0; i < chld_out.lines; i++) 100 for (size_t i = 0; i < chld_out.lines; i++) {
113 printf("stdout: %s\n", chld_out.line[i]); 101 printf("stdout: %s\n", chld_out.line[i]);
114 for(size_t i = 0; i < chld_err.lines; i++) 102 }
103 for (size_t i = 0; i < chld_err.lines; i++) {
115 printf("stderr: %s\n", chld_err.line[i]); 104 printf("stderr: %s\n", chld_err.line[i]);
105 }
116 } 106 }
117 107
118 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 */
119 skip_stdout = chld_out.lines; 110 skip_stdout = chld_out.lines;
120 if (skip_stderr == -1) /* --skip-stderr specified without argument */ 111 } else {
112 skip_stdout = config.skip_stdout;
113 }
114
115 size_t skip_stderr = 0;
116 if (config.skip_stderr == -1) { /* --skip-stderr specified without argument */
121 skip_stderr = chld_err.lines; 117 skip_stderr = chld_err.lines;
118 } else {
119 skip_stderr = config.skip_stderr;
120 }
122 121
123 /* UNKNOWN or worse if (non-skipped) output found on stderr */ 122 /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */
124 if(chld_err.lines > (size_t)skip_stderr) { 123 if (chld_err.lines > (size_t)skip_stderr && (config.unknown_on_stderr || config.warn_on_stderr)) {
125 printf (_("Remote command execution failed: %s\n"), 124 printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]);
126 chld_err.line[skip_stderr]); 125 if (config.unknown_on_stderr) {
127 if ( warn_on_stderr )
128 return max_state_alt(result, STATE_WARNING);
129 else
130 return max_state_alt(result, STATE_UNKNOWN); 126 return max_state_alt(result, STATE_UNKNOWN);
127 } else if (config.warn_on_stderr) {
128 return max_state_alt(result, STATE_WARNING);
129 }
131 } 130 }
132 131
133 /* this is simple if we're not supposed to be passive. 132 /* this is simple if we're not supposed to be passive.
134 * Wrap up quickly and keep the tricks below */ 133 * Wrap up quickly and keep the tricks below */
135 if(!passive) { 134 if (!config.passive) {
136 if (chld_out.lines > (size_t)skip_stdout) 135 if (chld_out.lines > (size_t)skip_stdout) {
137 for (size_t i = skip_stdout; i < chld_out.lines; i++) 136 for (size_t i = skip_stdout; i < chld_out.lines; i++) {
138 puts (chld_out.line[i]); 137 puts(chld_out.line[i]);
139 else 138 }
140 printf (_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), 139 } else {
141 state_text(result), remotecmd, result); 140 printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"),
142 return result; /* return error status from remote command */ 141 state_text(result), config.remotecmd, result);
142 }
143 return result; /* return error status from remote command */
143 } 144 }
144 145
145
146 /* 146 /*
147 * Passive mode 147 * Passive mode
148 */ 148 */
149 149
150 /* process output */ 150 /* process output */
151 if (!(fp = fopen (outputfile, "a"))) { 151 FILE *file_pointer = NULL;
152 printf (_("SSH WARNING: could not open %s\n"), outputfile); 152 if (!(file_pointer = fopen(config.outputfile, "a"))) {
153 exit (STATE_UNKNOWN); 153 printf(_("SSH WARNING: could not open %s\n"), config.outputfile);
154 exit(STATE_UNKNOWN);
154 } 155 }
155 156
156 local_time = time (NULL); 157 time_t local_time = time(NULL);
157 commands = 0; 158 unsigned int commands = 0;
158 for(size_t i = skip_stdout; i < chld_out.lines; i++) { 159 char *status_text;
160 int cresult;
161 for (size_t i = skip_stdout; i < chld_out.lines; i++) {
159 status_text = chld_out.line[i++]; 162 status_text = chld_out.line[i++];
160 if (i == chld_out.lines || strstr (chld_out.line[i], "STATUS CODE: ") == NULL) 163 if (i == chld_out.lines || strstr(chld_out.line[i], "STATUS CODE: ") == NULL) {
161 die (STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); 164 die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname);
162 165 }
163 if (service[commands] && status_text 166
164 && sscanf (chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) 167 if (config.service[commands] && status_text &&
165 { 168 sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) {
166 fprintf (fp, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", 169 fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
167 (int) local_time, host_shortname, service[commands++], 170 (int)local_time, config.host_shortname, config.service[commands++], cresult,
168 cresult, status_text); 171 status_text);
169 } 172 }
170 } 173 }
171 174
172 /* Multiple commands and passive checking should always return OK */ 175 /* Multiple commands and passive checking should always return OK */
173 return result; 176 exit(result);
174} 177}
175 178
176/* process command-line arguments */ 179/* process command-line arguments */
177int 180check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
178process_arguments (int argc, char **argv)
179{
180 int c;
181 char *p1, *p2;
182
183 int option = 0;
184 static struct option longopts[] = { 181 static struct option longopts[] = {
185 {"version", no_argument, 0, 'V'}, 182 {"version", no_argument, 0, 'V'},
186 {"help", no_argument, 0, 'h'}, 183 {"help", no_argument, 0, 'h'},
@@ -188,19 +185,20 @@ process_arguments (int argc, char **argv)
188 {"fork", no_argument, 0, 'f'}, 185 {"fork", no_argument, 0, 'f'},
189 {"timeout", required_argument, 0, 't'}, 186 {"timeout", required_argument, 0, 't'},
190 {"unknown-timeout", no_argument, 0, 'U'}, 187 {"unknown-timeout", no_argument, 0, 'U'},
191 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 188 {"host", required_argument, 0, 'H'}, /* backward compatibility */
192 {"hostname", required_argument, 0, 'H'}, 189 {"hostname", required_argument, 0, 'H'},
193 {"port", required_argument,0,'p'}, 190 {"port", required_argument, 0, 'p'},
194 {"output", required_argument, 0, 'O'}, 191 {"output", required_argument, 0, 'O'},
195 {"name", required_argument, 0, 'n'}, 192 {"name", required_argument, 0, 'n'},
196 {"services", required_argument, 0, 's'}, 193 {"services", required_argument, 0, 's'},
197 {"identity", required_argument, 0, 'i'}, 194 {"identity", required_argument, 0, 'i'},
198 {"user", required_argument, 0, 'u'}, 195 {"user", required_argument, 0, 'u'}, /* backwards compatibility */
199 {"logname", required_argument, 0, 'l'}, 196 {"logname", required_argument, 0, 'l'},
200 {"command", required_argument, 0, 'C'}, 197 {"command", required_argument, 0, 'C'},
201 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ 198 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */
202 {"skip-stdout", optional_argument, 0, 'S'}, 199 {"skip-stdout", optional_argument, 0, 'S'},
203 {"skip-stderr", optional_argument, 0, 'E'}, 200 {"skip-stderr", optional_argument, 0, 'E'},
201 {"unknown-on-stderr", no_argument, 0, 'e'},
204 {"warn-on-stderr", no_argument, 0, 'W'}, 202 {"warn-on-stderr", no_argument, 0, 'W'},
205 {"proto1", no_argument, 0, '1'}, 203 {"proto1", no_argument, 0, '1'},
206 {"proto2", no_argument, 0, '2'}, 204 {"proto2", no_argument, 0, '2'},
@@ -209,287 +207,330 @@ process_arguments (int argc, char **argv)
209 {"ssh-option", required_argument, 0, 'o'}, 207 {"ssh-option", required_argument, 0, 'o'},
210 {"quiet", no_argument, 0, 'q'}, 208 {"quiet", no_argument, 0, 'q'},
211 {"configfile", optional_argument, 0, 'F'}, 209 {"configfile", optional_argument, 0, 'F'},
212 {0, 0, 0, 0} 210 {0, 0, 0, 0}};
211
212 check_by_ssh_config_wrapper result = {
213 .errorcode = OK,
214 .config = check_by_ssh_config_init(),
213 }; 215 };
214 216
215 if (argc < 2) 217 if (argc < 2) {
216 return ERROR; 218 result.errorcode = ERROR;
219 return result;
220 }
221
222 for (int index = 1; index < argc; index++) {
223 if (strcmp("-to", argv[index]) == 0) {
224 strcpy(argv[index], "-t");
225 }
226 }
217 227
218 for (c = 1; c < argc; c++) 228 result.config.cmd = comm_append(result.config.cmd, SSH_COMMAND);
219 if (strcmp ("-to", argv[c]) == 0)
220 strcpy (argv[c], "-t");
221 229
222 while (1) { 230 int option = 0;
223 c = getopt_long (argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, 231 while (true) {
224 &option); 232 int opt_index =
233 getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option);
225 234
226 if (c == -1 || c == EOF) 235 if (opt_index == -1 || opt_index == EOF) {
227 break; 236 break;
237 }
228 238
229 switch (c) { 239 switch (opt_index) {
230 case 'V': /* version */ 240 case 'V': /* version */
231 print_revision (progname, NP_VERSION); 241 print_revision(progname, NP_VERSION);
232 exit (STATE_UNKNOWN); 242 exit(STATE_UNKNOWN);
233 case 'h': /* help */ 243 case 'h': /* help */
234 print_help (); 244 print_help();
235 exit (STATE_UNKNOWN); 245 exit(STATE_UNKNOWN);
236 case 'v': /* help */ 246 case 'v': /* help */
237 verbose = true; 247 verbose = true;
238 break; 248 break;
239 case 't': /* timeout period */ 249 case 't': /* timeout period */
240 if (!is_integer (optarg)) 250 if (!is_integer(optarg)) {
241 usage_va(_("Timeout interval must be a positive integer")); 251 usage_va(_("Timeout interval must be a positive integer"));
242 else 252 } else {
243 timeout_interval = atoi (optarg); 253 timeout_interval = atoi(optarg);
254 }
244 break; 255 break;
245 case 'U': 256 case 'U':
246 unknown_timeout = true; 257 result.config.unknown_timeout = true;
247 break; 258 break;
248 case 'H': /* host */ 259 case 'H': /* host */
249 hostname = optarg; 260 result.config.hostname = optarg;
250 break; 261 break;
251 case 'p': /* port number */ 262 case 'p': /* port number */
252 if (!is_integer (optarg)) 263 if (!is_integer(optarg)) {
253 usage_va(_("Port must be a positive integer")); 264 usage_va(_("Port must be a positive integer"));
254 comm_append("-p"); 265 }
255 comm_append(optarg); 266 result.config.cmd = comm_append(result.config.cmd, "-p");
267 result.config.cmd = comm_append(result.config.cmd, optarg);
256 break; 268 break;
257 case 'O': /* output file */ 269 case 'O': /* output file */
258 outputfile = optarg; 270 result.config.outputfile = optarg;
259 passive = true; 271 result.config.passive = true;
260 break; 272 break;
261 case 's': /* description of service to check */ 273 case 's': /* description of service to check */ {
274 char *p1;
275 char *p2;
276
262 p1 = optarg; 277 p1 = optarg;
263 service = realloc (service, (++services) * sizeof(char *)); 278 result.config.service = realloc(result.config.service,
264 while ((p2 = index (p1, ':'))) { 279 (++result.config.number_of_services) * sizeof(char *));
280 while ((p2 = index(p1, ':'))) {
265 *p2 = '\0'; 281 *p2 = '\0';
266 service[services - 1] = p1; 282 result.config.service[result.config.number_of_services - 1] = p1;
267 service = realloc (service, (++services) * sizeof(char *)); 283 result.config.service = realloc(
284 result.config.service, (++result.config.number_of_services) * sizeof(char *));
268 p1 = p2 + 1; 285 p1 = p2 + 1;
269 } 286 }
270 service[services - 1] = p1; 287 result.config.service[result.config.number_of_services - 1] = p1;
271 break;
272 case 'n': /* short name of host in the monitoring configuration */
273 host_shortname = optarg;
274 break; 288 break;
275 289 case 'n': /* short name of host in the monitoring configuration */
290 result.config.host_shortname = optarg;
291 } break;
276 case 'u': 292 case 'u':
277 comm_append("-l"); 293 result.config.cmd = comm_append(result.config.cmd, "-l");
278 comm_append(optarg); 294 result.config.cmd = comm_append(result.config.cmd, optarg);
279 break; 295 break;
280 case 'l': /* login name */ 296 case 'l': /* login name */
281 comm_append("-l"); 297 result.config.cmd = comm_append(result.config.cmd, "-l");
282 comm_append(optarg); 298 result.config.cmd = comm_append(result.config.cmd, optarg);
283 break; 299 break;
284 case 'i': /* identity */ 300 case 'i': /* identity */
285 comm_append("-i"); 301 result.config.cmd = comm_append(result.config.cmd, "-i");
286 comm_append(optarg); 302 result.config.cmd = comm_append(result.config.cmd, optarg);
287 break; 303 break;
288 304
289 case '1': /* Pass these switches directly to ssh */ 305 case '1': /* Pass these switches directly to ssh */
290 comm_append("-1"); 306 result.config.cmd = comm_append(result.config.cmd, "-1");
291 break; 307 break;
292 case '2': /* 1 to force version 1, 2 to force version 2 */ 308 case '2': /* 1 to force version 1, 2 to force version 2 */
293 comm_append("-2"); 309 result.config.cmd = comm_append(result.config.cmd, "-2");
294 break; 310 break;
295 case '4': /* -4 for IPv4 */ 311 case '4': /* -4 for IPv4 */
296 comm_append("-4"); 312 result.config.cmd = comm_append(result.config.cmd, "-4");
297 break; 313 break;
298 case '6': /* -6 for IPv6 */ 314 case '6': /* -6 for IPv6 */
299 comm_append("-6"); 315 result.config.cmd = comm_append(result.config.cmd, "-6");
300 break; 316 break;
301 case 'f': /* fork to background */ 317 case 'f': /* fork to background */
302 comm_append("-f"); 318 result.config.cmd = comm_append(result.config.cmd, "-f");
303 break; 319 break;
304 case 'C': /* Command for remote machine */ 320 case 'C': /* Command for remote machine */
305 commands++; 321 result.config.commands++;
306 if (commands > 1) 322 if (result.config.commands > 1) {
307 xasprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); 323 xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;",
308 xasprintf (&remotecmd, "%s%s", remotecmd, optarg); 324 result.config.remotecmd);
325 }
326 xasprintf(&result.config.remotecmd, "%s%s", result.config.remotecmd, optarg);
309 break; 327 break;
310 case 'S': /* skip n (or all) lines on stdout */ 328 case 'S': /* skip n (or all) lines on stdout */
311 if (optarg == NULL) 329 if (optarg == NULL) {
312 skip_stdout = -1; /* skip all output on stdout */ 330 result.config.skip_stdout = -1; /* skip all output on stdout */
313 else if (!is_integer (optarg)) 331 } else if (!is_integer(optarg)) {
314 usage_va(_("skip-stdout argument must be an integer")); 332 usage_va(_("skip-stdout argument must be an integer"));
315 else 333 } else {
316 skip_stdout = atoi (optarg); 334 result.config.skip_stdout = atoi(optarg);
335 }
317 break; 336 break;
318 case 'E': /* skip n (or all) lines on stderr */ 337 case 'E': /* skip n (or all) lines on stderr */
319 if (optarg == NULL) 338 if (optarg == NULL) {
320 skip_stderr = -1; /* skip all output on stderr */ 339 result.config.skip_stderr = -1; /* skip all output on stderr */
321 else if (!is_integer (optarg)) 340 } else if (!is_integer(optarg)) {
322 usage_va(_("skip-stderr argument must be an integer")); 341 usage_va(_("skip-stderr argument must be an integer"));
323 else 342 } else {
324 skip_stderr = atoi (optarg); 343 result.config.skip_stderr = atoi(optarg);
344 }
325 break; 345 break;
326 case 'W': /* exit with warning if there is an output on stderr */ 346 case 'e': /* exit with unknown if there is an output on stderr */
327 warn_on_stderr = 1; 347 result.config.unknown_on_stderr = true;
328 break; 348 break;
329 case 'o': /* Extra options for the ssh command */ 349 case 'W': /* exit with warning if there is an output on stderr */
330 comm_append("-o"); 350 result.config.warn_on_stderr = true;
331 comm_append(optarg);
332 break; 351 break;
333 case 'q': /* Tell the ssh command to be quiet */ 352 case 'o': /* Extra options for the ssh command */
334 comm_append("-q"); 353 result.config.cmd = comm_append(result.config.cmd, "-o");
354 result.config.cmd = comm_append(result.config.cmd, optarg);
335 break; 355 break;
336 case 'F': /* ssh configfile */ 356 case 'q': /* Tell the ssh command to be quiet */
337 comm_append("-F"); 357 result.config.cmd = comm_append(result.config.cmd, "-q");
338 comm_append(optarg);
339 break; 358 break;
340 default: /* help */ 359 case 'F': /* ssh configfile */
360 result.config.cmd = comm_append(result.config.cmd, "-F");
361 result.config.cmd = comm_append(result.config.cmd, optarg);
362 break;
363 default: /* help */
341 usage5(); 364 usage5();
342 } 365 }
343 } 366 }
344 367
345 c = optind; 368 int c = optind;
346 if (hostname == NULL) { 369 if (result.config.hostname == NULL) {
347 if (c <= argc) { 370 if (c <= argc) {
348 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname); 371 die(STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
349 } 372 }
350 hostname = argv[c++]; 373 result.config.hostname = argv[c++];
351 } 374 }
352 375
353 if (strlen(remotecmd) == 0) { 376 if (strlen(result.config.remotecmd) == 0) {
354 for (; c < argc; c++) 377 for (; c < argc; c++) {
355 if (strlen(remotecmd) > 0) 378 if (strlen(result.config.remotecmd) > 0) {
356 xasprintf (&remotecmd, "%s %s", remotecmd, argv[c]); 379 xasprintf(&result.config.remotecmd, "%s %s", result.config.remotecmd, argv[c]);
357 else 380 } else {
358 xasprintf (&remotecmd, "%s", argv[c]); 381 xasprintf(&result.config.remotecmd, "%s", argv[c]);
382 }
383 }
359 } 384 }
360 385
361 if (commands > 1 || passive) 386 if (result.config.commands > 1 || result.config.passive) {
362 xasprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); 387 xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", result.config.remotecmd);
388 }
363 389
364 if (remotecmd == NULL || strlen (remotecmd) <= 1) 390 if (result.config.remotecmd == NULL || strlen(result.config.remotecmd) <= 1) {
365 usage_va(_("No remotecmd")); 391 usage_va(_("No remotecmd"));
392 }
366 393
367 comm_append(hostname); 394 result.config.cmd = comm_append(result.config.cmd, result.config.hostname);
368 comm_append(remotecmd); 395 result.config.cmd = comm_append(result.config.cmd, result.config.remotecmd);
369 396
370 return validate_arguments (); 397 return validate_arguments(result);
371} 398}
372 399
400command_construct comm_append(command_construct cmd, const char *str) {
373 401
374void 402 if (verbose) {
375comm_append (const char *str) 403 for (int i = 0; i < cmd.commargc; i++) {
376{ 404 printf("Current command: [%i] %s\n", i, cmd.commargv[i]);
405 }
406
407 printf("Appending: %s\n", str);
408 }
377 409
378 if (++commargc > NP_MAXARGS) 410 if (++cmd.commargc > NP_MAXARGS) {
379 die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS); 411 die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS);
412 }
380 413
381 if ((commargv = (char **)realloc(commargv, (commargc+1) * sizeof(char *))) == NULL) 414 if ((cmd.commargv = (char **)realloc(cmd.commargv, (cmd.commargc + 1) * sizeof(char *))) ==
415 NULL) {
382 die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n")); 416 die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n"));
417 }
383 418
384 commargv[commargc-1] = strdup(str); 419 cmd.commargv[cmd.commargc - 1] = strdup(str);
385 commargv[commargc] = NULL; 420 cmd.commargv[cmd.commargc] = NULL;
386 421
422 return cmd;
387} 423}
388 424
389int 425check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper config_wrapper) {
390validate_arguments (void) 426 if (config_wrapper.config.remotecmd == NULL || config_wrapper.config.hostname == NULL) {
391{ 427 config_wrapper.errorcode = ERROR;
392 if (remotecmd == NULL || hostname == NULL) 428 return config_wrapper;
393 return ERROR; 429 }
394 430
395 if (passive && commands != services) 431 if (config_wrapper.config.passive &&
396 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname); 432 config_wrapper.config.commands != config_wrapper.config.number_of_services) {
433 die(STATE_UNKNOWN,
434 _("%s: In passive mode, you must provide a service name for each command.\n"),
435 progname);
436 }
397 437
398 if (passive && host_shortname == NULL) 438 if (config_wrapper.config.passive && config_wrapper.config.host_shortname == NULL) {
399 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the monitoring configs.\n"), progname); 439 die(STATE_UNKNOWN,
440 _("%s: In passive mode, you must provide the host short name from the monitoring "
441 "configs.\n"),
442 progname);
443 }
400 444
401 return OK; 445 return config_wrapper;
402} 446}
403 447
404 448void print_help(void) {
405void 449 print_revision(progname, NP_VERSION);
406print_help (void) 450
407{ 451 printf("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
408 print_revision (progname, NP_VERSION); 452 printf(COPYRIGHT, copyright, email);
409 453
410 printf ("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"); 454 printf(_("This plugin uses SSH to execute commands on a remote host"));
411 printf (COPYRIGHT, copyright, email); 455
412 456 printf("\n\n");
413 printf (_("This plugin uses SSH to execute commands on a remote host")); 457
414 458 print_usage();
415 printf ("\n\n"); 459
416 460 printf(UT_HELP_VRSN);
417 print_usage (); 461
418 462 printf(UT_EXTRA_OPTS);
419 printf (UT_HELP_VRSN); 463
420 464 printf(UT_HOST_PORT, 'p', "none");
421 printf (UT_EXTRA_OPTS); 465
422 466 printf(UT_IPv46);
423 printf (UT_HOST_PORT, 'p', "none"); 467
424 468 printf(" %s\n", "-1, --proto1");
425 printf (UT_IPv46); 469 printf(" %s\n", _("tell ssh to use Protocol 1 [optional]"));
426 470 printf(" %s\n", "-2, --proto2");
427 printf (" %s\n", "-1, --proto1"); 471 printf(" %s\n", _("tell ssh to use Protocol 2 [optional]"));
428 printf (" %s\n", _("tell ssh to use Protocol 1 [optional]")); 472 printf(" %s\n", "-S, --skip-stdout[=n]");
429 printf (" %s\n", "-2, --proto2"); 473 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]"));
430 printf (" %s\n", _("tell ssh to use Protocol 2 [optional]")); 474 printf(" %s\n", "-E, --skip-stderr[=n]");
431 printf (" %s\n", "-S, --skip-stdout[=n]"); 475 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]"));
432 printf (" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]")); 476 printf(" %s\n", "-e, --unknown-on-stderr");
433 printf (" %s\n", "-E, --skip-stderr[=n]"); 477 printf(" %s\n", _("Exit with UNKNOWN, if there is output on STDERR"));
434 printf (" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]")); 478 printf(" %s\n", "-W, --warn-on-stderr");
435 printf (" %s\n", "-W, --warn-on-stderr]"); 479 printf(" %s\n", _("Exit with WARNING, if there is output on STDERR"));
436 printf (" %s\n", _("Exit with an warning, if there is an output on STDERR")); 480 printf(" %s\n", "-f");
437 printf (" %s\n", "-f"); 481 printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always "
438 printf (" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always return OK if ssh is executed")); 482 "return OK if ssh is executed"));
439 printf (" %s\n","-C, --command='COMMAND STRING'"); 483 printf(" %s\n", "-C, --command='COMMAND STRING'");
440 printf (" %s\n", _("command to execute on the remote machine")); 484 printf(" %s\n", _("command to execute on the remote machine"));
441 printf (" %s\n","-l, --logname=USERNAME"); 485 printf(" %s\n", "-l, --logname=USERNAME");
442 printf (" %s\n", _("SSH user name on remote host [optional]")); 486 printf(" %s\n", _("SSH user name on remote host [optional]"));
443 printf (" %s\n","-i, --identity=KEYFILE"); 487 printf(" %s\n", "-i, --identity=KEYFILE");
444 printf (" %s\n", _("identity of an authorized key [optional]")); 488 printf(" %s\n", _("identity of an authorized key [optional]"));
445 printf (" %s\n","-O, --output=FILE"); 489 printf(" %s\n", "-O, --output=FILE");
446 printf (" %s\n", _("external command file for monitoring [optional]")); 490 printf(" %s\n", _("external command file for monitoring [optional]"));
447 printf (" %s\n","-s, --services=LIST"); 491 printf(" %s\n", "-s, --services=LIST");
448 printf (" %s\n", _("list of monitoring service names, separated by ':' [optional]")); 492 printf(" %s\n", _("list of monitoring service names, separated by ':' [optional]"));
449 printf (" %s\n","-n, --name=NAME"); 493 printf(" %s\n", "-n, --name=NAME");
450 printf (" %s\n", _("short name of host in the monitoring configuration [optional]")); 494 printf(" %s\n", _("short name of host in the monitoring configuration [optional]"));
451 printf (" %s\n","-o, --ssh-option=OPTION"); 495 printf(" %s\n", "-o, --ssh-option=OPTION");
452 printf (" %s\n", _("Call ssh with '-o OPTION' (may be used multiple times) [optional]")); 496 printf(" %s\n", _("Call ssh with '-o OPTION' (may be used multiple times) [optional]"));
453 printf (" %s\n","-F, --configfile"); 497 printf(" %s\n", "-F, --configfile");
454 printf (" %s\n", _("Tell ssh to use this configfile [optional]")); 498 printf(" %s\n", _("Tell ssh to use this configfile [optional]"));
455 printf (" %s\n","-q, --quiet"); 499 printf(" %s\n", "-q, --quiet");
456 printf (" %s\n", _("Tell ssh to suppress warning and diagnostic messages [optional]")); 500 printf(" %s\n", _("Tell ssh to suppress warning and diagnostic messages [optional]"));
457 printf (UT_WARN_CRIT); 501 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
458 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 502 printf(" %s\n", "-U, --unknown-timeout");
459 printf (" %s\n","-U, --unknown-timeout"); 503 printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL"));
460 printf (" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); 504 printf(UT_VERBOSE);
461 printf (UT_VERBOSE); 505 printf("\n");
506 printf(" %s\n", _("The most common mode of use is to refer to a local identity file with"));
507 printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null"));
508 printf(" %s\n", _("passphrase and the public key should be listed in the authorized_keys"));
509 printf(" %s\n", _("file of the remote host. Usually the key will be restricted to running"));
510 printf(" %s\n", _("only one command on the remote server. If the remote SSH server tracks"));
511 printf(" %s\n", _("invocation arguments, the one remote program may be an agent that can"));
512 printf(" %s\n", _("execute additional commands as proxy"));
462 printf("\n"); 513 printf("\n");
463 printf (" %s\n", _("The most common mode of use is to refer to a local identity file with")); 514 printf(" %s\n", _("To use passive mode, provide multiple '-C' options, and provide"));
464 printf (" %s\n", _("the '-i' option. In this mode, the identity pair should have a null")); 515 printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)"));
465 printf (" %s\n", _("passphrase and the public key should be listed in the authorized_keys")); 516 printf("\n");
466 printf (" %s\n", _("file of the remote host. Usually the key will be restricted to running")); 517 printf("%s\n", _("Examples:"));
467 printf (" %s\n", _("only one command on the remote server. If the remote SSH server tracks")); 518 printf(
468 printf (" %s\n", _("invocation arguments, the one remote program may be an agent that can")); 519 " %s\n",
469 printf (" %s\n", _("execute additional commands as proxy")); 520 "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo");
470 printf("\n"); 521 printf(" %s\n", "$ cat /tmp/foo");
471 printf (" %s\n", _("To use passive mode, provide multiple '-C' options, and provide")); 522 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days");
472 printf (" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); 523 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days");
473 printf ("\n"); 524 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days");
474 printf ("%s\n", _("Examples:"));
475 printf (" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo");
476 printf (" %s\n", "$ cat /tmp/foo");
477 printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days");
478 printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days");
479 printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days");
480 525
481 printf(UT_SUPPORT); 526 printf(UT_SUPPORT);
482} 527}
483 528
484 529void print_usage(void) {
485 530 printf("%s\n", _("Usage:"));
486void 531 printf(" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n"
487print_usage (void) 532 " [-S [lines]] [-E [lines]] [-e|-W] [-t timeout] [-i identity]\n"
488{ 533 " [-l user] [-n name] [-s servicelist] [-O outputfile]\n"
489 printf ("%s\n", _("Usage:")); 534 " [-p port] [-o ssh-option] [-F configfile]\n",
490 printf (" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n" 535 progname);
491 " [-S [lines]] [-E [lines]] [-W] [-t timeout] [-i identity]\n"
492 " [-l user] [-n name] [-s servicelist] [-O outputfile]\n"
493 " [-p port] [-o ssh-option] [-F configfile]\n",
494 progname);
495} 536}
diff --git a/plugins/check_by_ssh.d/config.h b/plugins/check_by_ssh.d/config.h
new file mode 100644
index 00000000..0e4b56d4
--- /dev/null
+++ b/plugins/check_by_ssh.d/config.h
@@ -0,0 +1,58 @@
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 unknown_on_stderr;
25 bool warn_on_stderr;
26 int skip_stdout;
27 int skip_stderr;
28 bool passive;
29 char *outputfile;
30} check_by_ssh_config;
31
32check_by_ssh_config check_by_ssh_config_init() {
33 check_by_ssh_config tmp = {
34 .hostname = NULL,
35 .host_shortname = NULL,
36
37 .service = NULL,
38 .number_of_services = 0,
39
40 .commands = 0,
41 .remotecmd = "",
42
43 .cmd =
44 {
45 .commargc = 0,
46 .commargv = NULL,
47 },
48
49 .unknown_timeout = false,
50 .unknown_on_stderr = false,
51 .warn_on_stderr = false,
52 .skip_stderr = 0,
53 .skip_stdout = 0,
54 .passive = false,
55 .outputfile = NULL,
56 };
57 return tmp;
58}
diff --git a/plugins/check_cluster.c b/plugins/check_cluster.c
index e1ede9f7..1cbdcd60 100644
--- a/plugins/check_cluster.c
+++ b/plugins/check_cluster.c
@@ -1,92 +1,87 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* check_cluster.c - Host and Service Cluster Plugin for Monitoring 3 * check_cluster.c - Host and Service Cluster Plugin for Monitoring
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2004 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 2000-2004 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2007 Monitoring Plugins Development Team 7 * Copyright (c) 2007-2024 Monitoring Plugins Development Team
8* 8 *
9* This program is free software: you can redistribute it and/or modify 9 * This program is free software: you can redistribute it and/or modify
10* it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
11* the Free Software Foundation, either version 3 of the License, or 11 * the Free Software Foundation, either version 3 of the License, or
12* (at your option) any later version. 12 * (at your option) any later version.
13* 13 *
14* This program is distributed in the hope that it will be useful, 14 * This program is distributed in the hope that it will be useful,
15* but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17* GNU General Public License for more details. 17 * GNU General Public License for more details.
18* 18 *
19* You should have received a copy of the GNU General Public License 19 * You should have received a copy of the GNU General Public License
20* along with this program. If not, see <http://www.gnu.org/licenses/>. 20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21* 21 *
22* 22 *
23*****************************************************************************/ 23 *****************************************************************************/
24 24
25const char *progname = "check_cluster"; 25const char *progname = "check_cluster";
26const char *copyright = "2000-2007"; 26const char *copyright = "2000-2024";
27const char *email = "devel@monitoring-plugins.org"; 27const char *email = "devel@monitoring-plugins.org";
28 28
29#include "output.h"
30#include "states.h"
29#include "common.h" 31#include "common.h"
30#include "utils.h" 32#include "utils.h"
31#include "utils_base.h" 33#include "utils_base.h"
34#include "check_cluster.d/config.h"
32 35
33#define CHECK_SERVICES 1 36static void print_help(void);
34#define CHECK_HOSTS 2 37void print_usage(void);
35 38
36void print_help (void); 39static int verbose = 0;
37void print_usage (void);
38 40
39int total_services_ok=0; 41typedef struct {
40int total_services_warning=0; 42 int errorcode;
41int total_services_unknown=0; 43 check_cluster_config config;
42int total_services_critical=0; 44} check_cluster_config_wrapper;
45static check_cluster_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
43 46
44int total_hosts_up=0; 47int main(int argc, char **argv) {
45int total_hosts_down=0; 48 setlocale(LC_ALL, "");
46int total_hosts_unreachable=0; 49 bindtextdomain(PACKAGE, LOCALEDIR);
47 50 textdomain(PACKAGE);
48char *warn_threshold;
49char *crit_threshold;
50
51int check_type=CHECK_SERVICES;
52
53char *data_vals=NULL;
54char *label=NULL;
55
56int verbose=0;
57
58int process_arguments(int,char **);
59
60
61
62int main(int argc, char **argv){
63 char *ptr;
64 int data_val;
65 int return_code=STATE_OK;
66 thresholds *thresholds = NULL;
67
68 setlocale (LC_ALL, "");
69 bindtextdomain (PACKAGE, LOCALEDIR);
70 textdomain (PACKAGE);
71 51
72 /* Parse extra opts if any */ 52 /* Parse extra opts if any */
73 argv=np_extra_opts(&argc, argv, progname); 53 argv = np_extra_opts(&argc, argv, progname);
74 54
75 if(process_arguments(argc,argv)==ERROR) 55 check_cluster_config_wrapper tmp_config = process_arguments(argc, argv);
56 if (tmp_config.errorcode == ERROR) {
76 usage(_("Could not parse arguments")); 57 usage(_("Could not parse arguments"));
58 }
59
60 const check_cluster_config config = tmp_config.config;
61
62 if (config.output_format_is_set) {
63 mp_set_format(config.output_format);
64 }
77 65
78 /* Initialize the thresholds */ 66 /* Initialize the thresholds */
79 set_thresholds(&thresholds, warn_threshold, crit_threshold); 67 if (verbose) {
80 if(verbose) 68 print_thresholds("check_cluster", config.thresholds);
81 print_thresholds("check_cluster", thresholds); 69 }
82 70
71 int data_val;
72 int total_services_ok = 0;
73 int total_services_warning = 0;
74 int total_services_unknown = 0;
75 int total_services_critical = 0;
76 int total_hosts_up = 0;
77 int total_hosts_down = 0;
78 int total_hosts_unreachable = 0;
83 /* check the data values */ 79 /* check the data values */
84 for(ptr=strtok(data_vals,",");ptr!=NULL;ptr=strtok(NULL,",")){ 80 for (char *ptr = strtok(config.data_vals, ","); ptr != NULL; ptr = strtok(NULL, ",")) {
85 81 data_val = atoi(ptr);
86 data_val=atoi(ptr);
87 82
88 if(check_type==CHECK_SERVICES){ 83 if (config.check_type == CHECK_SERVICES) {
89 switch(data_val){ 84 switch (data_val) {
90 case 0: 85 case 0:
91 total_services_ok++; 86 total_services_ok++;
92 break; 87 break;
@@ -101,10 +96,9 @@ int main(int argc, char **argv){
101 break; 96 break;
102 default: 97 default:
103 break; 98 break;
104 } 99 }
105 } 100 } else {
106 else{ 101 switch (data_val) {
107 switch(data_val){
108 case 0: 102 case 0:
109 total_hosts_up++; 103 total_hosts_up++;
110 break; 104 break;
@@ -116,125 +110,150 @@ int main(int argc, char **argv){
116 break; 110 break;
117 default: 111 default:
118 break; 112 break;
119 } 113 }
120 } 114 }
121 } 115 }
122 116
117 mp_check overall = mp_check_init();
118 mp_subcheck sc_real_test = mp_subcheck_init();
119 sc_real_test = mp_set_subcheck_default_state(sc_real_test, STATE_OK);
123 120
124 /* return the status of the cluster */ 121 /* return the status of the cluster */
125 if(check_type==CHECK_SERVICES){ 122 if (config.check_type == CHECK_SERVICES) {
126 return_code=get_status(total_services_warning+total_services_unknown+total_services_critical, thresholds); 123 sc_real_test = mp_set_subcheck_state(
127 printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n", 124 sc_real_test,
128 state_text(return_code), (label==NULL)?"Service cluster":label, 125 get_status(total_services_warning + total_services_unknown + total_services_critical,
129 total_services_ok,total_services_warning, 126 config.thresholds));
130 total_services_unknown,total_services_critical); 127 xasprintf(&sc_real_test.output, "%s: %d ok, %d warning, %d unknown, %d critical",
131 } 128 (config.label == NULL) ? "Service cluster" : config.label, total_services_ok,
132 else{ 129 total_services_warning, total_services_unknown, total_services_critical);
133 return_code=get_status(total_hosts_down+total_hosts_unreachable, thresholds); 130 } else {
134 printf("CLUSTER %s: %s: %d up, %d down, %d unreachable\n", 131 sc_real_test = mp_set_subcheck_state(
135 state_text(return_code), (label==NULL)?"Host cluster":label, 132 sc_real_test,
136 total_hosts_up,total_hosts_down,total_hosts_unreachable); 133 get_status(total_hosts_down + total_hosts_unreachable, config.thresholds));
134 xasprintf(&sc_real_test.output, "%s: %d up, %d down, %d unreachable\n",
135 (config.label == NULL) ? "Host cluster" : config.label, total_hosts_up,
136 total_hosts_down, total_hosts_unreachable);
137 } 137 }
138 138
139 return return_code; 139 mp_add_subcheck_to_check(&overall, sc_real_test);
140}
141 140
141 mp_exit(overall);
142}
142 143
144check_cluster_config_wrapper process_arguments(int argc, char **argv) {
145 enum {
146 output_format_index = CHAR_MAX + 1,
147 };
143 148
144int process_arguments(int argc, char **argv){ 149 static struct option longopts[] = {{"data", required_argument, 0, 'd'},
145 int c; 150 {"warning", required_argument, 0, 'w'},
146 char *ptr; 151 {"critical", required_argument, 0, 'c'},
147 int option=0; 152 {"label", required_argument, 0, 'l'},
148 static struct option longopts[]={ 153 {"host", no_argument, 0, 'h'},
149 {"data", required_argument,0,'d'}, 154 {"service", no_argument, 0, 's'},
150 {"warning", required_argument,0,'w'}, 155 {"verbose", no_argument, 0, 'v'},
151 {"critical", required_argument,0,'c'}, 156 {"version", no_argument, 0, 'V'},
152 {"label", required_argument,0,'l'}, 157 {"help", no_argument, 0, 'H'},
153 {"host", no_argument, 0,'h'}, 158 {"output-format", required_argument, 0, output_format_index},
154 {"service", no_argument, 0,'s'}, 159 {0, 0, 0, 0}};
155 {"verbose", no_argument, 0,'v'}, 160
156 {"version", no_argument, 0,'V'}, 161 check_cluster_config_wrapper result = {
157 {"help", no_argument, 0,'H'}, 162 .errorcode = OK,
158 {0,0,0,0} 163 .config = check_cluster_config_init(),
159 }; 164 };
160 165
161 /* no options were supplied */ 166 /* no options were supplied */
162 if(argc<2) 167 if (argc < 2) {
163 return ERROR; 168 result.errorcode = ERROR;
164 169 return result;
165 while(1){ 170 }
166 171
167 c=getopt_long(argc,argv,"hHsvVw:c:d:l:",longopts,&option); 172 int option = 0;
173 char *warn_threshold = NULL;
174 char *crit_threshold = NULL;
175 while (true) {
176 int option_index = getopt_long(argc, argv, "hHsvVw:c:d:l:", longopts, &option);
168 177
169 if(c==-1 || c==EOF || c==1) 178 if (option_index == -1 || option_index == EOF || option_index == 1) {
170 break; 179 break;
180 }
171 181
172 switch(c){ 182 switch (option_index) {
173
174 case 'h': /* host cluster */ 183 case 'h': /* host cluster */
175 check_type=CHECK_HOSTS; 184 result.config.check_type = CHECK_HOSTS;
176 break; 185 break;
177
178 case 's': /* service cluster */ 186 case 's': /* service cluster */
179 check_type=CHECK_SERVICES; 187 result.config.check_type = CHECK_SERVICES;
180 break; 188 break;
181
182 case 'w': /* warning threshold */ 189 case 'w': /* warning threshold */
183 warn_threshold = strdup(optarg); 190 warn_threshold = strdup(optarg);
184 break; 191 break;
185
186 case 'c': /* warning threshold */ 192 case 'c': /* warning threshold */
187 crit_threshold = strdup(optarg); 193 crit_threshold = strdup(optarg);
188 break; 194 break;
189
190 case 'd': /* data values */ 195 case 'd': /* data values */
191 data_vals=(char *)strdup(optarg); 196 result.config.data_vals = strdup(optarg);
192 /* validate data */ 197 /* validate data */
193 for (ptr=data_vals;ptr!=NULL;ptr+=2){ 198 for (char *ptr = result.config.data_vals; ptr != NULL; ptr += 2) {
194 if (ptr[0]<'0' || ptr[0]>'3') 199 if (ptr[0] < '0' || ptr[0] > '3') {
195 return ERROR; 200 result.errorcode = ERROR;
196 if (ptr[1]=='\0') 201 return result;
202 }
203 if (ptr[1] == '\0') {
197 break; 204 break;
198 if (ptr[1]!=',') 205 }
199 return ERROR; 206 if (ptr[1] != ',') {
207 result.errorcode = ERROR;
208 return result;
209 }
200 } 210 }
201 break; 211 break;
202
203 case 'l': /* text label */ 212 case 'l': /* text label */
204 label=(char *)strdup(optarg); 213 result.config.label = strdup(optarg);
205 break; 214 break;
206
207 case 'v': /* verbose */ 215 case 'v': /* verbose */
208 verbose++; 216 verbose++;
209 break; 217 break;
210
211 case 'V': /* version */ 218 case 'V': /* version */
212 print_revision (progname, NP_VERSION); 219 print_revision(progname, NP_VERSION);
213 exit (STATE_UNKNOWN); 220 exit(STATE_UNKNOWN);
214 break; 221 break;
215
216 case 'H': /* help */ 222 case 'H': /* help */
217 print_help(); 223 print_help();
218 exit(STATE_UNKNOWN); 224 exit(STATE_UNKNOWN);
219 break; 225 break;
226 case output_format_index: {
227 parsed_output_format parser = mp_parse_output_format(optarg);
228 if (!parser.parsing_success) {
229 // TODO List all available formats here, maybe add anothoer usage function
230 printf("Invalid output format: %s\n", optarg);
231 exit(STATE_UNKNOWN);
232 }
220 233
234 result.config.output_format_is_set = true;
235 result.config.output_format = parser.output_format;
236 break;
237 }
221 default: 238 default:
222 return ERROR; 239 result.errorcode = ERROR;
240 return result;
223 break; 241 break;
224 } 242 }
225 } 243 }
226 244
227 if(data_vals==NULL) 245 if (result.config.data_vals == NULL) {
228 return ERROR; 246 result.errorcode = ERROR;
247 return result;
248 }
229 249
230 return OK; 250 set_thresholds(&result.config.thresholds, warn_threshold, crit_threshold);
251 return result;
231} 252}
232 253
233void 254void print_help(void) {
234print_help(void)
235{
236 print_revision(progname, NP_VERSION); 255 print_revision(progname, NP_VERSION);
237 printf ("Copyright (c) 2000-2004 Ethan Galstad (nagios@nagios.org)\n"); 256 printf("Copyright (c) 2000-2004 Ethan Galstad (nagios@nagios.org)\n");
238 printf(COPYRIGHT, copyright, email); 257 printf(COPYRIGHT, copyright, email);
239 258
240 printf(_("Host/Service Cluster Plugin for Monitoring")); 259 printf(_("Host/Service Cluster Plugin for Monitoring"));
@@ -245,45 +264,43 @@ print_help(void)
245 printf("\n"); 264 printf("\n");
246 printf("%s\n", _("Options:")); 265 printf("%s\n", _("Options:"));
247 printf(UT_EXTRA_OPTS); 266 printf(UT_EXTRA_OPTS);
248 printf (" %s\n", "-s, --service"); 267 printf(" %s\n", "-s, --service");
249 printf (" %s\n", _("Check service cluster status")); 268 printf(" %s\n", _("Check service cluster status"));
250 printf (" %s\n", "-h, --host"); 269 printf(" %s\n", "-h, --host");
251 printf (" %s\n", _("Check host cluster status")); 270 printf(" %s\n", _("Check host cluster status"));
252 printf (" %s\n", "-l, --label=STRING"); 271 printf(" %s\n", "-l, --label=STRING");
253 printf (" %s\n", _("Optional prepended text output (i.e. \"Host cluster\")")); 272 printf(" %s\n", _("Optional prepended text output (i.e. \"Host cluster\")"));
254 printf (" %s\n", "-w, --warning=THRESHOLD"); 273 printf(" %s\n", "-w, --warning=THRESHOLD");
255 printf (" %s\n", _("Specifies the range of hosts or services in cluster that must be in a")); 274 printf(" %s\n", _("Specifies the range of hosts or services in cluster that must be in a"));
256 printf (" %s\n", _("non-OK state in order to return a WARNING status level")); 275 printf(" %s\n", _("non-OK state in order to return a WARNING status level"));
257 printf (" %s\n", "-c, --critical=THRESHOLD"); 276 printf(" %s\n", "-c, --critical=THRESHOLD");
258 printf (" %s\n", _("Specifies the range of hosts or services in cluster that must be in a")); 277 printf(" %s\n", _("Specifies the range of hosts or services in cluster that must be in a"));
259 printf (" %s\n", _("non-OK state in order to return a CRITICAL status level")); 278 printf(" %s\n", _("non-OK state in order to return a CRITICAL status level"));
260 printf (" %s\n", "-d, --data=LIST"); 279 printf(" %s\n", "-d, --data=LIST");
261 printf (" %s\n", _("The status codes of the hosts or services in the cluster, separated by")); 280 printf(" %s\n", _("The status codes of the hosts or services in the cluster, separated by"));
262 printf (" %s\n", _("commas")); 281 printf(" %s\n", _("commas"));
263 282
264 printf(UT_VERBOSE); 283 printf(UT_VERBOSE);
265 284
285 printf(UT_OUTPUT_FORMAT);
286
266 printf("\n"); 287 printf("\n");
267 printf("%s\n", _("Notes:")); 288 printf("%s\n", _("Notes:"));
268 printf(UT_THRESHOLDS_NOTES); 289 printf(UT_THRESHOLDS_NOTES);
269 290
270 printf ("\n"); 291 printf("\n");
271 printf ("%s\n", _("Examples:")); 292 printf("%s\n", _("Examples:"));
272 printf (" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:"); 293 printf(" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:");
273 printf (" %s\n", _("Will alert critical if there are 3 or more service data points in a non-OK") ); 294 printf(" %s\n",
274 printf (" %s\n", _("state.") ); 295 _("Will alert critical if there are 3 or more service data points in a non-OK"));
296 printf(" %s\n", _("state."));
275 297
276 printf(UT_SUPPORT); 298 printf(UT_SUPPORT);
277} 299}
278 300
279 301void print_usage(void) {
280void
281print_usage(void)
282{
283 302
284 printf("%s\n", _("Usage:")); 303 printf("%s\n", _("Usage:"));
285 printf(" %s (-s | -h) -d val1[,val2,...,valn] [-l label]\n", progname); 304 printf(" %s (-s | -h) -d val1[,val2,...,valn] [-l label]\n", progname);
286 printf("[-w threshold] [-c threshold] [-v] [--help]\n"); 305 printf("[-w threshold] [-c threshold] [-v] [--help]\n");
287
288} 306}
289
diff --git a/plugins/check_cluster.d/config.h b/plugins/check_cluster.d/config.h
new file mode 100644
index 00000000..054657b0
--- /dev/null
+++ b/plugins/check_cluster.d/config.h
@@ -0,0 +1,33 @@
1#pragma once
2
3#include "../../config.h"
4#include "../../lib/thresholds.h"
5#include "output.h"
6#include <stddef.h>
7
8enum {
9 CHECK_SERVICES = 1,
10 CHECK_HOSTS = 2
11};
12
13typedef struct {
14 char *data_vals;
15 thresholds *thresholds;
16 int check_type;
17 char *label;
18
19 mp_output_format output_format;
20 bool output_format_is_set;
21} check_cluster_config;
22
23check_cluster_config check_cluster_config_init() {
24 check_cluster_config tmp = {
25 .data_vals = NULL,
26 .thresholds = NULL,
27 .check_type = CHECK_SERVICES,
28 .label = NULL,
29
30 .output_format_is_set = false,
31 };
32 return tmp;
33}
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index e25d7a79..fc704171 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -1,2736 +1,1889 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_curl plugin 3 * Monitoring check_curl plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2019 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_curl plugin 10 * This file contains the check_curl plugin
11* 11 *
12* This plugin tests the HTTP service on the specified host. It can test 12 * This plugin tests the HTTP service on the specified host. It can test
13* normal (http) and secure (https) servers, follow redirects, search for 13 * normal (http) and secure (https) servers, follow redirects, search for
14* strings and regular expressions, check connection times, and report on 14 * strings and regular expressions, check connection times, and report on
15* certificate expiration times. 15 * certificate expiration times.
16* 16 *
17* This plugin uses functions from the curl library, see 17 * This plugin uses functions from the curl library, see
18* http://curl.haxx.se 18 * http://curl.haxx.se
19* 19 *
20* This program is free software: you can redistribute it and/or modify 20 * This program is free software: you can redistribute it and/or modify
21* it under the terms of the GNU General Public License as published by 21 * it under the terms of the GNU General Public License as published by
22* the Free Software Foundation, either version 3 of the License, or 22 * the Free Software Foundation, either version 3 of the License, or
23* (at your option) any later version. 23 * (at your option) any later version.
24* 24 *
25* This program is distributed in the hope that it will be useful, 25 * This program is distributed in the hope that it will be useful,
26* but WITHOUT ANY WARRANTY; without even the implied warranty of 26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28* GNU General Public License for more details. 28 * GNU General Public License for more details.
29* 29 *
30* You should have received a copy of the GNU General Public License 30 * You should have received a copy of the GNU General Public License
31* along with this program. If not, see <http://www.gnu.org/licenses/>. 31 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32* 32 *
33* 33 *
34*****************************************************************************/ 34 *****************************************************************************/
35const char *progname = "check_curl";
36 35
37const char *copyright = "2006-2019"; 36const char *progname = "check_curl";
37const char *copyright = "2006-2024";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "check_curl.d/config.h"
41#include "states.h"
42#include "thresholds.h"
40#include <stdbool.h> 43#include <stdbool.h>
41#include <ctype.h> 44#include <ctype.h>
45#include "output.h"
46#include "perfdata.h"
42 47
48#include <assert.h>
43#include "common.h" 49#include "common.h"
44#include "utils.h" 50#include "utils.h"
51#include "./check_curl.d/check_curl_helpers.h"
45 52
46#ifndef LIBCURL_PROTOCOL_HTTP 53#ifndef LIBCURL_PROTOCOL_HTTP
47#error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense 54# error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense
48#endif 55#endif
49 56
50#include "curl/curl.h" 57#include "curl/curl.h"
51#include "curl/easy.h" 58#include "curl/easy.h"
52 59
53#include "picohttpparser.h"
54
55#include "uriparser/Uri.h" 60#include "uriparser/Uri.h"
56 61
57#include <arpa/inet.h> 62#include <arpa/inet.h>
58#include <netinet/in.h> 63#include <netinet/in.h>
59 64
60#if defined(HAVE_SSL) && defined(USE_OPENSSL) 65#if defined(HAVE_SSL) && defined(USE_OPENSSL)
61#include <openssl/opensslv.h> 66# include <openssl/opensslv.h>
62#endif 67#endif
63 68
64#include <netdb.h> 69#include <netdb.h>
65 70
66#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
67
68#define DEFAULT_BUFFER_SIZE 2048
69#define DEFAULT_SERVER_URL "/"
70#define HTTP_EXPECT "HTTP/"
71#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
72enum { 71enum {
73 MAX_IPV4_HOSTLENGTH = 255, 72 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}; 77};
85 78
86enum {
87 FOLLOW_HTTP_CURL = 0,
88 FOLLOW_LIBCURL = 1
89};
90
91/* for buffers for header and body */
92typedef struct {
93 char *buf;
94 size_t buflen;
95 size_t bufsize;
96} curlhelp_write_curlbuf;
97
98/* for buffering the data sent in PUT */
99typedef struct {
100 char *buf;
101 size_t buflen;
102 off_t pos;
103} curlhelp_read_curlbuf;
104
105/* for parsing the HTTP status line */
106typedef struct {
107 int http_major; /* major version of the protocol, always 1 (HTTP/0.9
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
126enum {
127 REGS = 2,
128 MAX_RE_SIZE = 1024
129};
130#include "regex.h" 79#include "regex.h"
131regex_t preg; 80
132regmatch_t pmatch[REGS]; 81// Globals
133char regexp[MAX_RE_SIZE];
134int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
135int errcode;
136bool invert_regex = false;
137int state_regex = STATE_CRITICAL;
138
139char *server_address = NULL;
140char *host_name = NULL;
141char *server_url = 0;
142char server_ip[DEFAULT_BUFFER_SIZE];
143struct curl_slist *server_ips = NULL;
144bool specify_port = false;
145unsigned short server_port = HTTP_PORT;
146unsigned short virtual_port = 0;
147int host_name_length;
148char output_header_search[30] = "";
149char output_string_search[30] = "";
150char *warning_thresholds = NULL;
151char *critical_thresholds = NULL;
152int days_till_exp_warn, days_till_exp_crit;
153thresholds *thlds;
154char user_agent[DEFAULT_BUFFER_SIZE];
155int verbose = 0; 82int verbose = 0;
156bool show_extended_perfdata = false; 83
157bool show_body = false; 84extern char errbuf[MAX_INPUT_BUFFER];
158int min_page_len = 0; 85extern bool is_openssl_callback;
159int max_page_len = 0; 86extern bool add_sslctx_verify_fun;
160int redir_depth = 0;
161int max_depth = DEFAULT_MAX_REDIRS;
162char *http_method = NULL;
163char *http_post_data = NULL;
164char *http_content_type = NULL;
165CURL *curl;
166bool curl_global_initialized = false;
167bool curl_easy_initialized = false;
168struct curl_slist *header_list = NULL;
169bool body_buf_initialized = false;
170curlhelp_write_curlbuf body_buf;
171bool header_buf_initialized = false;
172curlhelp_write_curlbuf header_buf;
173bool status_line_initialized = false;
174curlhelp_statusline status_line;
175bool put_buf_initialized = false;
176curlhelp_read_curlbuf put_buf;
177char http_header[DEFAULT_BUFFER_SIZE];
178long code;
179long socket_timeout = DEFAULT_SOCKET_TIMEOUT;
180double total_time;
181double time_connect;
182double time_appconnect;
183double time_headers;
184double time_firstbyte;
185char errbuf[MAX_INPUT_BUFFER];
186CURLcode res;
187char url[DEFAULT_BUFFER_SIZE];
188char msg[DEFAULT_BUFFER_SIZE];
189char perfstring[DEFAULT_BUFFER_SIZE];
190char header_expect[MAX_INPUT_BUFFER] = "";
191char string_expect[MAX_INPUT_BUFFER] = "";
192char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
193int server_expect_yn = 0;
194char user_auth[MAX_INPUT_BUFFER] = "";
195char proxy_auth[MAX_INPUT_BUFFER] = "";
196char **http_opt_headers;
197int http_opt_headers_count = 0;
198bool display_html = false;
199int onredirect = STATE_OK;
200int followmethod = FOLLOW_HTTP_CURL;
201int followsticky = STICKY_NONE;
202bool use_ssl = false;
203bool use_sni = true;
204bool check_cert = false;
205bool continue_after_check_cert = false;
206typedef union {
207 struct curl_slist* to_info;
208 struct curl_certinfo* to_certinfo;
209} cert_ptr_union;
210cert_ptr_union cert_ptr;
211int ssl_version = CURL_SSLVERSION_DEFAULT;
212char *client_cert = NULL;
213char *client_privkey = NULL;
214char *ca_cert = NULL;
215bool verify_peer_and_host = false;
216bool is_openssl_callback = false;
217#if defined(HAVE_SSL) && defined(USE_OPENSSL)
218X509 *cert = NULL;
219#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
220bool no_body = false;
221int maximum_age = -1;
222int address_family = AF_UNSPEC;
223curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
224int curl_http_version = CURL_HTTP_VERSION_NONE;
225bool automatic_decompression = false;
226char *cookie_jar_file = NULL;
227bool haproxy_protocol = false;
228
229bool process_arguments (int, char**);
230void handle_curl_option_return_code (CURLcode res, const char* option);
231int check_http (void);
232void redir (curlhelp_write_curlbuf*);
233char *perfd_time (double microsec);
234char *perfd_time_connect (double microsec);
235char *perfd_time_ssl (double microsec);
236char *perfd_time_firstbyte (double microsec);
237char *perfd_time_headers (double microsec);
238char *perfd_time_transfer (double microsec);
239char *perfd_size (int page_len);
240void print_help (void);
241void print_usage (void);
242void print_curl_version (void);
243int curlhelp_initwritebuffer (curlhelp_write_curlbuf*);
244size_t curlhelp_buffer_write_callback(void*, size_t , size_t , void*);
245void curlhelp_freewritebuffer (curlhelp_write_curlbuf*);
246int curlhelp_initreadbuffer (curlhelp_read_curlbuf *, const char *, size_t);
247size_t curlhelp_buffer_read_callback(void *, size_t , size_t , void *);
248void curlhelp_freereadbuffer (curlhelp_read_curlbuf *);
249curlhelp_ssl_library curlhelp_get_ssl_library ();
250const char* curlhelp_get_ssl_library_string (curlhelp_ssl_library);
251int net_noopenssl_check_certificate (cert_ptr_union*, int, int);
252
253int curlhelp_parse_statusline (const char*, curlhelp_statusline *);
254void curlhelp_free_statusline (curlhelp_statusline *);
255char *get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header);
256int check_document_dates (const curlhelp_write_curlbuf *, char (*msg)[DEFAULT_BUFFER_SIZE]);
257int get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf);
258 87
259#if defined(HAVE_SSL) && defined(USE_OPENSSL) 88#if defined(HAVE_SSL) && defined(USE_OPENSSL)
260int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); 89static X509 *cert = NULL;
261#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ 90#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
262 91
263void remove_newlines (char *); 92typedef struct {
264void test_file (char *); 93 int errorcode;
94 check_curl_config config;
95} check_curl_config_wrapper;
96static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
265 97
266int 98static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState,
267main (int argc, char **argv) 99 int redir_depth);
268{
269 int result = STATE_UNKNOWN;
270 100
271 setlocale (LC_ALL, ""); 101typedef struct {
272 bindtextdomain (PACKAGE, LOCALEDIR); 102 int redir_depth;
273 textdomain (PACKAGE); 103 check_curl_working_state working_state;
104 int error_code;
105 check_curl_global_state curl_state;
106} redir_wrapper;
107static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/,
108 int redir_depth, check_curl_working_state working_state);
274 109
275 /* Parse extra opts if any */ 110static void print_help(void);
276 argv = np_extra_opts (&argc, argv, progname); 111void print_usage(void);
277 112
278 /* set defaults */ 113static void print_curl_version(void);
279 snprintf( user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)",
280 progname, NP_VERSION, VERSION, curl_version());
281 114
282 /* parse arguments */ 115// typedef struct {
283 if (process_arguments (argc, argv) == false) 116// int errorcode;
284 usage4 (_("Could not parse arguments")); 117// } check_curl_evaluation_wrapper;
118// check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config,
119// mp_check overall[static 1]) {}
285 120
286 if (display_html) 121#if defined(HAVE_SSL) && defined(USE_OPENSSL)
287 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", 122mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
288 use_ssl ? "https" : "http", 123 int days_till_exp_crit);
289 host_name ? host_name : server_address, 124#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
290 virtual_port ? virtual_port : server_port,
291 server_url);
292 125
293 result = check_http (); 126int main(int argc, char **argv) {
294 return result; 127 setlocale(LC_ALL, "");
295} 128 bindtextdomain(PACKAGE, LOCALEDIR);
129 textdomain(PACKAGE);
296 130
297#ifdef HAVE_SSL 131 /* Parse extra opts if any */
298#ifdef USE_OPENSSL 132 argv = np_extra_opts(&argc, argv, progname);
299
300int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
301{
302 (void) preverify_ok;
303 /* TODO: we get all certificates of the chain, so which ones
304 * should we test?
305 * TODO: is the last certificate always the server certificate?
306 */
307 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
308#if OPENSSL_VERSION_NUMBER >= 0x10100000L
309 X509_up_ref(cert);
310#endif
311 if (verbose>=2) {
312 puts("* SSL verify callback with certificate:");
313 X509_NAME *subject, *issuer;
314 printf("* issuer:\n");
315 issuer = X509_get_issuer_name( cert );
316 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE);
317 printf("* curl verify_callback:\n* subject:\n");
318 subject = X509_get_subject_name( cert );
319 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE);
320 puts("");
321 }
322 return 1;
323}
324 133
325CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) 134 /* parse arguments */
326{ 135 check_curl_config_wrapper tmp_config = process_arguments(argc, argv);
327 (void) curl; // ignore unused parameter 136 if (tmp_config.errorcode == ERROR) {
328 (void) parm; // ignore unused parameter 137 usage4(_("Could not parse arguments"));
329 SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback); 138 }
330 139
331 return CURLE_OK; 140 const check_curl_config config = tmp_config.config;
332}
333 141
334#endif /* USE_OPENSSL */ 142 if (config.output_format_is_set) {
335#endif /* HAVE_SSL */ 143 mp_set_format(config.output_format);
336 144 }
337/* returns a string "HTTP/1.x" or "HTTP/2" */
338static char *string_statuscode (int major, int minor)
339{
340 static char buf[10];
341
342 switch (major) {
343 case 1:
344 snprintf (buf, sizeof (buf), "HTTP/%d.%d", major, minor);
345 break;
346 case 2:
347 case 3:
348 snprintf (buf, sizeof (buf), "HTTP/%d", major);
349 break;
350 default:
351 /* assuming here HTTP/N with N>=4 */
352 snprintf (buf, sizeof (buf), "HTTP/%d", major);
353 break;
354 }
355
356 return buf;
357}
358 145
359/* Checks if the server 'reply' is one of the expected 'statuscodes' */ 146 check_curl_working_state working_state = config.initial_config;
360static int
361expected_statuscode (const char *reply, const char *statuscodes)
362{
363 char *expected, *code;
364 int result = 0;
365 147
366 if ((expected = strdup (statuscodes)) == NULL) 148 mp_check overall = mp_check_init();
367 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); 149 mp_subcheck sc_test = check_http(config, working_state, 0);
368 150
369 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ",")) 151 mp_add_subcheck_to_check(&overall, sc_test);
370 if (strstr (reply, code) != NULL) {
371 result = 1;
372 break;
373 }
374 152
375 free (expected); 153 mp_exit(overall);
376 return result;
377} 154}
378 155
379void 156#ifdef HAVE_SSL
380handle_curl_option_return_code (CURLcode res, const char* option) 157# ifdef USE_OPENSSL
381{ 158int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
382 if (res != CURLE_OK) { 159 (void)preverify_ok;
383 snprintf (msg, 160 /* TODO: we get all certificates of the chain, so which ones
384 DEFAULT_BUFFER_SIZE, 161 * should we test?
385 _("Error while setting cURL option '%s': cURL returned %d - %s"), 162 * TODO: is the last certificate always the server certificate?
386 option, 163 */
387 res, 164 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
388 curl_easy_strerror(res)); 165# if OPENSSL_VERSION_NUMBER >= 0x10100000L
389 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); 166 X509_up_ref(cert);
390 } 167# endif
168 if (verbose >= 2) {
169 puts("* SSL verify callback with certificate:");
170 printf("* issuer:\n");
171 X509_NAME *issuer = X509_get_issuer_name(cert);
172 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE);
173 printf("* curl verify_callback:\n* subject:\n");
174 X509_NAME *subject = X509_get_subject_name(cert);
175 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE);
176 puts("");
177 }
178 return 1;
391} 179}
180# endif /* USE_OPENSSL */
181#endif /* HAVE_SSL */
392 182
393int 183#ifdef HAVE_SSL
394lookup_host (const char *host, char *buf, size_t buflen) 184# ifdef USE_OPENSSL
395{ 185CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
396 struct addrinfo hints, *res, *result; 186 (void)curl; // ignore unused parameter
397 char addrstr[100]; 187 (void)parm; // ignore unused parameter
398 size_t addrstr_len; 188 if (add_sslctx_verify_fun) {
399 int errcode; 189 SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback);
400 void *ptr = { 0 }; 190 }
401 size_t buflen_remaining = buflen - 1;
402
403 memset (&hints, 0, sizeof (hints));
404 hints.ai_family = address_family;
405 hints.ai_socktype = SOCK_STREAM;
406 hints.ai_flags |= AI_CANONNAME;
407
408 errcode = getaddrinfo (host, NULL, &hints, &result);
409 if (errcode != 0)
410 return errcode;
411
412 strcpy(buf, "");
413 res = result;
414
415 while (res) {
416 switch (res->ai_family) {
417 case AF_INET:
418 ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
419 break;
420 case AF_INET6:
421 ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
422 break;
423 }
424
425 inet_ntop (res->ai_family, ptr, addrstr, 100);
426 if (verbose >= 1) {
427 printf ("* getaddrinfo IPv%d address: %s\n",
428 res->ai_family == PF_INET6 ? 6 : 4, addrstr);
429 }
430
431 // Append all IPs to buf as a comma-separated string
432 addrstr_len = strlen(addrstr);
433 if (buflen_remaining > addrstr_len + 1) {
434 if (buf[0] != '\0') {
435 strncat(buf, ",", buflen_remaining);
436 buflen_remaining -= 1;
437 }
438 strncat(buf, addrstr, buflen_remaining);
439 buflen_remaining -= addrstr_len;
440 }
441
442 res = res->ai_next;
443 }
444
445 freeaddrinfo(result);
446
447 return 0;
448}
449 191
450static void 192 // workaround for issue:
451cleanup (void) 193 // OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0
452{ 194 // see discussion https://github.com/openssl/openssl/discussions/22690
453 if (status_line_initialized) curlhelp_free_statusline(&status_line); 195# ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
454 status_line_initialized = false; 196 SSL_CTX_set_options(sslctx, SSL_OP_IGNORE_UNEXPECTED_EOF);
455 if (curl_easy_initialized) curl_easy_cleanup (curl); 197# endif
456 curl_easy_initialized = false; 198
457 if (curl_global_initialized) curl_global_cleanup (); 199 return CURLE_OK;
458 curl_global_initialized = false;
459 if (body_buf_initialized) curlhelp_freewritebuffer (&body_buf);
460 body_buf_initialized = false;
461 if (header_buf_initialized) curlhelp_freewritebuffer (&header_buf);
462 header_buf_initialized = false;
463 if (put_buf_initialized) curlhelp_freereadbuffer (&put_buf);
464 put_buf_initialized = false;
465} 200}
201# endif /* USE_OPENSSL */
202#endif /* HAVE_SSL */
466 203
467int 204mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState,
468check_http (void) 205 int redir_depth) {
469{
470 int result = STATE_OK;
471 int result_ssl = STATE_OK;
472 int page_len = 0;
473 int i;
474 char *force_host_header = NULL;
475 struct curl_slist *host = NULL;
476 char addrstr[DEFAULT_BUFFER_SIZE/2];
477 char dnscache[DEFAULT_BUFFER_SIZE];
478
479 /* initialize curl */
480 if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK)
481 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
482 curl_global_initialized = true;
483
484 if ((curl = curl_easy_init()) == NULL) {
485 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
486 }
487 curl_easy_initialized = true;
488
489 /* register cleanup function to shut down libcurl properly */
490 atexit (cleanup);
491
492 if (verbose >= 1)
493 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE");
494
495 /* print everything on stdout like check_http would do */
496 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR");
497
498 if (automatic_decompression)
499#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
500 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING");
501#else
502 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
503#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
504
505 /* initialize buffer for body of the answer */
506 if (curlhelp_initwritebuffer(&body_buf) < 0)
507 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
508 body_buf_initialized = true;
509 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION");
510 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA");
511
512 /* initialize buffer for header of the answer */
513 if (curlhelp_initwritebuffer( &header_buf ) < 0)
514 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n" );
515 header_buf_initialized = true;
516 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION");
517 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER");
518
519 /* set the error buffer */
520 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER");
521
522 /* set timeouts */
523 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT");
524 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT");
525
526 /* enable haproxy protocol */
527 if (haproxy_protocol) {
528 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL");
529 }
530
531 // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we use the host_name later on to make SNI happy
532 if(use_ssl && host_name != NULL) {
533 if ( (res=lookup_host (server_address, addrstr, DEFAULT_BUFFER_SIZE/2)) != 0) {
534 snprintf (msg,
535 DEFAULT_BUFFER_SIZE,
536 _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"),
537 server_address,
538 res,
539 gai_strerror (res));
540 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
541 }
542 snprintf (dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr);
543 host = curl_slist_append(NULL, dnscache);
544 curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
545 if (verbose>=1)
546 printf ("* curl CURLOPT_RESOLVE: %s\n", dnscache);
547 }
548
549 // If server_address is an IPv6 address it must be surround by square brackets
550 struct in6_addr tmp_in_addr;
551 if (inet_pton(AF_INET6, server_address, &tmp_in_addr) == 1) {
552 char *new_server_address = malloc(strlen(server_address) + 3);
553 if (new_server_address == NULL) {
554 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
555 }
556 snprintf(new_server_address, strlen(server_address)+3, "[%s]", server_address);
557 free(server_address);
558 server_address = new_server_address;
559 }
560
561 /* compose URL: use the address we want to connect to, set Host: header later */
562 snprintf (url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s",
563 use_ssl ? "https" : "http",
564 ( use_ssl & ( host_name != NULL ) ) ? host_name : server_address,
565 server_port,
566 server_url
567 );
568
569 if (verbose>=1)
570 printf ("* curl CURLOPT_URL: %s\n", url);
571 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, url), "CURLOPT_URL");
572
573 /* extract proxy information for legacy proxy https requests */
574 if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) {
575 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY");
576 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT");
577 if (verbose>=2)
578 printf ("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port);
579 http_method = "GET";
580 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, server_url), "CURLOPT_URL");
581 }
582
583 /* disable body for HEAD request */
584 if (http_method && !strcmp (http_method, "HEAD" )) {
585 no_body = true;
586 }
587
588 /* set HTTP protocol version */
589 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION");
590
591 /* set HTTP method */
592 if (http_method) {
593 if (!strcmp(http_method, "POST"))
594 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POST, 1), "CURLOPT_POST");
595 else if (!strcmp(http_method, "PUT"))
596 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
597 else
598 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST");
599 }
600
601 /* check if Host header is explicitly set in options */
602 if (http_opt_headers_count) {
603 for (i = 0; i < http_opt_headers_count ; i++) {
604 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
605 force_host_header = http_opt_headers[i];
606 }
607 }
608 }
609
610 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */
611 if(host_name != NULL && force_host_header == NULL) {
612 if((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) {
613 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port);
614 } else {
615 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name);
616 }
617 header_list = curl_slist_append (header_list, http_header);
618 }
619
620 /* always close connection, be nice to servers */
621 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
622 header_list = curl_slist_append (header_list, http_header);
623
624 /* attach additional headers supplied by the user */
625 /* optionally send any other header tag */
626 if (http_opt_headers_count) {
627 for (i = 0; i < http_opt_headers_count ; i++) {
628 header_list = curl_slist_append (header_list, http_opt_headers[i]);
629 }
630 /* This cannot be free'd here because a redirection will then try to access this and segfault */
631 /* Covered in a testcase in tests/check_http.t */
632 /* free(http_opt_headers); */
633 }
634
635 /* set HTTP headers */
636 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ), "CURLOPT_HTTPHEADER");
637 206
638#ifdef LIBCURL_FEATURE_SSL 207 // =======================
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);
639 213
640 /* set SSL version, warn about insecure or unsupported versions */ 214 check_curl_global_state curl_state = conf_curl_struct.curl_state;
641 if (use_ssl) { 215 workingState = conf_curl_struct.working_state;
642 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION");
643 }
644
645 /* client certificate and key to present to server (SSL) */
646 if (client_cert)
647 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT");
648 if (client_privkey)
649 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
650 if (ca_cert) {
651 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
652 }
653 if (ca_cert || verify_peer_and_host) {
654 /* per default if we have a CA verify both the peer and the
655 * hostname in the certificate, can be switched off later */
656 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
657 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
658 } else {
659 /* backward-compatible behaviour, be tolerant in checks
660 * TODO: depending on more options have aspects we want
661 * to be less tolerant about ssl verfications
662 */
663 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER");
664 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST");
665 }
666
667 /* detect SSL library used by libcurl */
668 ssl_library = curlhelp_get_ssl_library ();
669
670 /* try hard to get a stack of certificates to verify against */
671 if (check_cert) {
672#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
673 /* inform curl to report back certificates */
674 switch (ssl_library) {
675 case CURLHELP_SSL_LIBRARY_OPENSSL:
676 case CURLHELP_SSL_LIBRARY_LIBRESSL:
677 /* set callback to extract certificate with OpenSSL context function (works with
678 * OpenSSL-style libraries only!) */
679#ifdef USE_OPENSSL
680 /* libcurl and monitoring plugins built with OpenSSL, good */
681 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
682 is_openssl_callback = true;
683#else /* USE_OPENSSL */
684#endif /* USE_OPENSSL */
685 /* libcurl is built with OpenSSL, monitoring plugins, so falling
686 * back to manually extracting certificate information */
687 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
688 break;
689
690 case CURLHELP_SSL_LIBRARY_NSS:
691#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
692 /* NSS: support for CERTINFO is implemented since 7.34.0 */
693 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
694#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
695 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library));
696#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
697 break;
698
699 case CURLHELP_SSL_LIBRARY_GNUTLS:
700#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
701 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
702 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
703#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
704 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library));
705#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
706 break;
707
708 case CURLHELP_SSL_LIBRARY_UNKNOWN:
709 default:
710 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", curlhelp_get_ssl_library_string (ssl_library));
711 break;
712 }
713#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
714 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
715 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL)
716 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
717 else
718 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl too old and has no CURLOPT_CERTINFO)\n");
719#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
720 }
721 216
722#endif /* LIBCURL_FEATURE_SSL */ 217 mp_subcheck sc_result = mp_subcheck_init();
723 218
724 /* set default or user-given user agent identification */ 219 char *url = fmt_url(workingState);
725 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT"); 220 xasprintf(&sc_result.output, "Testing %s", url);
726 221 // TODO add some output here URL or something
727 /* proxy-authentication */ 222 free(url);
728 if (strcmp(proxy_auth, ""))
729 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD");
730
731 /* authentication */
732 if (strcmp(user_auth, ""))
733 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD");
734
735 /* TODO: parameter auth method, bitfield of following methods:
736 * CURLAUTH_BASIC (default)
737 * CURLAUTH_DIGEST
738 * CURLAUTH_DIGEST_IE
739 * CURLAUTH_NEGOTIATE
740 * CURLAUTH_NTLM
741 * CURLAUTH_NTLM_WB
742 *
743 * convenience tokens for typical sets of methods:
744 * CURLAUTH_ANYSAFE: most secure, without BASIC
745 * or CURLAUTH_ANY: most secure, even BASIC if necessary
746 *
747 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
748 */
749
750 /* handle redirections */
751 if (onredirect == STATE_DEPENDENT) {
752 if( followmethod == FOLLOW_LIBCURL ) {
753 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
754
755 /* default -1 is infinite, not good, could lead to zombie plugins!
756 Setting it to one bigger than maximal limit to handle errors nicely below
757 */
758 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_MAXREDIRS, max_depth+1), "CURLOPT_MAXREDIRS");
759
760 /* for now allow only http and https (we are a http(s) check plugin in the end) */
761#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
762 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), "CURLOPT_REDIR_PROTOCOLS_STR");
763#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
764 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), "CURLOPT_REDIRECT_PROTOCOLS");
765#endif
766 223
767 /* TODO: handle the following aspects of redirection, make them 224 // ==============
768 * command line options too later: 225 // do the request
769 CURLOPT_POSTREDIR: method switch 226 // ==============
770 CURLINFO_REDIRECT_URL: custom redirect option 227 CURLcode res = curl_easy_perform(curl_state.curl);
771 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
772 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size?
773 */
774 } else {
775 /* old style redirection is handled below */
776 }
777 }
778
779 /* no-body */
780 if (no_body)
781 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY");
782
783 /* IPv4 or IPv6 forced DNS resolution */
784 if (address_family == AF_UNSPEC)
785 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
786 else if (address_family == AF_INET)
787 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
788#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
789 else if (address_family == AF_INET6)
790 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
791#endif
792
793 /* either send http POST data (any data, not only POST)*/
794 if (!strcmp(http_method, "POST") ||!strcmp(http_method, "PUT")) {
795 /* set content of payload for POST and PUT */
796 if (http_content_type) {
797 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type);
798 header_list = curl_slist_append (header_list, http_header);
799 }
800 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
801 * in case of no POST/PUT data */
802 if (!http_post_data)
803 http_post_data = "";
804 if (!strcmp(http_method, "POST")) {
805 /* POST method, set payload with CURLOPT_POSTFIELDS */
806 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS");
807 } else if (!strcmp(http_method, "PUT")) {
808 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION");
809 if (curlhelp_initreadbuffer (&put_buf, http_post_data, strlen (http_post_data)) < 0)
810 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n");
811 put_buf_initialized = true;
812 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA");
813 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_INFILESIZE, (curl_off_t)strlen (http_post_data)), "CURLOPT_INFILESIZE");
814 }
815 }
816
817 /* cookie handling */
818 if (cookie_jar_file != NULL) {
819 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR");
820 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE");
821 }
822
823 /* do the request */
824 res = curl_easy_perform(curl);
825
826 if (verbose>=2 && http_post_data)
827 printf ("**** REQUEST CONTENT ****\n%s\n", http_post_data);
828
829 /* free header and server IP resolve lists, we don't need it anymore */
830 curl_slist_free_all (header_list); header_list = NULL;
831 curl_slist_free_all (server_ips); server_ips = NULL;
832 if (host) {
833 curl_slist_free_all (host); host = NULL;
834 }
835
836 /* Curl errors, result in critical Nagios state */
837 if (res != CURLE_OK) {
838 snprintf (msg,
839 DEFAULT_BUFFER_SIZE,
840 _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"),
841 server_port,
842 res,
843 errbuf[0] ? errbuf : curl_easy_strerror(res));
844 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
845 }
846
847 /* certificate checks */
848#ifdef LIBCURL_FEATURE_SSL
849 if (use_ssl) {
850 if (check_cert) {
851 if (is_openssl_callback) {
852#ifdef USE_OPENSSL
853 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
854 * and we actually have OpenSSL in the monitoring tools
855 */
856 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
857 if (!continue_after_check_cert) {
858 return result_ssl;
859 }
860#else /* USE_OPENSSL */
861 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n");
862#endif /* USE_OPENSSL */
863 } else {
864 int i;
865 struct curl_slist *slist;
866
867 cert_ptr.to_info = NULL;
868 res = curl_easy_getinfo (curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
869 if (!res && cert_ptr.to_info) {
870#ifdef USE_OPENSSL
871 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing
872 * We only check the first certificate and assume it's the one of the server
873 */
874 const char* raw_cert = NULL;
875 for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
876 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
877 if (verbose >= 2)
878 printf ("%d ** %s\n", i, slist->data);
879 if (strncmp (slist->data, "Cert:", 5) == 0) {
880 raw_cert = &slist->data[5];
881 goto GOT_FIRST_CERT;
882 }
883 }
884 }
885GOT_FIRST_CERT:
886 if (!raw_cert) {
887 snprintf (msg,
888 DEFAULT_BUFFER_SIZE,
889 _("Cannot retrieve certificates from CERTINFO information - certificate data was empty"));
890 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
891 }
892 BIO* cert_BIO = BIO_new (BIO_s_mem());
893 BIO_write (cert_BIO, raw_cert, strlen(raw_cert));
894 cert = PEM_read_bio_X509 (cert_BIO, NULL, NULL, NULL);
895 if (!cert) {
896 snprintf (msg,
897 DEFAULT_BUFFER_SIZE,
898 _("Cannot read certificate from CERTINFO information - BIO error"));
899 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
900 }
901 BIO_free (cert_BIO);
902 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
903 if (!continue_after_check_cert) {
904 return result_ssl;
905 }
906#else /* USE_OPENSSL */
907 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal,
908 * so we use the libcurl CURLINFO data
909 */
910 result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit);
911 if (!continue_after_check_cert) {
912 return result_ssl;
913 }
914#endif /* USE_OPENSSL */
915 } else {
916 snprintf (msg,
917 DEFAULT_BUFFER_SIZE,
918 _("Cannot retrieve certificates - cURL returned %d - %s"),
919 res,
920 curl_easy_strerror(res));
921 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
922 }
923 }
924 }
925 }
926#endif /* LIBCURL_FEATURE_SSL */
927 228
928 /* we got the data and we executed the request in a given time, so we can append 229 if (verbose >= 2 && workingState.http_post_data) {
929 * performance data to the answer always 230 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data);
930 */ 231 }
931 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME");
932 page_len = get_content_length(&header_buf, &body_buf);
933 if(show_extended_perfdata) {
934 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME");
935 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME");
936 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME");
937 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME");
938 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s",
939 perfd_time(total_time),
940 perfd_size(page_len),
941 perfd_time_connect(time_connect),
942 use_ssl ? perfd_time_ssl (time_appconnect-time_connect) : "",
943 perfd_time_headers(time_headers - time_appconnect),
944 perfd_time_firstbyte(time_firstbyte - time_headers),
945 perfd_time_transfer(total_time-time_firstbyte)
946 );
947 } else {
948 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s",
949 perfd_time(total_time),
950 perfd_size(page_len)
951 );
952 }
953
954 /* return a CRITICAL status if we couldn't read any data */
955 if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0)
956 die (STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n"));
957
958 /* get status line of answer, check sanity of HTTP code */
959 if (curlhelp_parse_statusline (header_buf.buf, &status_line) < 0) {
960 snprintf (msg,
961 DEFAULT_BUFFER_SIZE,
962 "Unparsable status line in %.3g seconds response time|%s\n",
963 total_time,
964 perfstring);
965 /* we cannot know the major/minor version here for sure as we cannot parse the first line */
966 die (STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg);
967 }
968 status_line_initialized = true;
969
970 /* get result code from cURL */
971 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE");
972 if (verbose>=2)
973 printf ("* curl CURLINFO_RESPONSE_CODE is %ld\n", code);
974
975 /* print status line, header, body if verbose */
976 if (verbose >= 2) {
977 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf,
978 (no_body ? " [[ skipped ]]" : body_buf.buf));
979 }
980
981 /* make sure the status line matches the response we are looking for */
982 if (!expected_statuscode(status_line.first_line, server_expect)) {
983 if (server_port == HTTP_PORT)
984 snprintf(msg,
985 DEFAULT_BUFFER_SIZE,
986 _("Invalid HTTP response received from host: %s\n"),
987 status_line.first_line);
988 else
989 snprintf(msg,
990 DEFAULT_BUFFER_SIZE,
991 _("Invalid HTTP response received from host on port %d: %s\n"),
992 server_port,
993 status_line.first_line);
994 die (STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg,
995 show_body ? "\n" : "",
996 show_body ? body_buf.buf : "");
997 }
998
999 if( server_expect_yn ) {
1000 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect);
1001 if (verbose)
1002 printf ("%s\n",msg);
1003 result = STATE_OK;
1004 }
1005 else {
1006 /* illegal return codes result in a critical state */
1007 if (code >= 600 || code < 100) {
1008 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg);
1009 /* server errors result in a critical state */
1010 } else if (code >= 500) {
1011 result = STATE_CRITICAL;
1012 /* client errors result in a warning state */
1013 } else if (code >= 400) {
1014 result = STATE_WARNING;
1015 /* check redirected page if specified */
1016 } else if (code >= 300) {
1017 if (onredirect == STATE_DEPENDENT) {
1018 if( followmethod == FOLLOW_LIBCURL ) {
1019 code = status_line.http_code;
1020 } else {
1021 /* old check_http style redirection, if we come
1022 * back here, we are in the same status as with
1023 * the libcurl method
1024 */
1025 redir (&header_buf);
1026 }
1027 } else {
1028 /* this is a specific code in the command line to
1029 * be returned when a redirection is encountered
1030 */
1031 }
1032 result = max_state_alt (onredirect, result);
1033 /* all other codes are considered ok */
1034 } else {
1035 result = STATE_OK;
1036 }
1037 }
1038
1039 /* libcurl redirection internally, handle error states here */
1040 if( followmethod == FOLLOW_LIBCURL ) {
1041 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT");
1042 if (verbose >= 2)
1043 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
1044 if (redir_depth > max_depth) {
1045 snprintf (msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl",
1046 max_depth);
1047 die (STATE_WARNING, "HTTP WARNING - %s", msg);
1048 }
1049 }
1050
1051 /* check status codes, set exit status accordingly */
1052 if( status_line.http_code != code ) {
1053 die (STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"),
1054 string_statuscode (status_line.http_major, status_line.http_minor),
1055 status_line.http_code, status_line.msg, code);
1056 }
1057
1058 if (maximum_age >= 0) {
1059 result = max_state_alt(check_document_dates(&header_buf, &msg), result);
1060 }
1061
1062 /* Page and Header content checks go here */
1063
1064 if (strlen (header_expect)) {
1065 if (!strstr (header_buf.buf, header_expect)) {
1066
1067 strncpy(&output_header_search[0],header_expect,sizeof(output_header_search));
1068
1069 if(output_header_search[sizeof(output_header_search)-1]!='\0') {
1070 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4);
1071 }
1072
1073 char tmp[DEFAULT_BUFFER_SIZE];
1074
1075 snprintf (tmp,
1076 DEFAULT_BUFFER_SIZE,
1077 _("%sheader '%s' not found on '%s://%s:%d%s', "),
1078 msg,
1079 output_header_search,
1080 use_ssl ? "https" : "http",
1081 host_name ? host_name : server_address,
1082 server_port,
1083 server_url);
1084
1085 strcpy(msg, tmp);
1086
1087 result = STATE_CRITICAL;
1088 }
1089 }
1090
1091 if (strlen (string_expect)) {
1092 if (!strstr (body_buf.buf, string_expect)) {
1093
1094 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
1095
1096 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
1097 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
1098 }
1099
1100 char tmp[DEFAULT_BUFFER_SIZE];
1101
1102 snprintf (tmp,
1103 DEFAULT_BUFFER_SIZE,
1104 _("%sstring '%s' not found on '%s://%s:%d%s', "),
1105 msg,
1106 output_string_search,
1107 use_ssl ? "https" : "http",
1108 host_name ? host_name : server_address,
1109 server_port,
1110 server_url);
1111
1112 strcpy(msg, tmp);
1113
1114 result = STATE_CRITICAL;
1115 }
1116 }
1117
1118 if (strlen (regexp)) {
1119 errcode = regexec (&preg, body_buf.buf, REGS, pmatch, 0);
1120 if ((errcode == 0 && !invert_regex) || (errcode == REG_NOMATCH && invert_regex)) {
1121 /* OK - No-op to avoid changing the logic around it */
1122 result = max_state_alt(STATE_OK, result);
1123 }
1124 else if ((errcode == REG_NOMATCH && !invert_regex) || (errcode == 0 && invert_regex)) {
1125 if (!invert_regex) {
1126 char tmp[DEFAULT_BUFFER_SIZE];
1127
1128 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg);
1129 strcpy(msg, tmp);
1130 232
1131 } else { 233 mp_subcheck sc_curl = mp_subcheck_init();
1132 char tmp[DEFAULT_BUFFER_SIZE];
1133 234
1134 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); 235 /* Curl errors, result in critical Nagios state */
1135 strcpy(msg, tmp); 236 if (res != CURLE_OK) {
237 xasprintf(&sc_curl.output, _("Error while performing connection: cURL returned %d - %s"),
238 res, errbuf[0] ? errbuf : curl_easy_strerror(res));
239 sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL);
240 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
241 return sc_result;
242 }
1136 243
1137 } 244 /* get status line of answer, check sanity of HTTP code */
1138 result = state_regex; 245 if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) {
1139 } else { 246 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
1140 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 247 /* we cannot know the major/minor version here for sure as we cannot parse the first
248 * line */
249 xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line");
250 return sc_result;
251 }
1141 252
1142 char tmp[DEFAULT_BUFFER_SIZE]; 253 curl_state.status_line_initialized = true;
1143 254
1144 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf); 255 size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf);
1145 strcpy(msg, tmp);
1146 result = STATE_UNKNOWN;
1147 }
1148 }
1149 256
1150 /* make sure the page is of an appropriate size */ 257 double total_time;
1151 if ((max_page_len > 0) && (page_len > max_page_len)) { 258 handle_curl_option_return_code(
1152 char tmp[DEFAULT_BUFFER_SIZE]; 259 curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time),
260 "CURLINFO_TOTAL_TIME");
1153 261
1154 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); 262 xasprintf(
263 &sc_curl.output, "%s %d %s - %ld bytes in %.3f second response time",
264 string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor),
265 curl_state.status_line->http_code, curl_state.status_line->msg, page_len, total_time);
266 sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK);
267 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
1155 268
1156 strcpy(msg, tmp); 269 // ==========
270 // Evaluation
271 // ==========
1157 272
1158 result = max_state_alt(STATE_WARNING, result); 273#ifdef LIBCURL_FEATURE_SSL
1159 274 if (workingState.use_ssl && config.check_cert) {
1160 } else if ((min_page_len > 0) && (page_len < min_page_len)) { 275 mp_subcheck sc_certificate = check_curl_certificate_checks(
1161 char tmp[DEFAULT_BUFFER_SIZE]; 276 curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit);
1162 277
1163 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); 278 mp_add_subcheck_to_subcheck(&sc_result, sc_certificate);
1164 strcpy(msg, tmp); 279 if (!config.continue_after_check_cert) {
1165 result = max_state_alt(STATE_WARNING, result); 280 return sc_result;
281 }
1166 } 282 }
283#endif
1167 284
1168 /* -w, -c: check warning and critical level */ 285 /* we got the data and we executed the request in a given time, so we can append
1169 result = max_state_alt(get_status(total_time, thlds), result); 286 * performance data to the answer always
1170 287 */
1171 /* Cut-off trailing characters */ 288
1172 if (strlen(msg) >= 2) { 289 // total time the query took
1173 if(msg[strlen(msg)-2] == ',') 290 mp_perfdata pd_total_time = perfdata_init();
1174 msg[strlen(msg)-2] = '\0'; 291 mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time);
1175 else 292 pd_total_time.value = pd_val_total_time;
1176 msg[strlen(msg)-3] = '\0'; 293 pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds);
1177 } 294 pd_total_time.label = "time";
1178 295 pd_total_time.uom = "s";
1179 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ 296
1180 die (max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", 297 mp_subcheck sc_total_time = mp_subcheck_init();
1181 state_text(result), string_statuscode (status_line.http_major, status_line.http_minor), 298 sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time));
1182 status_line.http_code, status_line.msg, 299 xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time);
1183 strlen(msg) > 0 ? " - " : "", 300 mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time);
1184 msg, page_len, total_time, 301
1185 (display_html ? "</A>" : ""), 302 mp_add_subcheck_to_subcheck(&sc_result, sc_total_time);
1186 perfstring, 303
1187 (show_body ? body_buf.buf : ""), 304 if (config.show_extended_perfdata) {
1188 (show_body ? "\n" : "") ); 305 // overall connection time
1189 306 mp_perfdata pd_time_connect = perfdata_init();
1190 return max_state_alt(result, result_ssl); 307 double time_connect;
1191} 308 handle_curl_option_return_code(
1192 309 curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect),
1193int 310 "CURLINFO_CONNECT_TIME");
1194uri_strcmp (const UriTextRangeA range, const char* s) 311
1195{ 312 mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect);
1196 if (!range.first) return -1; 313 pd_time_connect.value = pd_val_time_connect;
1197 if ( (size_t)(range.afterLast - range.first) < strlen (s) ) return -1; 314 pd_time_connect.label = "time_connect";
1198 return strncmp (s, range.first, min( (size_t)(range.afterLast - range.first), strlen (s))); 315 pd_time_connect.uom = "s";
1199} 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);
339 }
1200 340
1201char* 341 mp_perfdata pd_time_headers = perfdata_init();
1202uri_string (const UriTextRangeA range, char* buf, size_t buflen) 342 {
1203{ 343 double time_headers;
1204 if (!range.first) return "(null)"; 344 handle_curl_option_return_code(
1205 strncpy (buf, range.first, max (buflen-1, (size_t)(range.afterLast - range.first))); 345 curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers),
1206 buf[max (buflen-1, (size_t)(range.afterLast - range.first))] = '\0'; 346 "CURLINFO_PRETRANSFER_TIME");
1207 buf[range.afterLast - range.first] = '\0';
1208 return buf;
1209}
1210 347
1211void 348 mp_perfdata_value pd_val_time_headers =
1212redir (curlhelp_write_curlbuf* header_buf) 349 mp_create_pd_value(time_headers - time_appconnect);
1213{
1214 char *location = NULL;
1215 curlhelp_statusline status_line;
1216 struct phr_header headers[255];
1217 size_t nof_headers = 255;
1218 size_t msglen;
1219 char buf[DEFAULT_BUFFER_SIZE];
1220 char ipstr[INET_ADDR_MAX_SIZE];
1221 int new_port;
1222 char *new_host;
1223 char *new_url;
1224
1225 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
1226 &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
1227 headers, &nof_headers, 0);
1228 350
1229 if (res == -1) { 351 pd_time_headers.value = pd_val_time_headers;
1230 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); 352 }
353 pd_time_headers.label = "time_headers";
354 pd_time_headers.uom = "s";
355 mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers);
356
357 mp_perfdata pd_time_firstbyte = perfdata_init();
358 double time_firstbyte;
359 handle_curl_option_return_code(
360 curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
361 "CURLINFO_STARTTRANSFER_TIME");
362
363 mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte);
364 pd_time_firstbyte.value = pd_val_time_firstbyte;
365 pd_time_firstbyte.label = "time_firstbyte";
366 pd_time_firstbyte.uom = "s";
367 mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte);
368
369 mp_perfdata pd_time_transfer = perfdata_init();
370 pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte);
371 pd_time_transfer.label = "time_transfer";
372 pd_time_transfer.uom = "s";
373 mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer);
1231 } 374 }
1232 375
1233 location = get_header_value (headers, nof_headers, "location"); 376 /* return a CRITICAL status if we couldn't read any data */
1234 377 if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) {
1235 if (verbose >= 2) 378 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
1236 printf(_("* Seen redirect location %s\n"), location); 379 xasprintf(&sc_result.output, "No header received from host");
1237 380 return sc_result;
1238 if (++redir_depth > max_depth)
1239 die (STATE_WARNING,
1240 _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"),
1241 max_depth, location, (display_html ? "</A>" : ""));
1242
1243 UriParserStateA state;
1244 UriUriA uri;
1245 state.uri = &uri;
1246 if (uriParseUriA (&state, location) != URI_SUCCESS) {
1247 if (state.errorCode == URI_ERROR_SYNTAX) {
1248 die (STATE_UNKNOWN,
1249 _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"),
1250 location, (display_html ? "</A>" : ""));
1251 } else if (state.errorCode == URI_ERROR_MALLOC) {
1252 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1253 }
1254 }
1255
1256 if (verbose >= 2) {
1257 printf (_("** scheme: %s\n"),
1258 uri_string (uri.scheme, buf, DEFAULT_BUFFER_SIZE));
1259 printf (_("** host: %s\n"),
1260 uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1261 printf (_("** port: %s\n"),
1262 uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
1263 if (uri.hostData.ip4) {
1264 inet_ntop (AF_INET, uri.hostData.ip4->data, ipstr, sizeof (ipstr));
1265 printf (_("** IPv4: %s\n"), ipstr);
1266 }
1267 if (uri.hostData.ip6) {
1268 inet_ntop (AF_INET, uri.hostData.ip6->data, ipstr, sizeof (ipstr));
1269 printf (_("** IPv6: %s\n"), ipstr);
1270 }
1271 if (uri.pathHead) {
1272 printf (_("** path: "));
1273 const UriPathSegmentA* p = uri.pathHead;
1274 for (; p; p = p->next) {
1275 printf ("/%s", uri_string (p->text, buf, DEFAULT_BUFFER_SIZE));
1276 }
1277 puts ("");
1278 }
1279 if (uri.query.first) {
1280 printf (_("** query: %s\n"),
1281 uri_string (uri.query, buf, DEFAULT_BUFFER_SIZE));
1282 }
1283 if (uri.fragment.first) {
1284 printf (_("** fragment: %s\n"),
1285 uri_string (uri.fragment, buf, DEFAULT_BUFFER_SIZE));
1286 }
1287 }
1288
1289 if (uri.scheme.first) {
1290 if (!uri_strcmp (uri.scheme, "https"))
1291 use_ssl = true;
1292 else
1293 use_ssl = false;
1294 }
1295
1296 /* we do a sloppy test here only, because uriparser would have failed
1297 * above, if the port would be invalid, we just check for MAX_PORT
1298 */
1299 if (uri.portText.first) {
1300 new_port = atoi (uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
1301 } else {
1302 new_port = HTTP_PORT;
1303 if (use_ssl)
1304 new_port = HTTPS_PORT;
1305 }
1306 if (new_port > MAX_PORT)
1307 die (STATE_UNKNOWN,
1308 _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"),
1309 MAX_PORT, location, display_html ? "</A>" : "");
1310
1311 /* by RFC 7231 relative URLs in Location should be taken relative to
1312 * the original URL, so we try to form a new absolute URL here
1313 */
1314 if (!uri.scheme.first && !uri.hostText.first) {
1315 new_host = strdup (host_name ? host_name : server_address);
1316 new_port = server_port;
1317 if(use_ssl)
1318 uri_string (uri.scheme, "https", DEFAULT_BUFFER_SIZE);
1319 } else {
1320 new_host = strdup (uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1321 }
1322
1323 /* compose new path */
1324 /* TODO: handle fragments and query part of URL */
1325 new_url = (char *)calloc( 1, DEFAULT_BUFFER_SIZE);
1326 if (uri.pathHead) {
1327 const UriPathSegmentA* p = uri.pathHead;
1328 for (; p; p = p->next) {
1329 strncat (new_url, "/", DEFAULT_BUFFER_SIZE);
1330 strncat (new_url, uri_string (p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE-1);
1331 }
1332 }
1333
1334 if (server_port==new_port &&
1335 !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
1336 (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
1337 !strcmp(server_url, new_url))
1338 die (STATE_CRITICAL,
1339 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1340 use_ssl ? "https" : "http", new_host, new_port, new_url, (display_html ? "</A>" : ""));
1341
1342 /* set new values for redirected request */
1343
1344 if (!(followsticky & STICKY_HOST)) {
1345 free (server_address);
1346 server_address = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1347 }
1348 if (!(followsticky & STICKY_PORT)) {
1349 server_port = (unsigned short)new_port;
1350 }
1351
1352 free (host_name);
1353 host_name = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1354
1355 /* reset virtual port */
1356 virtual_port = server_port;
1357
1358 free(new_host);
1359 free (server_url);
1360 server_url = new_url;
1361
1362 uriFreeUriMembersA (&uri);
1363
1364 if (verbose)
1365 printf (_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http",
1366 host_name ? host_name : server_address, server_port, server_url);
1367
1368 /* TODO: the hash component MUST be taken from the original URL and
1369 * attached to the URL in Location
1370 */
1371
1372 cleanup ();
1373 check_http ();
1374}
1375
1376/* check whether a file exists */
1377void
1378test_file (char *path)
1379{
1380 if (access(path, R_OK) == 0)
1381 return;
1382 usage2 (_("file does not exist or is not readable"), path);
1383}
1384
1385bool
1386process_arguments (int argc, char **argv)
1387{
1388 char *p;
1389 int c = 1;
1390 char *temp;
1391
1392 enum {
1393 INVERT_REGEX = CHAR_MAX + 1,
1394 SNI_OPTION,
1395 MAX_REDIRS_OPTION,
1396 CONTINUE_AFTER_CHECK_CERT,
1397 CA_CERT_OPTION,
1398 HTTP_VERSION_OPTION,
1399 AUTOMATIC_DECOMPRESSION,
1400 COOKIE_JAR,
1401 HAPROXY_PROTOCOL,
1402 STATE_REGEX
1403 };
1404
1405 int option = 0;
1406 int got_plus = 0;
1407 static struct option longopts[] = {
1408 STD_LONG_OPTS,
1409 {"link", no_argument, 0, 'L'},
1410 {"nohtml", no_argument, 0, 'n'},
1411 {"ssl", optional_argument, 0, 'S'},
1412 {"sni", no_argument, 0, SNI_OPTION},
1413 {"post", required_argument, 0, 'P'},
1414 {"method", required_argument, 0, 'j'},
1415 {"IP-address", required_argument, 0, 'I'},
1416 {"url", required_argument, 0, 'u'},
1417 {"port", required_argument, 0, 'p'},
1418 {"authorization", required_argument, 0, 'a'},
1419 {"proxy-authorization", required_argument, 0, 'b'},
1420 {"header-string", required_argument, 0, 'd'},
1421 {"string", required_argument, 0, 's'},
1422 {"expect", required_argument, 0, 'e'},
1423 {"regex", required_argument, 0, 'r'},
1424 {"ereg", required_argument, 0, 'r'},
1425 {"eregi", required_argument, 0, 'R'},
1426 {"linespan", no_argument, 0, 'l'},
1427 {"onredirect", required_argument, 0, 'f'},
1428 {"certificate", required_argument, 0, 'C'},
1429 {"client-cert", required_argument, 0, 'J'},
1430 {"private-key", required_argument, 0, 'K'},
1431 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
1432 {"verify-cert", no_argument, 0, 'D'},
1433 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
1434 {"useragent", required_argument, 0, 'A'},
1435 {"header", required_argument, 0, 'k'},
1436 {"no-body", no_argument, 0, 'N'},
1437 {"max-age", required_argument, 0, 'M'},
1438 {"content-type", required_argument, 0, 'T'},
1439 {"pagesize", required_argument, 0, 'm'},
1440 {"invert-regex", no_argument, NULL, INVERT_REGEX},
1441 {"state-regex", required_argument, 0, STATE_REGEX},
1442 {"use-ipv4", no_argument, 0, '4'},
1443 {"use-ipv6", no_argument, 0, '6'},
1444 {"extended-perfdata", no_argument, 0, 'E'},
1445 {"show-body", no_argument, 0, 'B'},
1446 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
1447 {"http-version", required_argument, 0, HTTP_VERSION_OPTION},
1448 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION},
1449 {"cookie-jar", required_argument, 0, COOKIE_JAR},
1450 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL},
1451 {0, 0, 0, 0}
1452 };
1453
1454 if (argc < 2)
1455 return false;
1456
1457 /* support check_http compatible arguments */
1458 for (c = 1; c < argc; c++) {
1459 if (strcmp ("-to", argv[c]) == 0)
1460 strcpy (argv[c], "-t");
1461 if (strcmp ("-hn", argv[c]) == 0)
1462 strcpy (argv[c], "-H");
1463 if (strcmp ("-wt", argv[c]) == 0)
1464 strcpy (argv[c], "-w");
1465 if (strcmp ("-ct", argv[c]) == 0)
1466 strcpy (argv[c], "-c");
1467 if (strcmp ("-nohtml", argv[c]) == 0)
1468 strcpy (argv[c], "-n");
1469 }
1470
1471 server_url = strdup(DEFAULT_SERVER_URL);
1472
1473 while (1) {
1474 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option);
1475 if (c == -1 || c == EOF || c == 1)
1476 break;
1477
1478 switch (c) {
1479 case 'h':
1480 print_help();
1481 exit(STATE_UNKNOWN);
1482 break;
1483 case 'V':
1484 print_revision(progname, NP_VERSION);
1485 print_curl_version();
1486 exit(STATE_UNKNOWN);
1487 break;
1488 case 'v':
1489 verbose++;
1490 break;
1491 case 't': /* timeout period */
1492 if (!is_intnonneg (optarg))
1493 usage2 (_("Timeout interval must be a positive integer"), optarg);
1494 else
1495 socket_timeout = (int)strtol (optarg, NULL, 10);
1496 break;
1497 case 'c': /* critical time threshold */
1498 critical_thresholds = optarg;
1499 break;
1500 case 'w': /* warning time threshold */
1501 warning_thresholds = optarg;
1502 break;
1503 case 'H': /* virtual host */
1504 host_name = strdup (optarg);
1505 if (host_name[0] == '[') {
1506 if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */
1507 virtual_port = atoi (p + 2);
1508 /* cut off the port */
1509 host_name_length = strlen (host_name) - strlen (p) - 1;
1510 free (host_name);
1511 host_name = strndup (optarg, host_name_length);
1512 } 381 }
1513 } else if ((p = strchr (host_name, ':')) != NULL
1514 && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */
1515 virtual_port = atoi (p);
1516 /* cut off the port */
1517 host_name_length = strlen (host_name) - strlen (p) - 1;
1518 free (host_name);
1519 host_name = strndup (optarg, host_name_length);
1520 }
1521 break;
1522 case 'I': /* internet address */
1523 server_address = strdup (optarg);
1524 break;
1525 case 'u': /* URL path */
1526 server_url = strdup (optarg);
1527 break;
1528 case 'p': /* Server port */
1529 if (!is_intnonneg (optarg))
1530 usage2 (_("Invalid port number, expecting a non-negative number"), optarg);
1531 else {
1532 if( strtol(optarg, NULL, 10) > MAX_PORT)
1533 usage2 (_("Invalid port number, supplied port number is too big"), optarg);
1534 server_port = (unsigned short)strtol(optarg, NULL, 10);
1535 specify_port = true;
1536 }
1537 break;
1538 case 'a': /* authorization info */
1539 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
1540 user_auth[MAX_INPUT_BUFFER - 1] = 0;
1541 break;
1542 case 'b': /* proxy-authorization info */
1543 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1544 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1545 break;
1546 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1547 if (! http_post_data)
1548 http_post_data = strdup (optarg);
1549 if (! http_method)
1550 http_method = strdup("POST");
1551 break;
1552 case 'j': /* Set HTTP method */
1553 if (http_method)
1554 free(http_method);
1555 http_method = strdup (optarg);
1556 break;
1557 case 'A': /* useragent */
1558 strncpy (user_agent, optarg, DEFAULT_BUFFER_SIZE);
1559 user_agent[DEFAULT_BUFFER_SIZE-1] = '\0';
1560 break;
1561 case 'k': /* Additional headers */
1562 if (http_opt_headers_count == 0)
1563 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
1564 else
1565 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
1566 http_opt_headers[http_opt_headers_count - 1] = optarg;
1567 break;
1568 case 'L': /* show html link */
1569 display_html = true;
1570 break;
1571 case 'n': /* do not show html link */
1572 display_html = false;
1573 break;
1574 case 'C': /* Check SSL cert validity */
1575#ifdef LIBCURL_FEATURE_SSL
1576 if ((temp=strchr(optarg,','))!=NULL) {
1577 *temp='\0';
1578 if (!is_intnonneg (optarg))
1579 usage2 (_("Invalid certificate expiration period"), optarg);
1580 days_till_exp_warn = atoi(optarg);
1581 *temp=',';
1582 temp++;
1583 if (!is_intnonneg (temp))
1584 usage2 (_("Invalid certificate expiration period"), temp);
1585 days_till_exp_crit = atoi (temp);
1586 }
1587 else {
1588 days_till_exp_crit=0;
1589 if (!is_intnonneg (optarg))
1590 usage2 (_("Invalid certificate expiration period"), optarg);
1591 days_till_exp_warn = atoi (optarg);
1592 }
1593 check_cert = true;
1594 goto enable_ssl;
1595#endif
1596 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
1597#ifdef HAVE_SSL
1598 continue_after_check_cert = true;
1599 break;
1600#endif
1601 case 'J': /* use client certificate */
1602#ifdef LIBCURL_FEATURE_SSL
1603 test_file(optarg);
1604 client_cert = optarg;
1605 goto enable_ssl;
1606#endif
1607 case 'K': /* use client private key */
1608#ifdef LIBCURL_FEATURE_SSL
1609 test_file(optarg);
1610 client_privkey = optarg;
1611 goto enable_ssl;
1612#endif
1613#ifdef LIBCURL_FEATURE_SSL
1614 case CA_CERT_OPTION: /* use CA chain file */
1615 test_file(optarg);
1616 ca_cert = optarg;
1617 goto enable_ssl;
1618#endif
1619#ifdef LIBCURL_FEATURE_SSL
1620 case 'D': /* verify peer certificate & host */
1621 verify_peer_and_host = true;
1622 break;
1623#endif
1624 case 'S': /* use SSL */
1625#ifdef LIBCURL_FEATURE_SSL
1626 enable_ssl:
1627 use_ssl = true;
1628 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1629 * Only set if it's non-zero. This helps when we include multiple
1630 * parameters, like -S and -C combinations */
1631 ssl_version = CURL_SSLVERSION_DEFAULT;
1632 if (c=='S' && optarg != NULL) {
1633 char *plus_ptr = strchr(optarg, '+');
1634 if (plus_ptr) {
1635 got_plus = 1;
1636 *plus_ptr = '\0';
1637 }
1638
1639 if (optarg[0] == '2')
1640 ssl_version = CURL_SSLVERSION_SSLv2;
1641 else if (optarg[0] == '3')
1642 ssl_version = CURL_SSLVERSION_SSLv3;
1643 else if (!strcmp (optarg, "1") || !strcmp (optarg, "1.0"))
1644#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1645 ssl_version = CURL_SSLVERSION_TLSv1_0;
1646#else
1647 ssl_version = CURL_SSLVERSION_DEFAULT;
1648#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1649 else if (!strcmp (optarg, "1.1"))
1650#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1651 ssl_version = CURL_SSLVERSION_TLSv1_1;
1652#else
1653 ssl_version = CURL_SSLVERSION_DEFAULT;
1654#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1655 else if (!strcmp (optarg, "1.2"))
1656#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1657 ssl_version = CURL_SSLVERSION_TLSv1_2;
1658#else
1659 ssl_version = CURL_SSLVERSION_DEFAULT;
1660#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1661 else if (!strcmp (optarg, "1.3"))
1662#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1663 ssl_version = CURL_SSLVERSION_TLSv1_3;
1664#else
1665 ssl_version = CURL_SSLVERSION_DEFAULT;
1666#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1667 else
1668 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)"));
1669 }
1670#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1671 if (got_plus) {
1672 switch (ssl_version) {
1673 case CURL_SSLVERSION_TLSv1_3:
1674 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1675 break;
1676 case CURL_SSLVERSION_TLSv1_2:
1677 case CURL_SSLVERSION_TLSv1_1:
1678 case CURL_SSLVERSION_TLSv1_0:
1679 ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1680 break;
1681 }
1682 } else {
1683 switch (ssl_version) {
1684 case CURL_SSLVERSION_TLSv1_3:
1685 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1686 break;
1687 case CURL_SSLVERSION_TLSv1_2:
1688 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1689 break;
1690 case CURL_SSLVERSION_TLSv1_1:
1691 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1692 break;
1693 case CURL_SSLVERSION_TLSv1_0:
1694 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1695 break;
1696 }
1697 }
1698#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1699 if (verbose >= 2)
1700 printf(_("* Set SSL/TLS version to %d\n"), ssl_version);
1701 if (!specify_port)
1702 server_port = HTTPS_PORT;
1703 break;
1704#else /* LIBCURL_FEATURE_SSL */
1705 /* -C -J and -K fall through to here without SSL */
1706 usage4 (_("Invalid option - SSL is not available"));
1707 break;
1708 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */
1709 use_sni = true;
1710 break;
1711#endif /* LIBCURL_FEATURE_SSL */
1712 case MAX_REDIRS_OPTION:
1713 if (!is_intnonneg (optarg))
1714 usage2 (_("Invalid max_redirs count"), optarg);
1715 else {
1716 max_depth = atoi (optarg);
1717 }
1718 break;
1719 case 'f': /* onredirect */
1720 if (!strcmp (optarg, "ok"))
1721 onredirect = STATE_OK;
1722 else if (!strcmp (optarg, "warning"))
1723 onredirect = STATE_WARNING;
1724 else if (!strcmp (optarg, "critical"))
1725 onredirect = STATE_CRITICAL;
1726 else if (!strcmp (optarg, "unknown"))
1727 onredirect = STATE_UNKNOWN;
1728 else if (!strcmp (optarg, "follow"))
1729 onredirect = STATE_DEPENDENT;
1730 else if (!strcmp (optarg, "stickyport"))
1731 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST|STICKY_PORT;
1732 else if (!strcmp (optarg, "sticky"))
1733 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST;
1734 else if (!strcmp (optarg, "follow"))
1735 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE;
1736 else if (!strcmp (optarg, "curl"))
1737 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL;
1738 else usage2 (_("Invalid onredirect option"), optarg);
1739 if (verbose >= 2)
1740 printf(_("* Following redirects set to %s\n"), state_text(onredirect));
1741 break;
1742 case 'd': /* string or substring */
1743 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
1744 header_expect[MAX_INPUT_BUFFER - 1] = 0;
1745 break;
1746 case 's': /* string or substring */
1747 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
1748 string_expect[MAX_INPUT_BUFFER - 1] = 0;
1749 break;
1750 case 'e': /* string or substring */
1751 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
1752 server_expect[MAX_INPUT_BUFFER - 1] = 0;
1753 server_expect_yn = 1;
1754 break;
1755 case 'T': /* Content-type */
1756 http_content_type = strdup (optarg);
1757 break;
1758 case 'l': /* linespan */
1759 cflags &= ~REG_NEWLINE;
1760 break;
1761 case 'R': /* regex */
1762 cflags |= REG_ICASE;
1763 // fall through
1764 case 'r': /* regex */
1765 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
1766 regexp[MAX_RE_SIZE - 1] = 0;
1767 errcode = regcomp (&preg, regexp, cflags);
1768 if (errcode != 0) {
1769 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1770 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
1771 return false;
1772 }
1773 break;
1774 case INVERT_REGEX:
1775 invert_regex = true;
1776 break;
1777 case STATE_REGEX:
1778 if (!strcmp (optarg, "critical"))
1779 state_regex = STATE_CRITICAL;
1780 else if (!strcmp (optarg, "warning"))
1781 state_regex = STATE_WARNING;
1782 else usage2 (_("Invalid state-regex option"), optarg);
1783 break;
1784 case '4':
1785 address_family = AF_INET;
1786 break;
1787 case '6':
1788#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
1789 address_family = AF_INET6;
1790#else
1791 usage4 (_("IPv6 support not available"));
1792#endif
1793 break;
1794 case 'm': /* min_page_length */
1795 {
1796 char *tmp;
1797 if (strchr(optarg, ':') != (char *)NULL) {
1798 /* range, so get two values, min:max */
1799 tmp = strtok(optarg, ":");
1800 if (tmp == NULL) {
1801 printf("Bad format: try \"-m min:max\"\n");
1802 exit (STATE_WARNING);
1803 } else
1804 min_page_len = atoi(tmp);
1805
1806 tmp = strtok(NULL, ":");
1807 if (tmp == NULL) {
1808 printf("Bad format: try \"-m min:max\"\n");
1809 exit (STATE_WARNING);
1810 } else
1811 max_page_len = atoi(tmp);
1812 } else
1813 min_page_len = atoi (optarg);
1814 break;
1815 }
1816 case 'N': /* no-body */
1817 no_body = true;
1818 break;
1819 case 'M': /* max-age */
1820 {
1821 int L = strlen(optarg);
1822 if (L && optarg[L-1] == 'm')
1823 maximum_age = atoi (optarg) * 60;
1824 else if (L && optarg[L-1] == 'h')
1825 maximum_age = atoi (optarg) * 60 * 60;
1826 else if (L && optarg[L-1] == 'd')
1827 maximum_age = atoi (optarg) * 60 * 60 * 24;
1828 else if (L && (optarg[L-1] == 's' ||
1829 isdigit (optarg[L-1])))
1830 maximum_age = atoi (optarg);
1831 else {
1832 fprintf (stderr, "unparsable max-age: %s\n", optarg);
1833 exit (STATE_WARNING);
1834 }
1835 if (verbose >= 2)
1836 printf ("* Maximal age of document set to %d seconds\n", maximum_age);
1837 }
1838 break;
1839 case 'E': /* show extended perfdata */
1840 show_extended_perfdata = true;
1841 break;
1842 case 'B': /* print body content after status line */
1843 show_body = true;
1844 break;
1845 case HTTP_VERSION_OPTION:
1846 curl_http_version = CURL_HTTP_VERSION_NONE;
1847 if (strcmp (optarg, "1.0") == 0) {
1848 curl_http_version = CURL_HTTP_VERSION_1_0;
1849 } else if (strcmp (optarg, "1.1") == 0) {
1850 curl_http_version = CURL_HTTP_VERSION_1_1;
1851 } else if ((strcmp (optarg, "2.0") == 0) || (strcmp (optarg, "2") == 0)) {
1852#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0)
1853 curl_http_version = CURL_HTTP_VERSION_2_0;
1854#else
1855 curl_http_version = CURL_HTTP_VERSION_NONE;
1856#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */
1857 } else {
1858 fprintf (stderr, "unknown http-version parameter: %s\n", optarg);
1859 exit (STATE_WARNING);
1860 }
1861 break;
1862 case AUTOMATIC_DECOMPRESSION:
1863 automatic_decompression = true;
1864 break;
1865 case COOKIE_JAR:
1866 cookie_jar_file = optarg;
1867 break;
1868 case HAPROXY_PROTOCOL:
1869 haproxy_protocol = true;
1870 break;
1871 case '?':
1872 /* print short usage statement if args not parsable */
1873 usage5 ();
1874 break;
1875 }
1876 }
1877
1878 c = optind;
1879
1880 if (server_address == NULL && c < argc)
1881 server_address = strdup (argv[c++]);
1882
1883 if (host_name == NULL && c < argc)
1884 host_name = strdup (argv[c++]);
1885
1886 if (server_address == NULL) {
1887 if (host_name == NULL)
1888 usage4 (_("You must specify a server address or host name"));
1889 else
1890 server_address = strdup (host_name);
1891 }
1892
1893 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
1894
1895 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
1896 socket_timeout = (int)thlds->critical->end + 1;
1897 if (verbose >= 2)
1898 printf ("* Socket timeout set to %ld seconds\n", socket_timeout);
1899
1900 if (http_method == NULL)
1901 http_method = strdup ("GET");
1902
1903 if (client_cert && !client_privkey)
1904 usage4 (_("If you use a client certificate you must also specify a private key file"));
1905
1906 if (virtual_port == 0)
1907 virtual_port = server_port;
1908 else {
1909 if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT))
1910 if(!specify_port)
1911 server_port = virtual_port;
1912 }
1913
1914 return true;
1915}
1916
1917char *perfd_time (double elapsed_time)
1918{
1919 return fperfdata ("time", elapsed_time, "s",
1920 thlds->warning?true:false, thlds->warning?thlds->warning->end:0,
1921 thlds->critical?true:false, thlds->critical?thlds->critical->end:0,
1922 true, 0, true, socket_timeout);
1923}
1924 382
1925char *perfd_time_connect (double elapsed_time_connect) 383 /* get result code from cURL */
1926{ 384 long httpReturnCode;
1927 return fperfdata ("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); 385 handle_curl_option_return_code(
1928} 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 }
1929 391
1930char *perfd_time_ssl (double elapsed_time_ssl) 392 /* print status line, header, body if verbose */
1931{ 393 if (verbose >= 2) {
1932 return fperfdata ("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); 394 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf->buf,
1933} 395 (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf->buf));
396 }
1934 397
1935char *perfd_time_headers (double elapsed_time_headers) 398 /* make sure the status line matches the response we are looking for */
1936{ 399 mp_subcheck sc_expect = mp_subcheck_init();
1937 return fperfdata ("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); 400 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK);
1938} 401 if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) {
402 if (workingState.serverPort == HTTP_PORT) {
403 xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"),
404 curl_state.status_line->first_line);
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);
414 }
415 mp_add_subcheck_to_subcheck(&sc_result, sc_expect);
416
417 if (!config.server_expect.is_present) {
418 /* illegal return codes result in a critical state */
419 mp_subcheck sc_return_code = mp_subcheck_init();
420 sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK);
421 xasprintf(&sc_return_code.output, "HTTP return code: %d",
422 curl_state.status_line->http_code);
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 }
1939 431
1940char *perfd_time_firstbyte (double elapsed_time_firstbyte) 432 // server errors result in a critical state
1941{ 433 if (httpReturnCode >= 500) {
1942 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); 434 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
1943} 435 /* client errors result in a warning state */
436 } else if (httpReturnCode >= 400) {
437 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING);
438 /* check redirected page if specified */
439 } else if (httpReturnCode >= 300) {
440 if (config.on_redirect_dependent) {
441 if (config.followmethod == FOLLOW_LIBCURL) {
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
464 } else {
465 /* old check_http style redirection, if we come
466 * back here, we are in the same status as with
467 * the libcurl method
468 */
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;
477 }
478 } else {
479 /* this is a specific code in the command line to
480 * be returned when a redirection is encountered
481 */
482 sc_return_code =
483 mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state);
484 }
485 } else {
486 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK);
487 }
1944 488
1945char *perfd_time_transfer (double elapsed_time_transfer) 489 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
1946{ 490 }
1947 return fperfdata ("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1948}
1949 491
1950char *perfd_size (int page_len) 492 /* check status codes, set exit status accordingly */
1951{ 493 if (curl_state.status_line->http_code != httpReturnCode) {
1952 return perfdata ("size", page_len, "B", 494 mp_subcheck sc_http_return_code_sanity = mp_subcheck_init();
1953 (min_page_len>0?true:false), min_page_len, 495 sc_http_return_code_sanity =
1954 (min_page_len>0?true:false), 0, 496 mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL);
1955 true, 0, false, 0); 497 xasprintf(&sc_http_return_code_sanity.output,
1956} 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;
505 }
1957 506
1958void 507 if (config.maximum_age >= 0) {
1959print_help (void) 508 mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age);
1960{ 509 mp_add_subcheck_to_subcheck(&sc_result, sc_max_age);
1961 print_revision (progname, NP_VERSION); 510 }
1962 511
1963 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 512 /* Page and Header content checks go here */
1964 printf (COPYRIGHT, copyright, email); 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);
1965 517
1966 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); 518 if (!strstr(curl_state.header_buf->buf, config.header_expect)) {
1967 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for")); 519 char output_header_search[30] = "";
1968 printf ("%s\n", _("strings and regular expressions, check connection times, and report on")); 520 strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search));
1969 printf ("%s\n", _("certificate expiration times."));
1970 printf ("\n");
1971 printf ("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1972 printf ("%s\n", _("as possible."));
1973 521
1974 printf ("\n\n"); 522 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
523 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
524 }
1975 525
1976 print_usage (); 526 xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "),
527 output_header_search, workingState.use_ssl ? "https" : "http",
528 workingState.host_name ? workingState.host_name : workingState.server_address,
529 workingState.serverPort, workingState.server_url);
1977 530
1978 printf (_("NOTE: One or both of -H and -I must be specified")); 531 sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL);
532 }
1979 533
1980 printf ("\n"); 534 mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect);
535 }
1981 536
1982 printf (UT_HELP_VRSN); 537 if (strlen(config.string_expect)) {
1983 printf (UT_EXTRA_OPTS); 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);
1984 541
1985 printf (" %s\n", "-H, --hostname=ADDRESS"); 542 if (!strstr(curl_state.body_buf->buf, config.string_expect)) {
1986 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)")); 543 char output_string_search[30] = "";
1987 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); 544 strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search));
1988 printf (" %s\n", "-I, --IP-address=ADDRESS");
1989 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1990 printf (" %s\n", "-p, --port=INTEGER");
1991 printf (" %s", _("Port number (default: "));
1992 printf ("%d)\n", HTTP_PORT);
1993 545
1994 printf (UT_IPv46); 546 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
547 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
548 }
1995 549
1996#ifdef LIBCURL_FEATURE_SSL 550 xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "),
1997 printf (" %s\n", "-S, --ssl=VERSION[+]"); 551 output_string_search, workingState.use_ssl ? "https" : "http",
1998 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); 552 workingState.host_name ? workingState.host_name : workingState.server_address,
1999 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); 553 workingState.serverPort, workingState.server_url);
2000 printf (" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted."));
2001 printf (" %s\n", _("Note: SSLv2 and SSLv3 are deprecated and are usually disabled in libcurl"));
2002 printf (" %s\n", "--sni");
2003 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
2004#if LIBCURL_VERSION_NUM >= 0x071801
2005 printf (" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
2006 printf (" %s\n", _(" SNI only really works since TLSv1.0"));
2007#else
2008 printf (" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
2009#endif
2010 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
2011 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443."));
2012 printf (" %s\n", _("A STATE_WARNING is returned if the certificate has a validity less than the"));
2013 printf (" %s\n", _("first agument's value. If there is a second argument and the certificate's"));
2014 printf (" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned."));
2015 printf (" %s\n", _("(When this option is used the URL is not checked by default. You can use"));
2016 printf (" %s\n", _(" --continue-after-certificate to override this behavior)"));
2017 printf (" %s\n", "--continue-after-certificate");
2018 printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check."));
2019 printf (" %s\n", _("Does nothing unless -C is used."));
2020 printf (" %s\n", "-J, --client-cert=FILE");
2021 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
2022 printf (" %s\n", _("to be used in establishing the SSL session"));
2023 printf (" %s\n", "-K, --private-key=FILE");
2024 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
2025 printf (" %s\n", _("matching the client certificate"));
2026 printf (" %s\n", "--ca-cert=FILE");
2027 printf (" %s\n", _("CA certificate file to verify peer against"));
2028 printf (" %s\n", "-D, --verify-cert");
2029 printf (" %s\n", _("Verify the peer's SSL certificate and hostname"));
2030#endif
2031 554
2032 printf (" %s\n", "-e, --expect=STRING"); 555 sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL);
2033 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); 556 }
2034 printf (" %s", _("the first (status) line of the server response (default: "));
2035 printf ("%s)\n", HTTP_EXPECT);
2036 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
2037 printf (" %s\n", "-d, --header-string=STRING");
2038 printf (" %s\n", _("String to expect in the response headers"));
2039 printf (" %s\n", "-s, --string=STRING");
2040 printf (" %s\n", _("String to expect in the content"));
2041 printf (" %s\n", "-u, --url=PATH");
2042 printf (" %s\n", _("URL to GET or POST (default: /)"));
2043 printf (" %s\n", "-P, --post=STRING");
2044 printf (" %s\n", _("URL decoded http POST data"));
2045 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
2046 printf (" %s\n", _("Set HTTP method."));
2047 printf (" %s\n", "-N, --no-body");
2048 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
2049 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
2050 printf (" %s\n", "-M, --max-age=SECONDS");
2051 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
2052 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
2053 printf (" %s\n", "-T, --content-type=STRING");
2054 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
2055 printf (" %s\n", "-l, --linespan");
2056 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
2057 printf (" %s\n", "-r, --regex, --ereg=STRING");
2058 printf (" %s\n", _("Search page for regex STRING"));
2059 printf (" %s\n", "-R, --eregi=STRING");
2060 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
2061 printf (" %s\n", "--invert-regex");
2062 printf (" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
2063 printf (" %s\n", _("can be changed with --state--regex)"));
2064 printf (" %s\n", "--regex-state=STATE");
2065 printf (" %s\n", _("Return STATE if regex is found, OK if not\n"));
2066 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
2067 printf (" %s\n", _("Username:password on sites with basic authentication"));
2068 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
2069 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
2070 printf (" %s\n", "-A, --useragent=STRING");
2071 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
2072 printf (" %s\n", "-k, --header=STRING");
2073 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
2074 printf (" %s\n", "-E, --extended-perfdata");
2075 printf (" %s\n", _("Print additional performance data"));
2076 printf (" %s\n", "-B, --show-body");
2077 printf (" %s\n", _("Print body content below status line"));
2078 printf (" %s\n", "-L, --link");
2079 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
2080 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
2081 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
2082 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
2083 printf (" %s\n", _("follow uses the old redirection algorithm of check_http."));
2084 printf (" %s\n", _("curl uses CURL_FOLLOWLOCATION built into libcurl."));
2085 printf (" %s\n", "--max-redirs=INTEGER");
2086 printf (" %s", _("Maximal number of redirects (default: "));
2087 printf ("%d)\n", DEFAULT_MAX_REDIRS);
2088 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
2089 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
2090 printf ("\n");
2091 printf (" %s\n", "--http-version=VERSION");
2092 printf (" %s\n", _("Connect via specific HTTP protocol."));
2093 printf (" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)"));
2094 printf (" %s\n", "--enable-automatic-decompression");
2095 printf (" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING)."));
2096 printf(" %s\n", "--haproxy-protocol");
2097 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL)."));
2098 printf (" %s\n", "--cookie-jar=FILE");
2099 printf (" %s\n", _("Store cookies in the cookie jar and send them out when requested."));
2100 printf ("\n");
2101
2102 printf (UT_WARN_CRIT);
2103
2104 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
2105
2106 printf (UT_VERBOSE);
2107
2108 printf ("\n");
2109 printf ("%s\n", _("Notes:"));
2110 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
2111 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
2112 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
2113 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
2114 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
2115 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
2116 557
2117#ifdef LIBCURL_FEATURE_SSL 558 mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect);
2118 printf ("\n"); 559 }
2119 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
2120 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
2121 printf (" %s\n", _("certificate is still valid for the specified number of days."));
2122 printf ("\n");
2123 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
2124 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
2125 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
2126 printf ("\n");
2127 printf ("%s\n", _("Examples:"));
2128 printf (" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
2129 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
2130 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
2131 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2132 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
2133 printf ("\n");
2134 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
2135 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
2136 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
2137 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
2138 printf (" %s\n\n", _("the certificate is expired."));
2139 printf ("\n");
2140 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
2141 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
2142 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
2143 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
2144 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
2145#endif
2146 560
2147 printf ("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); 561 if (strlen(config.regexp)) {
2148 printf (" %s\n", _("It is recommended to use an environment proxy like:")); 562 mp_subcheck sc_body_regex = mp_subcheck_init();
2149 printf (" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); 563 xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp);
2150 printf (" %s\n", _("legacy proxy requests in check_http style still work:")); 564 regmatch_t pmatch[REGS];
2151 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org"));
2152 565
2153#ifdef LIBCURL_FEATURE_SSL 566 int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0);
2154 printf ("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
2155 printf (" %s\n", _("It is recommended to use an environment proxy like:"));
2156 printf (" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
2157 printf (" %s\n", _("legacy proxy requests in check_http style still work:"));
2158 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
2159 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
2160 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
2161 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2162 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
2163 567
2164#endif 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);
572 } else {
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);
2165 578
2166 printf (UT_SUPPORT); 579 if (config.invert_regex) {
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);
583 }
584 } else {
585 // error in regexec
586 char error_buffer[DEFAULT_BUFFER_SIZE];
587 regerror(errcode, &config.compiled_regex, &error_buffer[0], DEFAULT_BUFFER_SIZE);
588 xasprintf(&sc_body_regex.output, "regexec error: %s", error_buffer);
589 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_UNKNOWN);
590 }
2167 591
2168} 592 mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex);
593 }
2169 594
595 // size a.k.a. page length
596 mp_perfdata pd_page_length = perfdata_init();
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;
604
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;
610
611 pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold);
612
613 mp_subcheck sc_page_length = mp_subcheck_init();
614
615 mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length);
616
617 mp_state_enum tmp_state = mp_get_pd_status(pd_page_length);
618 sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state);
619
620 switch (tmp_state) {
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 }
2170 631
632 mp_add_subcheck_to_subcheck(&sc_result, sc_page_length);
633 }
2171 634
2172void 635 return sc_result;
2173print_usage (void)
2174{
2175 printf ("%s\n", _("Usage:"));
2176 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
2177 printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>] [-D]\n");
2178 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
2179 printf (" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n");
2180 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
2181 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
2182 printf (" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
2183 printf (" [-T <content-type>] [-j method]\n");
2184 printf (" [--http-version=<version>] [--enable-automatic-decompression]\n");
2185 printf (" [--cookie-jar=<cookie jar file>\n");
2186 printf (" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n",progname);
2187 printf (" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
2188 printf ("\n");
2189#ifdef LIBCURL_FEATURE_SSL
2190 printf ("%s\n", _("In the first form, make an HTTP request."));
2191 printf ("%s\n\n", _("In the second form, connect to the server and check the TLS certificate."));
2192#endif
2193} 636}
2194 637
2195void 638int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) {
2196print_curl_version (void) 639 if (!range.first) {
2197{ 640 return -1;
2198 printf( "%s\n", curl_version()); 641 }
642 if ((size_t)(range.afterLast - range.first) < strlen(stringToCompare)) {
643 return -1;
644 }
645 return strncmp(stringToCompare, range.first,
646 min((size_t)(range.afterLast - range.first), strlen(stringToCompare)));
2199} 647}
2200 648
2201int 649char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) {
2202curlhelp_initwritebuffer (curlhelp_write_curlbuf *buf) 650 if (!range.first) {
2203{ 651 return "(null)";
2204 buf->bufsize = DEFAULT_BUFFER_SIZE; 652 }
2205 buf->buflen = 0; 653 strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first)));
2206 buf->buf = (char *)malloc ((size_t)buf->bufsize); 654 buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0';
2207 if (buf->buf == NULL) return -1; 655 buf[range.afterLast - range.first] = '\0';
2208 return 0; 656 return buf;
2209} 657}
2210 658
2211size_t curlhelp_buffer_write_callback (void *buffer, size_t size, size_t nmemb, void *stream) 659redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config,
2212{ 660 int redir_depth, check_curl_working_state working_state) {
2213 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream; 661 curlhelp_statusline status_line;
662 struct phr_header headers[255];
663 size_t msglen;
664 size_t nof_headers = 255;
665 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
666 &status_line.http_minor, &status_line.http_code, &status_line.msg,
667 &msglen, headers, &nof_headers, 0);
2214 668
2215 while (buf->bufsize < buf->buflen + size * nmemb + 1) { 669 if (res == -1) {
2216 buf->bufsize = buf->bufsize * 2; 670 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2217 buf->buf = (char *)realloc (buf->buf, buf->bufsize); 671 }
2218 if (buf->buf == NULL) {
2219 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
2220 return -1;
2221 }
2222 }
2223 672
2224 memcpy (buf->buf + buf->buflen, buffer, size * nmemb); 673 char *location = get_header_value(headers, nof_headers, "location");
2225 buf->buflen += size * nmemb;
2226 buf->buf[buf->buflen] = '\0';
2227 674
2228 return (int)(size * nmemb); 675 if (verbose >= 2) {
2229} 676 printf(_("* Seen redirect location %s\n"), location);
677 }
2230 678
2231size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) 679 if (++redir_depth > config.max_depth) {
2232{ 680 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"),
2233 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; 681 config.max_depth, location);
682 }
2234 683
2235 size_t n = min (nmemb * size, buf->buflen - buf->pos); 684 UriParserStateA state;
685 UriUriA uri;
686 state.uri = &uri;
687 if (uriParseUriA(&state, location) != URI_SUCCESS) {
688 if (state.errorCode == URI_ERROR_SYNTAX) {
689 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'\n"),
690 location);
691 } else if (state.errorCode == URI_ERROR_MALLOC) {
692 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
693 }
694 }
2236 695
2237 memcpy (buffer, buf->buf + buf->pos, n); 696 char ipstr[INET_ADDR_MAX_SIZE];
2238 buf->pos += n; 697 char buf[DEFAULT_BUFFER_SIZE];
698 if (verbose >= 2) {
699 printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE));
700 printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
701 printf(_("** port: %s\n"), uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE));
702 if (uri.hostData.ip4) {
703 inet_ntop(AF_INET, uri.hostData.ip4->data, ipstr, sizeof(ipstr));
704 printf(_("** IPv4: %s\n"), ipstr);
705 }
706 if (uri.hostData.ip6) {
707 inet_ntop(AF_INET, uri.hostData.ip6->data, ipstr, sizeof(ipstr));
708 printf(_("** IPv6: %s\n"), ipstr);
709 }
710 if (uri.pathHead) {
711 printf(_("** path: "));
712 for (UriPathSegmentA *path_segment = uri.pathHead; path_segment;
713 path_segment = path_segment->next) {
714 printf("/%s", uri_string(path_segment->text, buf, DEFAULT_BUFFER_SIZE));
715 }
716 puts("");
717 }
718 if (uri.query.first) {
719 printf(_("** query: %s\n"), uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE));
720 }
721 if (uri.fragment.first) {
722 printf(_("** fragment: %s\n"), uri_string(uri.fragment, buf, DEFAULT_BUFFER_SIZE));
723 }
724 }
2239 725
2240 return (int)n; 726 if (uri.scheme.first) {
2241} 727 working_state.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https"));
728 }
2242 729
2243void 730 /* we do a sloppy test here only, because uriparser would have failed
2244curlhelp_freewritebuffer (curlhelp_write_curlbuf *buf) 731 * above, if the port would be invalid, we just check for MAX_PORT
2245{ 732 */
2246 free (buf->buf); 733 int new_port;
2247 buf->buf = NULL; 734 if (uri.portText.first) {
2248} 735 new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE));
736 } else {
737 new_port = HTTP_PORT;
738 if (working_state.use_ssl) {
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);
745 }
2249 746
2250int 747 /* by RFC 7231 relative URLs in Location should be taken relative to
2251curlhelp_initreadbuffer (curlhelp_read_curlbuf *buf, const char *data, size_t datalen) 748 * the original URL, so we try to form a new absolute URL here
2252{ 749 */
2253 buf->buflen = datalen; 750 char *new_host;
2254 buf->buf = (char *)malloc ((size_t)buf->buflen); 751 if (!uri.scheme.first && !uri.hostText.first) {
2255 if (buf->buf == NULL) return -1; 752 new_host = strdup(working_state.host_name ? working_state.host_name
2256 memcpy (buf->buf, data, datalen); 753 : working_state.server_address);
2257 buf->pos = 0; 754 new_port = working_state.serverPort;
2258 return 0; 755 if (working_state.use_ssl) {
2259} 756 uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE);
757 }
758 } else {
759 new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
760 }
2260 761
2261void 762 /* compose new path */
2262curlhelp_freereadbuffer (curlhelp_read_curlbuf *buf) 763 /* TODO: handle fragments and query part of URL */
2263{ 764 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE);
2264 free (buf->buf); 765 if (uri.pathHead) {
2265 buf->buf = NULL; 766 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment;
2266} 767 pathSegment = pathSegment->next) {
768 strncat(new_url, "/", DEFAULT_BUFFER_SIZE);
769 strncat(new_url, uri_string(pathSegment->text, buf, DEFAULT_BUFFER_SIZE),
770 DEFAULT_BUFFER_SIZE - 1);
771 }
772 }
2267 773
2268/* TODO: where to put this, it's actually part of sstrings2 (logically)? 774 if (working_state.serverPort == new_port &&
2269 */ 775 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
2270const char* 776 (working_state.host_name &&
2271strrstr2(const char *haystack, const char *needle) 777 !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
2272{ 778 !strcmp(working_state.server_url, new_url)) {
2273 int counter; 779 die(STATE_CRITICAL,
2274 size_t len; 780 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s\n"),
2275 const char *prev_pos; 781 working_state.use_ssl ? "https" : "http", new_host, new_port, new_url);
2276 const char *pos; 782 }
2277
2278 if (haystack == NULL || needle == NULL)
2279 return NULL;
2280
2281 if (haystack[0] == '\0' || needle[0] == '\0')
2282 return NULL;
2283
2284 counter = 0;
2285 prev_pos = NULL;
2286 pos = haystack;
2287 len = strlen (needle);
2288 for (;;) {
2289 pos = strstr (pos, needle);
2290 if (pos == NULL) {
2291 if (counter == 0)
2292 return NULL;
2293 else
2294 return prev_pos;
2295 }
2296 counter++;
2297 prev_pos = pos;
2298 pos += len;
2299 if (*pos == '\0') return prev_pos;
2300 }
2301}
2302 783
2303int 784 /* set new values for redirected request */
2304curlhelp_parse_statusline (const char *buf, curlhelp_statusline *status_line)
2305{
2306 char *first_line_end;
2307 char *p;
2308 size_t first_line_len;
2309 char *pp;
2310 const char *start;
2311 char *first_line_buf;
2312
2313 /* find last start of a new header */
2314 start = strrstr2 (buf, "\r\nHTTP/");
2315 if (start != NULL) {
2316 start += 2;
2317 buf = start;
2318 }
2319
2320 first_line_end = strstr(buf, "\r\n");
2321 if (first_line_end == NULL) return -1;
2322
2323 first_line_len = (size_t)(first_line_end - buf);
2324 status_line->first_line = (char *)malloc (first_line_len + 1);
2325 if (status_line->first_line == NULL) return -1;
2326 memcpy (status_line->first_line, buf, first_line_len);
2327 status_line->first_line[first_line_len] = '\0';
2328 first_line_buf = strdup( status_line->first_line );
2329
2330 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
2331
2332 p = strtok(first_line_buf, "/");
2333 if( p == NULL ) { free( first_line_buf ); return -1; }
2334 if( strcmp( p, "HTTP" ) != 0 ) { free( first_line_buf ); return -1; }
2335
2336 p = strtok( NULL, " " );
2337 if( p == NULL ) { free( first_line_buf ); return -1; }
2338 if( strchr( p, '.' ) != NULL ) {
2339
2340 /* HTTP 1.x case */
2341 strtok( p, "." );
2342 status_line->http_major = (int)strtol( p, &pp, 10 );
2343 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2344 strtok( NULL, " " );
2345 status_line->http_minor = (int)strtol( p, &pp, 10 );
2346 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2347 p += 4; /* 1.x SP */
2348 } else {
2349 /* HTTP 2 case */
2350 status_line->http_major = (int)strtol( p, &pp, 10 );
2351 status_line->http_minor = 0;
2352 p += 2; /* 2 SP */
2353 }
2354
2355 /* status code: "404" or "404.1", then SP */
2356
2357 p = strtok( p, " " );
2358 if( p == NULL ) { free( first_line_buf ); return -1; }
2359 if( strchr( p, '.' ) != NULL ) {
2360 char *ppp;
2361 ppp = strtok( p, "." );
2362 status_line->http_code = (int)strtol( ppp, &pp, 10 );
2363 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2364 ppp = strtok( NULL, "" );
2365 status_line->http_subcode = (int)strtol( ppp, &pp, 10 );
2366 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2367 p += 6; /* 400.1 SP */
2368 } else {
2369 status_line->http_code = (int)strtol( p, &pp, 10 );
2370 status_line->http_subcode = -1;
2371 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
2372 p += 4; /* 400 SP */
2373 }
2374
2375 /* Human readable message: "Not Found" CRLF */
2376
2377 p = strtok( p, "" );
2378 if( p == NULL ) { status_line->msg = ""; return 0; }
2379 status_line->msg = status_line->first_line + ( p - first_line_buf );
2380 free( first_line_buf );
2381
2382 return 0;
2383}
2384 785
2385void 786 if (!(config.followsticky & STICKY_HOST)) {
2386curlhelp_free_statusline (curlhelp_statusline *status_line) 787 free(working_state.server_address);
2387{ 788 working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH);
2388 free (status_line->first_line); 789 }
2389} 790 if (!(config.followsticky & STICKY_PORT)) {
791 working_state.serverPort = (unsigned short)new_port;
792 }
2390 793
2391void 794 free(working_state.host_name);
2392remove_newlines (char *s) 795 working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH);
2393{
2394 char *p;
2395 796
2396 for (p = s; *p != '\0'; p++) 797 /* reset virtual port */
2397 if (*p == '\r' || *p == '\n') 798 working_state.virtualPort = working_state.serverPort;
2398 *p = ' ';
2399}
2400 799
2401char * 800 free(new_host);
2402get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header) 801 free(working_state.server_url);
2403{ 802 working_state.server_url = new_url;
2404 for(size_t i = 0; i < nof_headers; i++ ) {
2405 if(headers[i].name != NULL && strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) {
2406 return strndup( headers[i].value, headers[i].value_len );
2407 }
2408 }
2409 return NULL;
2410}
2411 803
2412int 804 uriFreeUriMembersA(&uri);
2413check_document_dates (const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE])
2414{
2415 char *server_date = NULL;
2416 char *document_date = NULL;
2417 int date_result = STATE_OK;
2418 curlhelp_statusline status_line;
2419 struct phr_header headers[255];
2420 size_t nof_headers = 255;
2421 size_t msglen;
2422
2423 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
2424 &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
2425 headers, &nof_headers, 0);
2426 805
2427 if (res == -1) { 806 if (verbose) {
2428 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); 807 printf(_("Redirection to %s://%s:%d%s\n"), working_state.use_ssl ? "https" : "http",
808 working_state.host_name ? working_state.host_name : working_state.server_address,
809 working_state.serverPort, working_state.server_url);
2429 } 810 }
2430 811
2431 server_date = get_header_value (headers, nof_headers, "date"); 812 /* TODO: the hash component MUST be taken from the original URL and
2432 document_date = get_header_value (headers, nof_headers, "last-modified"); 813 * attached to the URL in Location
814 */
2433 815
2434 if (!server_date || !*server_date) { 816 redir_wrapper result = {
2435 char tmp[DEFAULT_BUFFER_SIZE]; 817 .redir_depth = redir_depth,
818 .working_state = working_state,
819 .error_code = OK,
820 };
821 return result;
822}
2436 823
2437 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg); 824check_curl_config_wrapper process_arguments(int argc, char **argv) {
2438 strcpy(*msg, tmp); 825 enum {
826 INVERT_REGEX = CHAR_MAX + 1,
827 SNI_OPTION,
828 MAX_REDIRS_OPTION,
829 CONTINUE_AFTER_CHECK_CERT,
830 CA_CERT_OPTION,
831 HTTP_VERSION_OPTION,
832 AUTOMATIC_DECOMPRESSION,
833 COOKIE_JAR,
834 HAPROXY_PROTOCOL,
835 STATE_REGEX,
836 OUTPUT_FORMAT
837 };
838
839 static struct option longopts[] = {
840 STD_LONG_OPTS,
841 {"link", no_argument, 0, 'L'},
842 {"nohtml", no_argument, 0, 'n'},
843 {"ssl", optional_argument, 0, 'S'},
844 {"sni", no_argument, 0, SNI_OPTION},
845 {"post", required_argument, 0, 'P'},
846 {"method", required_argument, 0, 'j'},
847 {"IP-address", required_argument, 0, 'I'},
848 {"url", required_argument, 0, 'u'},
849 {"port", required_argument, 0, 'p'},
850 {"authorization", required_argument, 0, 'a'},
851 {"proxy-authorization", required_argument, 0, 'b'},
852 {"header-string", required_argument, 0, 'd'},
853 {"string", required_argument, 0, 's'},
854 {"expect", required_argument, 0, 'e'},
855 {"regex", required_argument, 0, 'r'},
856 {"ereg", required_argument, 0, 'r'},
857 {"eregi", required_argument, 0, 'R'},
858 {"linespan", no_argument, 0, 'l'},
859 {"onredirect", required_argument, 0, 'f'},
860 {"certificate", required_argument, 0, 'C'},
861 {"client-cert", required_argument, 0, 'J'},
862 {"private-key", required_argument, 0, 'K'},
863 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
864 {"verify-cert", no_argument, 0, 'D'},
865 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
866 {"useragent", required_argument, 0, 'A'},
867 {"header", required_argument, 0, 'k'},
868 {"no-body", no_argument, 0, 'N'},
869 {"max-age", required_argument, 0, 'M'},
870 {"content-type", required_argument, 0, 'T'},
871 {"pagesize", required_argument, 0, 'm'},
872 {"invert-regex", no_argument, NULL, INVERT_REGEX},
873 {"state-regex", required_argument, 0, STATE_REGEX},
874 {"use-ipv4", no_argument, 0, '4'},
875 {"use-ipv6", no_argument, 0, '6'},
876 {"extended-perfdata", no_argument, 0, 'E'},
877 {"show-body", no_argument, 0, 'B'},
878 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
879 {"http-version", required_argument, 0, HTTP_VERSION_OPTION},
880 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION},
881 {"cookie-jar", required_argument, 0, COOKIE_JAR},
882 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL},
883 {"output-format", required_argument, 0, OUTPUT_FORMAT},
884 {0, 0, 0, 0}};
885
886 check_curl_config_wrapper result = {
887 .errorcode = OK,
888 .config = check_curl_config_init(),
889 };
890
891 if (argc < 2) {
892 result.errorcode = ERROR;
893 return result;
894 }
2439 895
2440 date_result = max_state_alt(STATE_UNKNOWN, date_result); 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 }
2441 914
2442 } else if (!document_date || !*document_date) { 915 int option = 0;
2443 char tmp[DEFAULT_BUFFER_SIZE]; 916 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
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) {
926 break;
927 }
2444 928
2445 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg); 929 switch (option_index) {
2446 strcpy(*msg, tmp); 930 case 'h':
931 print_help();
932 exit(STATE_UNKNOWN);
933 break;
934 case 'V':
935 print_revision(progname, NP_VERSION);
936 print_curl_version();
937 exit(STATE_UNKNOWN);
938 break;
939 case 'v':
940 verbose++;
941 break;
942 case 't': /* timeout period */
943 if (!is_intnonneg(optarg)) {
944 usage2(_("Timeout interval must be a positive integer"), optarg);
945 } else {
946 result.config.curl_config.socket_timeout = (int)strtol(optarg, NULL, 10);
947 }
948 break;
949 case 'c': /* critical time threshold */
950 {
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;
957 case 'w': /* warning time threshold */
958 {
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;
966 case 'H': /* virtual host */
967 result.config.initial_config.host_name = strdup(optarg);
968 char *tmp_string;
969 size_t host_name_length;
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);
974 /* cut off the port */
975 host_name_length =
976 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
977 free(result.config.initial_config.host_name);
978 result.config.initial_config.host_name = strndup(optarg, host_name_length);
979 }
980 } else if ((tmp_string = strchr(result.config.initial_config.host_name, ':')) != NULL &&
981 strchr(++tmp_string, ':') == NULL) { /* IPv4:port or host:port */
982 result.config.initial_config.virtualPort = atoi(tmp_string);
983 /* cut off the port */
984 host_name_length =
985 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
986 free(result.config.initial_config.host_name);
987 result.config.initial_config.host_name = strndup(optarg, host_name_length);
988 }
989 break;
990 case 'I': /* internet address */
991 result.config.initial_config.server_address = strdup(optarg);
992 break;
993 case 'u': /* URL path */
994 result.config.initial_config.server_url = strdup(optarg);
995 break;
996 case 'p': /* Server port */
997 if (!is_intnonneg(optarg)) {
998 usage2(_("Invalid port number, expecting a non-negative number"), optarg);
999 } else {
1000 if (strtol(optarg, NULL, 10) > MAX_PORT) {
1001 usage2(_("Invalid port number, supplied port number is too big"), optarg);
1002 }
1003 result.config.initial_config.serverPort = (unsigned short)strtol(optarg, NULL, 10);
1004 specify_port = true;
1005 }
1006 break;
1007 case 'a': /* authorization info */
1008 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1);
1009 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0;
1010 break;
1011 case 'b': /* proxy-authorization info */
1012 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1013 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1014 break;
1015 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1016 if (!result.config.initial_config.http_post_data) {
1017 result.config.initial_config.http_post_data = strdup(optarg);
1018 }
1019 if (!result.config.initial_config.http_method) {
1020 result.config.initial_config.http_method = strdup("POST");
1021 }
1022 break;
1023 case 'j': /* Set HTTP method */
1024 if (result.config.initial_config.http_method) {
1025 free(result.config.initial_config.http_method);
1026 }
1027 result.config.initial_config.http_method = strdup(optarg);
1028 break;
1029 case 'A': /* useragent */
1030 strncpy(result.config.curl_config.user_agent, optarg, DEFAULT_BUFFER_SIZE);
1031 result.config.curl_config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0';
1032 break;
1033 case 'k': /* Additional headers */
1034 if (result.config.curl_config.http_opt_headers_count == 0) {
1035 result.config.curl_config.http_opt_headers =
1036 malloc(sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1037 } else {
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;
1044 break;
1045 case 'L': /* show html link */
1046 case 'n': /* do not show html link */
1047 // HTML link related options are deprecated
1048 break;
1049 case 'C': /* Check SSL cert validity */
1050#ifndef LIBCURL_FEATURE_SSL
1051 usage4(_("Invalid option - SSL is not available"));
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;
1078 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
1079#ifdef HAVE_SSL
1080 result.config.continue_after_check_cert = true;
1081 break;
1082#endif
1083 case 'J': /* use client certificate */
1084#ifndef LIBCURL_FEATURE_SSL
1085 usage4(_("Invalid option - SSL is not available"));
1086#endif
1087 test_file(optarg);
1088 result.config.curl_config.client_cert = optarg;
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"));
1094#endif
1095 test_file(optarg);
1096 result.config.curl_config.client_privkey = optarg;
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"));
1102#endif
1103 test_file(optarg);
1104 result.config.curl_config.ca_cert = optarg;
1105 enable_tls = true;
1106 break;
1107 case 'D': /* verify peer certificate & host */
1108#ifndef LIBCURL_FEATURE_SSL
1109 usage4(_("Invalid option - SSL is not available"));
1110#endif
1111 result.config.curl_config.verify_peer_and_host = true;
1112 enable_tls = true;
1113 break;
1114 case 'S': /* use SSL */
1115 tls_option_optarg = optarg;
1116 enable_tls = true;
1117#ifndef LIBCURL_FEATURE_SSL
1118 usage4(_("Invalid option - SSL is not available"));
1119#endif
1120 break;
1121 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */
1122#ifndef LIBCURL_FEATURE_SSL
1123 usage4(_("Invalid option - SSL is not available"));
1124#endif /* LIBCURL_FEATURE_SSL */
1125 break;
1126 case MAX_REDIRS_OPTION:
1127 if (!is_intnonneg(optarg)) {
1128 usage2(_("Invalid max_redirs count"), optarg);
1129 } else {
1130 result.config.max_depth = atoi(optarg);
1131 }
1132 break;
1133 case 'f': /* onredirect */
1134 if (!strcmp(optarg, "ok")) {
1135 result.config.on_redirect_result_state = STATE_OK;
1136 result.config.on_redirect_dependent = false;
1137 } else if (!strcmp(optarg, "warning")) {
1138 result.config.on_redirect_result_state = STATE_WARNING;
1139 result.config.on_redirect_dependent = false;
1140 } else if (!strcmp(optarg, "critical")) {
1141 result.config.on_redirect_result_state = STATE_CRITICAL;
1142 result.config.on_redirect_dependent = false;
1143 } else if (!strcmp(optarg, "unknown")) {
1144 result.config.on_redirect_result_state = STATE_UNKNOWN;
1145 result.config.on_redirect_dependent = false;
1146 } else if (!strcmp(optarg, "follow")) {
1147 result.config.on_redirect_dependent = true;
1148 } else if (!strcmp(optarg, "stickyport")) {
1149 result.config.on_redirect_dependent = true;
1150 result.config.followmethod = FOLLOW_HTTP_CURL,
1151 result.config.followsticky = STICKY_HOST | STICKY_PORT;
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 {
1164 usage2(_("Invalid onredirect option"), optarg);
1165 }
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 }
1174 break;
1175 case 'd': /* string or substring */
1176 strncpy(result.config.header_expect, optarg, MAX_INPUT_BUFFER - 1);
1177 result.config.header_expect[MAX_INPUT_BUFFER - 1] = 0;
1178 break;
1179 case 's': /* string or substring */
1180 strncpy(result.config.string_expect, optarg, MAX_INPUT_BUFFER - 1);
1181 result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0;
1182 break;
1183 case 'e': /* string or substring */
1184 strncpy(result.config.server_expect.string, optarg, MAX_INPUT_BUFFER - 1);
1185 result.config.server_expect.string[MAX_INPUT_BUFFER - 1] = 0;
1186 result.config.server_expect.is_present = true;
1187 break;
1188 case 'T': /* Content-type */
1189 result.config.curl_config.http_content_type = strdup(optarg);
1190 break;
1191 case 'l': /* linespan */
1192 cflags &= ~REG_NEWLINE;
1193 break;
1194 case 'R': /* regex */
1195 cflags |= REG_ICASE;
1196 // fall through
1197 case 'r': /* regex */
1198 strncpy(result.config.regexp, optarg, MAX_RE_SIZE - 1);
1199 result.config.regexp[MAX_RE_SIZE - 1] = 0;
1200 regex_t preg;
1201 int errcode = regcomp(&preg, result.config.regexp, cflags);
1202 if (errcode != 0) {
1203 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1204 printf(_("Could Not Compile Regular Expression: %s"), errbuf);
1205 result.errorcode = ERROR;
1206 return result;
1207 }
1208
1209 result.config.compiled_regex = preg;
1210 break;
1211 case INVERT_REGEX:
1212 result.config.invert_regex = true;
1213 break;
1214 case STATE_REGEX:
1215 if (!strcasecmp(optarg, "critical")) {
1216 result.config.state_regex = STATE_CRITICAL;
1217 } else if (!strcasecmp(optarg, "warning")) {
1218 result.config.state_regex = STATE_WARNING;
1219 } else {
1220 usage2(_("Invalid state-regex option"), optarg);
1221 }
1222 break;
1223 case '4':
1224 result.config.curl_config.sin_family = AF_INET;
1225 break;
1226 case '6':
1227#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
1228 result.config.curl_config.sin_family = AF_INET6;
1229#else
1230 usage4(_("IPv6 support not available"));
1231#endif
1232 break;
1233 case 'm': /* min_page_length */
1234 {
1235 mp_range_parsed foo = mp_parse_range_string(optarg);
2447 1236
2448 date_result = max_state_alt(STATE_CRITICAL, date_result); 1237 if (foo.error != MP_PARSING_SUCCES) {
1238 die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg);
1239 }
2449 1240
2450 } else { 1241 result.config.page_length_limits = foo.range;
2451 time_t srv_data = curl_getdate (server_date, NULL); 1242 result.config.page_length_limits_is_set = true;
2452 time_t doc_data = curl_getdate (document_date, NULL); 1243 break;
2453 if (verbose >= 2) 1244 }
2454 printf ("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); 1245 case 'N': /* no-body */
2455 if (srv_data <= 0) { 1246 result.config.initial_config.no_body = true;
2456 char tmp[DEFAULT_BUFFER_SIZE]; 1247 break;
1248 case 'M': /* max-age */
1249 {
1250 size_t option_length = strlen(optarg);
1251 if (option_length && optarg[option_length - 1] == 'm') {
1252 result.config.maximum_age = atoi(optarg) * 60;
1253 } else if (option_length && optarg[option_length - 1] == 'h') {
1254 result.config.maximum_age = atoi(optarg) * 60 * 60;
1255 } else if (option_length && optarg[option_length - 1] == 'd') {
1256 result.config.maximum_age = atoi(optarg) * 60 * 60 * 24;
1257 } else if (option_length &&
1258 (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) {
1259 result.config.maximum_age = atoi(optarg);
1260 } else {
1261 fprintf(stderr, "unparsable max-age: %s\n", optarg);
1262 exit(STATE_WARNING);
1263 }
1264 if (verbose >= 2) {
1265 printf("* Maximal age of document set to %d seconds\n", result.config.maximum_age);
1266 }
1267 } break;
1268 case 'E': /* show extended perfdata */
1269 result.config.show_extended_perfdata = true;
1270 break;
1271 case 'B': /* print body content after status line */
1272 result.config.show_body = true;
1273 break;
1274 case HTTP_VERSION_OPTION:
1275 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1276 if (strcmp(optarg, "1.0") == 0) {
1277 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_0;
1278 } else if (strcmp(optarg, "1.1") == 0) {
1279 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_1;
1280 } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) {
1281#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0)
1282 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_2_0;
1283#else
1284 result.config.curl_http_version = CURL_HTTP_VERSION_NONE;
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) */
1292 } else {
1293 fprintf(stderr, "unknown http-version parameter: %s\n", optarg);
1294 exit(STATE_WARNING);
1295 }
1296 break;
1297 case AUTOMATIC_DECOMPRESSION:
1298 result.config.curl_config.automatic_decompression = true;
1299 break;
1300 case COOKIE_JAR:
1301 result.config.curl_config.cookie_jar_file = optarg;
1302 break;
1303 case HAPROXY_PROTOCOL:
1304 result.config.curl_config.haproxy_protocol = true;
1305 break;
1306 case '?':
1307 /* print short usage statement if args not parsable */
1308 usage5();
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 }
2457 1317
2458 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); 1318 result.config.output_format_is_set = true;
2459 strcpy(*msg, tmp); 1319 result.config.output_format = parser.output_format;
1320 break;
1321 }
1322 }
1323 }
2460 1324
2461 date_result = max_state_alt(STATE_CRITICAL, date_result); 1325 if (enable_tls) {
2462 } else if (doc_data <= 0) { 1326 bool got_plus = false;
2463 char tmp[DEFAULT_BUFFER_SIZE]; 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 }
2464 1338
2465 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); 1339 if (optarg[0] == '2') {
2466 strcpy(*msg, tmp); 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 }
2467 1408
2468 date_result = max_state_alt(STATE_CRITICAL, date_result); 1409 int option_counter = optind;
2469 } else if (doc_data > srv_data + 30) {
2470 char tmp[DEFAULT_BUFFER_SIZE];
2471 1410
2472 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); 1411 if (result.config.initial_config.server_address == NULL && option_counter < argc) {
2473 strcpy(*msg, tmp); 1412 result.config.initial_config.server_address = strdup(argv[option_counter++]);
1413 }
2474 1414
2475 date_result = max_state_alt(STATE_CRITICAL, date_result); 1415 if (result.config.initial_config.host_name == NULL && option_counter < argc) {
2476 } else if (doc_data < srv_data - maximum_age) { 1416 result.config.initial_config.host_name = strdup(argv[option_counter++]);
2477 int n = (srv_data - doc_data); 1417 }
2478 if (n > (60 * 60 * 24 * 2)) {
2479 char tmp[DEFAULT_BUFFER_SIZE];
2480 1418
2481 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24)); 1419 if (result.config.initial_config.server_address == NULL) {
2482 strcpy(*msg, tmp); 1420 if (result.config.initial_config.host_name == NULL) {
1421 usage4(_("You must specify a server address or host name"));
1422 } else {
1423 result.config.initial_config.server_address =
1424 strdup(result.config.initial_config.host_name);
1425 }
1426 }
2483 1427
2484 date_result = max_state_alt(STATE_CRITICAL, date_result); 1428 if (result.config.initial_config.http_method == NULL) {
2485 } else { 1429 result.config.initial_config.http_method = strdup("GET");
2486 char tmp[DEFAULT_BUFFER_SIZE]; 1430 }
2487 1431
2488 snprintf (tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); 1432 if (result.config.curl_config.client_cert && !result.config.curl_config.client_privkey) {
2489 strcpy(*msg, tmp); 1433 usage4(_("If you use a client certificate you must also specify a private key file"));
1434 }
2490 1435
2491 date_result = max_state_alt(STATE_CRITICAL, date_result); 1436 if (result.config.initial_config.virtualPort == 0) {
1437 result.config.initial_config.virtualPort = result.config.initial_config.serverPort;
1438 } else {
1439 if ((result.config.initial_config.use_ssl &&
1440 result.config.initial_config.serverPort == HTTPS_PORT) ||
1441 (!result.config.initial_config.use_ssl &&
1442 result.config.initial_config.serverPort == HTTP_PORT)) {
1443 if (!specify_port) {
1444 result.config.initial_config.serverPort = result.config.initial_config.virtualPort;
2492 } 1445 }
2493 } 1446 }
2494 } 1447 }
2495 1448
2496 if (server_date) free (server_date); 1449 return result;
2497 if (document_date) free (document_date);
2498
2499 return date_result;
2500} 1450}
2501 1451
1452void print_help(void) {
1453 print_revision(progname, NP_VERSION);
2502 1454
2503int 1455 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
2504get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf) 1456 printf(COPYRIGHT, copyright, email);
2505{
2506 size_t content_length = 0;
2507 struct phr_header headers[255];
2508 size_t nof_headers = 255;
2509 size_t msglen;
2510 char *content_length_s = NULL;
2511 curlhelp_statusline status_line;
2512 1457
2513 int res = phr_parse_response (header_buf->buf, header_buf->buflen, 1458 printf("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
2514 &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, 1459 printf("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
2515 headers, &nof_headers, 0); 1460 printf("%s\n", _("strings and regular expressions, check connection times, and report on"));
1461 printf("%s\n", _("certificate expiration times."));
1462 printf("\n");
1463 printf("%s\n",
1464 _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1465 printf("%s\n", _("as possible."));
2516 1466
2517 if (res == -1) { 1467 printf("\n\n");
2518 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2519 }
2520 1468
2521 content_length_s = get_header_value (headers, nof_headers, "content-length"); 1469 print_usage();
2522 if (!content_length_s) {
2523 return header_buf->buflen + body_buf->buflen;
2524 }
2525 content_length_s += strspn (content_length_s, " \t");
2526 content_length = atoi (content_length_s);
2527 if (content_length != body_buf->buflen) {
2528 /* TODO: should we warn if the actual and the reported body length don't match? */
2529 }
2530 1470
2531 if (content_length_s) free (content_length_s); 1471 printf(_("NOTE: One or both of -H and -I must be specified"));
2532 1472
2533 return header_buf->buflen + body_buf->buflen; 1473 printf("\n");
2534} 1474
1475 printf(UT_HELP_VRSN);
1476 printf(UT_EXTRA_OPTS);
2535 1477
2536/* TODO: is there a better way in libcurl to check for the SSL library? */ 1478 printf(" %s\n", "-H, --hostname=ADDRESS");
2537curlhelp_ssl_library 1479 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)"));
2538curlhelp_get_ssl_library () 1480 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
2539{ 1481 printf(" %s\n", "-I, --IP-address=ADDRESS");
2540 curl_version_info_data* version_data; 1482 printf(" %s\n",
2541 char *ssl_version; 1483 _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
2542 char *library; 1484 printf(" %s\n", "-p, --port=INTEGER");
2543 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; 1485 printf(" %s", _("Port number (default: "));
1486 printf("%d)\n", HTTP_PORT);
2544 1487
2545 version_data = curl_version_info (CURLVERSION_NOW); 1488 printf(UT_IPv46);
2546 if (version_data == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN; 1489
1490#ifdef LIBCURL_FEATURE_SSL
1491 printf(" %s\n", "-S, --ssl=VERSION[+]");
1492 printf(" %s\n",
1493 _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1494 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1495 printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are "
1496 "also accepted."));
1497 printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually "
1498 "disabled in libcurl"));
1499 printf(" %s\n", "--sni");
1500 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1501# if LIBCURL_VERSION_NUM >= 0x071801
1502 printf(" %s\n",
1503 _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
1504 printf(" %s\n", _(" SNI only really works since TLSv1.0"));
1505# else
1506 printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
1507# endif
1508 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1509 printf(" %s\n",
1510 _("Minimum number of days a certificate has to be valid. Port defaults to 443."));
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"));
1515 printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned."));
1516 printf(" %s\n",
1517 _("(When this option is used the URL is not checked by default. You can use"));
1518 printf(" %s\n", _(" --continue-after-certificate to override this behavior)"));
1519 printf(" %s\n", "--continue-after-certificate");
1520 printf(" %s\n",
1521 _("Allows the HTTP check to continue after performing the certificate check."));
1522 printf(" %s\n", _("Does nothing unless -C is used."));
1523 printf(" %s\n", "-J, --client-cert=FILE");
1524 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1525 printf(" %s\n", _("to be used in establishing the SSL session"));
1526 printf(" %s\n", "-K, --private-key=FILE");
1527 printf(" %s\n", _("Name of file containing the private key (PEM format)"));
1528 printf(" %s\n", _("matching the client certificate"));
1529 printf(" %s\n", "--ca-cert=FILE");
1530 printf(" %s\n", _("CA certificate file to verify peer against"));
1531 printf(" %s\n", "-D, --verify-cert");
1532 printf(" %s\n", _("Verify the peer's SSL certificate and hostname"));
1533#endif
2547 1534
2548 ssl_version = strdup (version_data->ssl_version); 1535 printf(" %s\n", "-e, --expect=STRING");
2549 if (ssl_version == NULL ) return CURLHELP_SSL_LIBRARY_UNKNOWN; 1536 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1537 printf(" %s", _("the first (status) line of the server response (default: "));
1538 printf("%s)\n", HTTP_EXPECT);
1539 printf(" %s\n",
1540 _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1541 printf(" %s\n", "-d, --header-string=STRING");
1542 printf(" %s\n", _("String to expect in the response headers"));
1543 printf(" %s\n", "-s, --string=STRING");
1544 printf(" %s\n", _("String to expect in the content"));
1545 printf(" %s\n", "-u, --url=PATH");
1546 printf(" %s\n", _("URL to GET or POST (default: /)"));
1547 printf(" %s\n", "-P, --post=STRING");
1548 printf(" %s\n", _("URL decoded http POST data"));
1549 printf(" %s\n",
1550 "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1551 printf(" %s\n", _("Set HTTP method."));
1552 printf(" %s\n", "-N, --no-body");
1553 printf(" %s\n", _("Don't wait for document body: stop reading after headers."));
1554 printf(" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1555 printf(" %s\n", "-M, --max-age=SECONDS");
1556 printf(" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1557 printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1558 printf(" %s\n", "-T, --content-type=STRING");
1559 printf(" %s\n", _("specify Content-Type header media type when POSTing\n"));
1560 printf(" %s\n", "-l, --linespan");
1561 printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1562 printf(" %s\n", "-r, --regex, --ereg=STRING");
1563 printf(" %s\n", _("Search page for regex STRING"));
1564 printf(" %s\n", "-R, --eregi=STRING");
1565 printf(" %s\n", _("Search page for case-insensitive regex STRING"));
1566 printf(" %s\n", "--invert-regex");
1567 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
1568 printf(" %s\n", _("can be changed with --state--regex)"));
1569 printf(" %s\n", "--state-regex=STATE");
1570 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of "
1571 "\"critical\",\"warning\""));
1572 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1573 printf(" %s\n", _("Username:password on sites with basic authentication"));
1574 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1575 printf(" %s\n", _("Username:password on proxy-servers with basic authentication"));
1576 printf(" %s\n", "-A, --useragent=STRING");
1577 printf(" %s\n", _("String to be sent in http header as \"User Agent\""));
1578 printf(" %s\n", "-k, --header=STRING");
1579 printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for "
1580 "additional headers"));
1581 printf(" %s\n", "-E, --extended-perfdata");
1582 printf(" %s\n", _("Print additional performance data"));
1583 printf(" %s\n", "-B, --show-body");
1584 printf(" %s\n", _("Print body content below status line"));
1585 // printf(" %s\n", "-L, --link");
1586 // printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1587 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
1588 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1589 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1590 printf(" %s\n", _("follow uses the old redirection algorithm of check_http."));
1591 printf(" %s\n", _("curl uses CURL_FOLLOWLOCATION built into libcurl."));
1592 printf(" %s\n", "--max-redirs=INTEGER");
1593 printf(" %s", _("Maximal number of redirects (default: "));
1594 printf("%d)\n", DEFAULT_MAX_REDIRS);
1595 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1596 printf(" %s\n",
1597 _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1598 printf("\n");
1599 printf(" %s\n", "--http-version=VERSION");
1600 printf(" %s\n", _("Connect via specific HTTP protocol."));
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)"));
1603 printf(" %s\n", "--enable-automatic-decompression");
1604 printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING)."));
1605 printf(" %s\n", "--haproxy-protocol");
1606 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL)."));
1607 printf(" %s\n", "--cookie-jar=FILE");
1608 printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested."));
1609 printf(" %s\n",
1610 _("Specify an empty string as FILE to enable curl's cookie engine without saving"));
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"));
1615 printf("\n");
1616
1617 printf(UT_WARN_CRIT);
1618
1619 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1620
1621 printf(UT_VERBOSE);
1622
1623 printf(UT_OUTPUT_FORMAT);
1624
1625 printf("\n");
1626 printf("%s\n", _("Notes:"));
1627 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1628 printf(" %s\n",
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"));
1632 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1633 printf(" %s\n",
1634 _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1635 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
2550 1636
2551 library = strtok (ssl_version, "/"); 1637#ifdef LIBCURL_FEATURE_SSL
2552 if (library == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN; 1638 printf("\n");
1639 printf(" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1640 printf(" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1641 printf(" %s\n", _("certificate is still valid for the specified number of days."));
1642 printf("\n");
1643 printf(" %s\n", _("Please note that this plugin does not check if the presented server"));
1644 printf(" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1645 printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1646 printf("\n");
1647 printf("%s\n", _("Examples:"));
1648 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
1649 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1650 printf(" %s\n",
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,"));
1654 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
1655 printf("\n");
1656 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
1657 printf(" %s\n",
1658 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
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"));
1663 printf(" %s\n\n", _("the certificate is expired."));
1664 printf("\n");
1665 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
1666 printf(" %s\n",
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"));
1670 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1671 printf(" %s\n",
1672 _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1673#endif
2553 1674
2554 if (strcmp (library, "OpenSSL") == 0) 1675 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
2555 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; 1676 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
2556 else if (strcmp (library, "LibreSSL") == 0) 1677 printf(" %s\n",
2557 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; 1678 _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
2558 else if (strcmp (library, "GnuTLS") == 0) 1679 printf(" %s\n", _("legacy proxy requests in check_http style still work:"));
2559 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; 1680 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ "
2560 else if (strcmp (library, "NSS") == 0) 1681 "-H www.monitoring-plugins.org"));
2561 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
2562 1682
2563 if (verbose >= 2) 1683#ifdef LIBCURL_FEATURE_SSL
2564 printf ("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library); 1684 printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1685 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
1686 printf(" %s\n",
1687 _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
1688 printf(" %s\n", _("legacy proxy requests in check_http style still work:"));
1689 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j "
1690 "CONNECT -H www.verisign.com "));
1691 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
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,"));
1697 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
2565 1698
2566 free (ssl_version); 1699#endif
2567 1700
2568 return ssl_library; 1701 printf(UT_SUPPORT);
2569} 1702}
2570 1703
2571const char* 1704void print_usage(void) {
2572curlhelp_get_ssl_library_string (curlhelp_ssl_library ssl_library) 1705 printf("%s\n", _("Usage:"));
2573{ 1706 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
2574 switch (ssl_library) { 1707 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate "
2575 case CURLHELP_SSL_LIBRARY_OPENSSL: 1708 "file>] [-D]\n");
2576 return "OpenSSL"; 1709 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
2577 case CURLHELP_SSL_LIBRARY_LIBRESSL: 1710 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n");
2578 return "LibreSSL"; 1711 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
2579 case CURLHELP_SSL_LIBRARY_GNUTLS: 1712 "regex>]\n");
2580 return "GnuTLS"; 1713 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
2581 case CURLHELP_SSL_LIBRARY_NSS: 1714 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
2582 return "NSS"; 1715 printf(" [-T <content-type>] [-j method]\n");
2583 case CURLHELP_SSL_LIBRARY_UNKNOWN: 1716 printf(" [--http-version=<version>] [--enable-automatic-decompression]\n");
2584 default: 1717 printf(" [--cookie-jar=<cookie jar file>\n");
2585 return "unknown"; 1718 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname);
2586 } 1719 printf(" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
1720 printf("\n");
1721#ifdef LIBCURL_FEATURE_SSL
1722 printf("%s\n", _("In the first form, make an HTTP request."));
1723 printf("%s\n\n", _("In the second form, connect to the server and check the TLS certificate."));
1724#endif
2587} 1725}
2588 1726
1727void print_curl_version(void) { printf("%s\n", curl_version()); }
1728
2589#ifdef LIBCURL_FEATURE_SSL 1729#ifdef LIBCURL_FEATURE_SSL
2590#ifndef USE_OPENSSL 1730# ifndef USE_OPENSSL
2591time_t 1731time_t parse_cert_date(const char *s) {
2592parse_cert_date (const char *s) 1732 if (!s) {
2593{ 1733 return -1;
2594 struct tm tm; 1734 }
2595 time_t date; 1735
2596 char *res; 1736 /* Jan 17 14:25:12 2020 GMT */
2597 1737 struct tm tm;
2598 if (!s) return -1; 1738 char *res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm);
2599 1739 /* Sep 11 12:00:00 2020 GMT */
2600 /* Jan 17 14:25:12 2020 GMT */ 1740 if (res == NULL) {
2601 res = strptime (s, "%Y-%m-%d %H:%M:%S GMT", &tm); 1741 strptime(s, "%Y %m %d %H:%M:%S GMT", &tm);
2602 /* Sep 11 12:00:00 2020 GMT */ 1742 }
2603 if (res == NULL) strptime (s, "%Y %m %d %H:%M:%S GMT", &tm); 1743 time_t date = mktime(&tm);
2604 date = mktime (&tm); 1744
2605 1745 return date;
2606 return date;
2607} 1746}
1747# endif /* USE_OPENSSL */
1748#endif /* LIBCURL_FEATURE_SSL */
2608 1749
1750#ifdef LIBCURL_FEATURE_SSL
1751# ifndef USE_OPENSSL
2609/* 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
2610 * OpenSSL could be this function 1753 * OpenSSL could be this function
2611 */ 1754 */
2612int 1755int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn,
2613net_noopenssl_check_certificate (cert_ptr_union* cert_ptr, int days_till_exp_warn, int days_till_exp_crit) 1756 int days_till_exp_crit) {
2614{ 1757
2615 int i; 1758 if (verbose >= 2) {
2616 struct curl_slist* slist; 1759 printf("**** REQUEST CERTIFICATES ****\n");
2617 int cname_found = 0; 1760 }
2618 char* start_date_str = NULL; 1761
2619 char* end_date_str = NULL; 1762 char *start_date_str = NULL;
2620 time_t start_date; 1763 char *end_date_str = NULL;
2621 time_t end_date; 1764 bool have_first_cert = false;
2622 char *tz; 1765 bool cname_found = false;
2623 float time_left; 1766 for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) {
2624 int days_left; 1767 if (have_first_cert) {
2625 int time_remaining; 1768 break;
2626 char timestamp[50] = ""; 1769 }
2627 int status = STATE_UNKNOWN; 1770
2628 1771 struct curl_slist *slist;
2629 if (verbose >= 2) 1772 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) {
2630 printf ("**** REQUEST CERTIFICATES ****\n"); 1773 /* find first common name in subject,
2631 1774 * TODO: check alternative subjects for
2632 for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { 1775 * TODO: have a decent parser here and not a hack
2633 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { 1776 * multi-host certificate, check wildcards
2634 /* find first common name in subject, 1777 */
2635 * TODO: check alternative subjects for 1778 if (strncasecmp(slist->data, "Subject:", 8) == 0) {
2636 * TODO: have a decent parser here and not a hack 1779 int d = 3;
2637 * multi-host certificate, check wildcards 1780 char *p = strstr(slist->data, "CN=");
2638 */ 1781 if (p == NULL) {
2639 if (strncasecmp (slist->data, "Subject:", 8) == 0) { 1782 d = 5;
2640 int d = 3; 1783 p = strstr(slist->data, "CN = ");
2641 char* p = strstr (slist->data, "CN="); 1784 }
2642 if (p == NULL) { 1785 if (p != NULL) {
2643 d = 5; 1786 if (strncmp(host_name, p + d, strlen(host_name)) == 0) {
2644 p = strstr (slist->data, "CN = "); 1787 cname_found = true;
2645 } 1788 }
2646 if (p != NULL) { 1789 }
2647 if (strncmp (host_name, p+d, strlen (host_name)) == 0) { 1790 } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) {
2648 cname_found = 1; 1791 start_date_str = &slist->data[11];
2649 } 1792 } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) {
2650 } 1793 end_date_str = &slist->data[12];
2651 } else if (strncasecmp (slist->data, "Start Date:", 11) == 0) { 1794 } else if (strncasecmp(slist->data, "Cert:", 5) == 0) {
2652 start_date_str = &slist->data[11]; 1795 have_first_cert = true;
2653 } else if (strncasecmp (slist->data, "Expire Date:", 12) == 0) { 1796 break;
2654 end_date_str = &slist->data[12]; 1797 }
2655 } else if (strncasecmp (slist->data, "Cert:", 5) == 0) { 1798 if (verbose >= 2) {
2656 goto HAVE_FIRST_CERT; 1799 printf("%d ** %s\n", i, slist->data);
2657 } 1800 }
2658 if (verbose >= 2) 1801 }
2659 printf ("%d ** %s\n", i, slist->data); 1802 }
2660 } 1803
2661 } 1804 if (verbose >= 2) {
2662HAVE_FIRST_CERT: 1805 printf("**** REQUEST CERTIFICATES ****\n");
2663 1806 }
2664 if (verbose >= 2) 1807
2665 printf ("**** REQUEST CERTIFICATES ****\n"); 1808 if (!cname_found) {
2666 1809 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
2667 if (!cname_found) {
2668 printf("%s\n",_("CRITICAL - Cannot retrieve certificate subject."));
2669 return STATE_CRITICAL; 1810 return STATE_CRITICAL;
2670 } 1811 }
2671 1812
2672 start_date = parse_cert_date (start_date_str); 1813 time_t start_date = parse_cert_date(start_date_str);
2673 if (start_date <= 0) { 1814 if (start_date <= 0) {
2674 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), 1815 snprintf(msg, DEFAULT_BUFFER_SIZE,
2675 start_date_str); 1816 _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str);
2676 puts (msg); 1817 puts(msg);
2677 return STATE_WARNING; 1818 return STATE_WARNING;
2678 } 1819 }
2679 1820
2680 end_date = parse_cert_date (end_date_str); 1821 time_t end_date = parse_cert_date(end_date_str);
2681 if (end_date <= 0) { 1822 if (end_date <= 0) {
2682 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), 1823 snprintf(msg, DEFAULT_BUFFER_SIZE,
2683 start_date_str); 1824 _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str);
2684 puts (msg); 1825 puts(msg);
2685 return STATE_WARNING; 1826 return STATE_WARNING;
2686 } 1827 }
2687 1828
2688 time_left = difftime (end_date, time(NULL)); 1829 float time_left = difftime(end_date, time(NULL));
2689 days_left = time_left / 86400; 1830 int days_left = time_left / 86400;
2690 tz = getenv("TZ"); 1831 char *tz = getenv("TZ");
2691 setenv("TZ", "GMT", 1); 1832 setenv("TZ", "GMT", 1);
2692 tzset(); 1833 tzset();
1834
1835 char timestamp[50] = "";
2693 strftime(timestamp, 50, "%c %z", localtime(&end_date)); 1836 strftime(timestamp, 50, "%c %z", localtime(&end_date));
2694 if (tz) 1837 if (tz) {
2695 setenv("TZ", tz, 1); 1838 setenv("TZ", tz, 1);
2696 else 1839 } else {
2697 unsetenv("TZ"); 1840 unsetenv("TZ");
1841 }
2698 tzset(); 1842 tzset();
2699 1843
1844 mp_state_enum status = STATE_UNKNOWN;
1845 int time_remaining;
2700 if (days_left > 0 && days_left <= days_till_exp_warn) { 1846 if (days_left > 0 && days_left <= days_till_exp_warn) {
2701 printf (_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, days_left, timestamp); 1847 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
2702 if (days_left > days_till_exp_crit) 1848 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left,
1849 timestamp);
1850 if (days_left > days_till_exp_crit) {
2703 status = STATE_WARNING; 1851 status = STATE_WARNING;
2704 else 1852 } else {
2705 status = STATE_CRITICAL; 1853 status = STATE_CRITICAL;
1854 }
2706 } else if (days_left == 0 && time_left > 0) { 1855 } else if (days_left == 0 && time_left > 0) {
2707 if (time_left >= 3600) 1856 if (time_left >= 3600) {
2708 time_remaining = (int) time_left / 3600; 1857 time_remaining = (int)time_left / 3600;
2709 else 1858 } else {
2710 time_remaining = (int) time_left / 60; 1859 time_remaining = (int)time_left / 60;
1860 }
2711 1861
2712 printf (_("%s - Certificate '%s' expires in %u %s (%s)\n"), 1862 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
2713 (days_left>days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining, 1863 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining,
2714 time_left >= 3600 ? "hours" : "minutes", timestamp); 1864 time_left >= 3600 ? "hours" : "minutes", timestamp);
2715 1865
2716 if ( days_left > days_till_exp_crit) 1866 if (days_left > days_till_exp_crit) {
2717 status = STATE_WARNING; 1867 status = STATE_WARNING;
2718 else 1868 } else {
2719 status = STATE_CRITICAL; 1869 status = STATE_CRITICAL;
1870 }
2720 } else if (time_left < 0) { 1871 } else if (time_left < 0) {
2721 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); 1872 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp);
2722 status=STATE_CRITICAL; 1873 status = STATE_CRITICAL;
2723 } else if (days_left == 0) { 1874 } else if (days_left == 0) {
2724 printf (_("%s - Certificate '%s' just expired (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, timestamp); 1875 printf(_("%s - Certificate '%s' just expired (%s).\n"),
2725 if (days_left > days_till_exp_crit) 1876 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp);
1877 if (days_left > days_till_exp_crit) {
2726 status = STATE_WARNING; 1878 status = STATE_WARNING;
2727 else 1879 } else {
2728 status = STATE_CRITICAL; 1880 status = STATE_CRITICAL;
1881 }
2729 } else { 1882 } else {
2730 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);
2731 status = STATE_OK; 1884 status = STATE_OK;
2732 } 1885 }
2733 return status; 1886 return status;
2734} 1887}
2735#endif /* USE_OPENSSL */ 1888# endif /* USE_OPENSSL */
2736#endif /* LIBCURL_FEATURE_SSL */ 1889#endif /* LIBCURL_FEATURE_SSL */
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
new file mode 100644
index 00000000..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 29c85206..468ded31 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -1,38 +1,40 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dbi plugin 3 * Monitoring check_dbi plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2011 Monitoring Plugins Development Team 6 * Copyright (c) 2011-2024 Monitoring Plugins Development Team
7* Author: Sebastian 'tokkee' Harl <sh@teamix.net> 7 * Original Author: Sebastian 'tokkee' Harl <sh@teamix.net>
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_dbi plugin 11 * This file contains the check_dbi plugin
12* 12 *
13* Runs an arbitrary (SQL) command and checks the result. 13 * Runs an arbitrary (SQL) command and checks the result.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_dbi"; 32const char *progname = "check_dbi";
33const char *copyright = "2011"; 33const char *copyright = "2011-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "../lib/monitoringplug.h"
37#include "check_dbi.d/config.h"
36#include "common.h" 38#include "common.h"
37#include "utils.h" 39#include "utils.h"
38#include "utils_cmd.h" 40#include "utils_cmd.h"
@@ -43,7 +45,7 @@ const char *email = "devel@monitoring-plugins.org";
43 45
44/* required for NAN */ 46/* required for NAN */
45#ifndef _ISOC99_SOURCE 47#ifndef _ISOC99_SOURCE
46#define _ISOC99_SOURCE 48# define _ISOC99_SOURCE
47#endif 49#endif
48 50
49#include <assert.h> 51#include <assert.h>
@@ -53,59 +55,27 @@ const char *email = "devel@monitoring-plugins.org";
53 55
54#include <stdarg.h> 56#include <stdarg.h>
55 57
56typedef enum { 58static int verbose = 0;
57 METRIC_CONN_TIME,
58 METRIC_SERVER_VERSION,
59 METRIC_QUERY_RESULT,
60 METRIC_QUERY_TIME,
61} np_dbi_metric_t;
62
63typedef enum {
64 TYPE_NUMERIC,
65 TYPE_STRING,
66} np_dbi_type_t;
67 59
68typedef struct { 60typedef struct {
69 char *key; 61 int errorcode;
70 char *value; 62 check_dbi_config config;
71} driver_option_t; 63} check_dbi_config_wrapper;
72
73char *host = NULL;
74int verbose = 0;
75
76char *warning_range = NULL;
77char *critical_range = NULL;
78thresholds *dbi_thresholds = NULL;
79
80char *expect = NULL;
81
82regex_t expect_re;
83char *expect_re_str = NULL;
84int expect_re_cflags = 0;
85
86np_dbi_metric_t metric = METRIC_QUERY_RESULT;
87np_dbi_type_t type = TYPE_NUMERIC;
88 64
89char *np_dbi_driver = NULL; 65static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
90driver_option_t *np_dbi_options = NULL; 66static check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper /*config_wrapper*/);
91int np_dbi_options_num = 0; 67void print_usage(void);
92char *np_dbi_database = NULL; 68static void print_help(void);
93char *np_dbi_query = NULL;
94 69
95int process_arguments (int, char **); 70static double timediff(struct timeval /*start*/, struct timeval /*end*/);
96int validate_arguments (void);
97void print_usage (void);
98void print_help (void);
99 71
100double timediff (struct timeval, struct timeval); 72static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...);
101 73
102void np_dbi_print_error (dbi_conn, char *, ...); 74static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/,
75 double * /*res_val*/, double * /*res_time*/, mp_dbi_metric /*metric*/,
76 mp_dbi_type /*type*/, char * /*np_dbi_query*/);
103 77
104int do_query (dbi_conn, const char **, double *, double *); 78int main(int argc, char **argv) {
105
106int
107main (int argc, char **argv)
108{
109 int status = STATE_UNKNOWN; 79 int status = STATE_UNKNOWN;
110 80
111 dbi_driver driver; 81 dbi_driver driver;
@@ -113,714 +83,762 @@ main (int argc, char **argv)
113 83
114 unsigned int server_version; 84 unsigned int server_version;
115 85
116 struct timeval start_timeval, end_timeval; 86 struct timeval start_timeval;
87 struct timeval end_timeval;
117 double conn_time = 0.0; 88 double conn_time = 0.0;
118 double query_time = 0.0; 89 double query_time = 0.0;
119 90
120 const char *query_val_str = NULL; 91 const char *query_val_str = NULL;
121 double query_val = 0.0; 92 double query_val = 0.0;
122 93
123 int i; 94 setlocale(LC_ALL, "");
124 95 bindtextdomain(PACKAGE, LOCALEDIR);
125 setlocale (LC_ALL, ""); 96 textdomain(PACKAGE);
126 bindtextdomain (PACKAGE, LOCALEDIR);
127 textdomain (PACKAGE);
128 97
129 /* Parse extra opts if any */ 98 /* Parse extra opts if any */
130 argv = np_extra_opts (&argc, argv, progname); 99 argv = np_extra_opts(&argc, argv, progname);
100
101 check_dbi_config_wrapper tmp = process_arguments(argc, argv);
102
103 if (tmp.errorcode == ERROR) {
104 usage4(_("Could not parse arguments"));
105 }
131 106
132 if (process_arguments (argc, argv) == ERROR) 107 const check_dbi_config config = tmp.config;
133 usage4 (_("Could not parse arguments"));
134 108
135 /* Set signal handling and alarm */ 109 /* Set signal handling and alarm */
136 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 110 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
137 usage4 (_("Cannot catch SIGALRM")); 111 usage4(_("Cannot catch SIGALRM"));
138 } 112 }
139 alarm (timeout_interval); 113 alarm(timeout_interval);
140 114
141 if (verbose > 2) 115 if (verbose > 2) {
142 printf ("Initializing DBI\n"); 116 printf("Initializing DBI\n");
117 }
143 118
144 dbi_inst *instance_p = { 0 }; 119 dbi_inst *instance_p = {0};
145 120
146 if (dbi_initialize_r(NULL, instance_p) < 0) { 121 if (dbi_initialize_r(NULL, instance_p) < 0) {
147 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");
148 return STATE_UNKNOWN; 124 return STATE_UNKNOWN;
149 } 125 }
150 126
151 if (instance_p == NULL) { 127 if (instance_p == NULL) {
152 printf ("UNKNOWN - failed to initialize DBI.\n"); 128 printf("UNKNOWN - failed to initialize DBI.\n");
153 return STATE_UNKNOWN; 129 return STATE_UNKNOWN;
154 } 130 }
155 131
156 if (verbose) 132 if (verbose) {
157 printf ("Opening DBI driver '%s'\n", np_dbi_driver); 133 printf("Opening DBI driver '%s'\n", config.dbi_driver);
134 }
158 135
159 driver = dbi_driver_open_r(np_dbi_driver, instance_p); 136 driver = dbi_driver_open_r(config.dbi_driver, instance_p);
160 if (! driver) { 137 if (!driver) {
161 printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", 138 printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
162 np_dbi_driver); 139 config.dbi_driver);
163 140
164 printf ("Known drivers:\n"); 141 printf("Known drivers:\n");
165 for (driver = dbi_driver_list_r(NULL, instance_p); driver; driver = dbi_driver_list_r(driver, instance_p)) { 142 for (driver = dbi_driver_list_r(NULL, instance_p); driver;
166 printf (" - %s\n", dbi_driver_get_name (driver)); 143 driver = dbi_driver_list_r(driver, instance_p)) {
144 printf(" - %s\n", dbi_driver_get_name(driver));
167 } 145 }
168 return STATE_UNKNOWN; 146 return STATE_UNKNOWN;
169 } 147 }
170 148
171 /* make a connection to the database */ 149 /* make a connection to the database */
172 gettimeofday (&start_timeval, NULL); 150 gettimeofday(&start_timeval, NULL);
173 151
174 conn = dbi_conn_open (driver); 152 conn = dbi_conn_open(driver);
175 if (! conn) { 153 if (!conn) {
176 printf ("UNKNOWN - failed top open connection object.\n"); 154 printf("UNKNOWN - failed top open connection object.\n");
177 dbi_conn_close (conn); 155 dbi_conn_close(conn);
178 return STATE_UNKNOWN; 156 return STATE_UNKNOWN;
179 } 157 }
180 158
181 for (i = 0; i < np_dbi_options_num; ++i) { 159 for (size_t i = 0; i < config.dbi_options_num; ++i) {
182 const char *opt; 160 const char *opt;
183 161
184 if (verbose > 1) 162 if (verbose > 1) {
185 printf ("Setting DBI driver option '%s' to '%s'\n", 163 printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key,
186 np_dbi_options[i].key, np_dbi_options[i].value); 164 config.dbi_options[i].value);
165 }
187 166
188 if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value)) 167 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) {
189 continue; 168 continue;
169 }
190 /* else: status != 0 */ 170 /* else: status != 0 */
191 171
192 np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'", 172 np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'",
193 np_dbi_options[i].key, np_dbi_options[i].value); 173 config.dbi_options[i].key, config.dbi_options[i].value);
194 printf ("Known driver options:\n"); 174 printf("Known driver options:\n");
195 175
196 for (opt = dbi_conn_get_option_list (conn, NULL); opt; 176 for (opt = dbi_conn_get_option_list(conn, NULL); opt;
197 opt = dbi_conn_get_option_list (conn, opt)) { 177 opt = dbi_conn_get_option_list(conn, opt)) {
198 printf (" - %s\n", opt); 178 printf(" - %s\n", opt);
199 } 179 }
200 dbi_conn_close (conn); 180 dbi_conn_close(conn);
201 return STATE_UNKNOWN; 181 return STATE_UNKNOWN;
202 } 182 }
203 183
204 if (host) { 184 if (config.host) {
205 if (verbose > 1) 185 if (verbose > 1) {
206 printf ("Setting DBI driver option 'host' to '%s'\n", host); 186 printf("Setting DBI driver option 'host' to '%s'\n", config.host);
207 dbi_conn_set_option (conn, "host", host); 187 }
188 dbi_conn_set_option(conn, "host", config.host);
208 } 189 }
209 190
210 if (verbose) { 191 if (verbose) {
211 const char *dbname, *host; 192 const char *dbname;
193 const char *host;
212 194
213 dbname = dbi_conn_get_option (conn, "dbname"); 195 dbname = dbi_conn_get_option(conn, "dbname");
214 host = dbi_conn_get_option (conn, "host"); 196 host = dbi_conn_get_option(conn, "host");
215 197
216 if (! dbname) 198 if (!dbname) {
217 dbname = "<unspecified>"; 199 dbname = "<unspecified>";
218 if (! host) 200 }
201 if (!host) {
219 host = "<unspecified>"; 202 host = "<unspecified>";
203 }
220 204
221 printf ("Connecting to database '%s' at host '%s'\n", 205 printf("Connecting to database '%s' at host '%s'\n", dbname, host);
222 dbname, host);
223 } 206 }
224 207
225 if (dbi_conn_connect (conn) < 0) { 208 if (dbi_conn_connect(conn) < 0) {
226 np_dbi_print_error (conn, "UNKNOWN - failed to connect to database"); 209 np_dbi_print_error(conn, "UNKNOWN - failed to connect to database");
227 return STATE_UNKNOWN; 210 return STATE_UNKNOWN;
228 } 211 }
229 212
230 gettimeofday (&end_timeval, NULL); 213 gettimeofday(&end_timeval, NULL);
231 conn_time = timediff (start_timeval, end_timeval); 214 conn_time = timediff(start_timeval, end_timeval);
232 215
233 server_version = dbi_conn_get_engine_version (conn); 216 server_version = dbi_conn_get_engine_version(conn);
234 if (verbose) 217 if (verbose) {
235 printf ("Connected to server version %u\n", server_version); 218 printf("Connected to server version %u\n", server_version);
219 }
236 220
237 if (metric == METRIC_SERVER_VERSION) 221 if (config.metric == METRIC_SERVER_VERSION) {
238 status = get_status (server_version, dbi_thresholds); 222 status = get_status(server_version, config.dbi_thresholds);
223 }
239 224
240 if (verbose) 225 if (verbose) {
241 printf ("Time elapsed: %f\n", conn_time); 226 printf("Time elapsed: %f\n", conn_time);
227 }
242 228
243 if (metric == METRIC_CONN_TIME) 229 if (config.metric == METRIC_CONN_TIME) {
244 status = get_status (conn_time, dbi_thresholds); 230 status = get_status(conn_time, config.dbi_thresholds);
231 }
245 232
246 /* select a database */ 233 /* select a database */
247 if (np_dbi_database) { 234 if (config.dbi_database) {
248 if (verbose > 1) 235 if (verbose > 1) {
249 printf ("Selecting database '%s'\n", np_dbi_database); 236 printf("Selecting database '%s'\n", config.dbi_database);
237 }
250 238
251 if (dbi_conn_select_db (conn, np_dbi_database)) { 239 if (dbi_conn_select_db(conn, config.dbi_database)) {
252 np_dbi_print_error (conn, "UNKNOWN - failed to select database '%s'", 240 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'",
253 np_dbi_database); 241 config.dbi_database);
254 return STATE_UNKNOWN; 242 return STATE_UNKNOWN;
255 } 243 }
256 } 244 }
257 245
258 if (np_dbi_query) { 246 if (config.dbi_query) {
259 /* execute query */ 247 /* execute query */
260 status = do_query (conn, &query_val_str, &query_val, &query_time); 248 status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type,
261 if (status != STATE_OK) 249 config.dbi_query);
250 if (status != STATE_OK) {
262 /* do_query prints an error message in this case */ 251 /* do_query prints an error message in this case */
263 return status; 252 return status;
253 }
264 254
265 if (metric == METRIC_QUERY_RESULT) { 255 if (config.metric == METRIC_QUERY_RESULT) {
266 if (expect) { 256 if (config.expect) {
267 if ((! query_val_str) || strcmp (query_val_str, expect)) 257 if ((!query_val_str) || strcmp(query_val_str, config.expect)) {
268 status = STATE_CRITICAL; 258 status = STATE_CRITICAL;
269 else 259 } else {
270 status = STATE_OK; 260 status = STATE_OK;
271 } 261 }
272 else if (expect_re_str) { 262 } else if (config.expect_re_str) {
273 int err; 263 int err;
274 264
275 err = regexec (&expect_re, query_val_str, 0, NULL, /* flags = */ 0); 265 regex_t expect_re = {};
276 if (! err) 266 err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0);
267 if (!err) {
277 status = STATE_OK; 268 status = STATE_OK;
278 else if (err == REG_NOMATCH) 269 } else if (err == REG_NOMATCH) {
279 status = STATE_CRITICAL; 270 status = STATE_CRITICAL;
280 else { 271 } else {
281 char errmsg[1024]; 272 char errmsg[1024];
282 regerror (err, &expect_re, errmsg, sizeof (errmsg)); 273 regerror(err, &expect_re, errmsg, sizeof(errmsg));
283 printf ("ERROR - failed to execute regular expression: %s\n", 274 printf("ERROR - failed to execute regular expression: %s\n", errmsg);
284 errmsg);
285 status = STATE_CRITICAL; 275 status = STATE_CRITICAL;
286 } 276 }
277 } else {
278 status = get_status(query_val, config.dbi_thresholds);
287 } 279 }
288 else 280 } else if (config.metric == METRIC_QUERY_TIME) {
289 status = get_status (query_val, dbi_thresholds); 281 status = get_status(query_time, config.dbi_thresholds);
290 } 282 }
291 else if (metric == METRIC_QUERY_TIME)
292 status = get_status (query_time, dbi_thresholds);
293 } 283 }
294 284
295 if (verbose) 285 if (verbose) {
296 printf("Closing connection\n"); 286 printf("Closing connection\n");
297 dbi_conn_close (conn); 287 }
288 dbi_conn_close(conn);
298 289
299 /* 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
300 * which should have been reported and handled (abort) before 291 * which should have been reported and handled (abort) before
301 * ... unless we expected a string to be returned */ 292 * ... unless we expected a string to be returned */
302 assert ((metric != METRIC_QUERY_RESULT) || (! isnan (query_val)) 293 assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) ||
303 || (type == TYPE_STRING)); 294 (config.type == TYPE_STRING));
304 295
305 assert ((type != TYPE_STRING) || (expect || expect_re_str)); 296 assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str));
306 297
307 printf ("%s - connection time: %fs", state_text (status), conn_time); 298 printf("%s - connection time: %fs", state_text(status), conn_time);
308 if (np_dbi_query) { 299 if (config.dbi_query) {
309 if (type == TYPE_STRING) { 300 if (config.type == TYPE_STRING) {
310 assert (expect || expect_re_str); 301 assert(config.expect || config.expect_re_str);
311 printf (", '%s' returned '%s' in %fs", np_dbi_query, 302 printf(", '%s' returned '%s' in %fs", config.dbi_query,
312 query_val_str ? query_val_str : "<nothing>", query_time); 303 query_val_str ? query_val_str : "<nothing>", query_time);
313 if (status != STATE_OK) { 304 if (status != STATE_OK) {
314 if (expect) 305 if (config.expect) {
315 printf (" (expected '%s')", expect); 306 printf(" (expected '%s')", config.expect);
316 else if (expect_re_str) 307 } else if (config.expect_re_str) {
317 printf (" (expected regex /%s/%s)", expect_re_str, 308 printf(" (expected regex /%s/%s)", config.expect_re_str,
318 ((expect_re_cflags & REG_ICASE) ? "i" : "")); 309 ((config.expect_re_cflags & REG_ICASE) ? "i" : ""));
310 }
319 } 311 }
312 } else if (isnan(query_val)) {
313 printf(", '%s' query execution time: %fs", config.dbi_query, query_time);
314 } else {
315 printf(", '%s' returned %f in %fs", config.dbi_query, query_val, query_time);
320 } 316 }
321 else if (isnan (query_val)) 317 }
322 printf (", '%s' query execution time: %fs", np_dbi_query, query_time); 318
323 else 319 printf(
324 printf (", '%s' returned %f in %fs", np_dbi_query, query_val, query_time); 320 " | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time,
325 } 321 ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "",
326 322 ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "",
327 printf (" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, 323 server_version,
328 ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "", 324 ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range
329 ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "", 325 : "",
330 server_version, 326 ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range
331 ((metric == METRIC_SERVER_VERSION) && warning_range) ? warning_range : "", 327 : "");
332 ((metric == METRIC_SERVER_VERSION) && critical_range) ? critical_range : ""); 328 if (config.dbi_query) {
333 if (np_dbi_query) { 329 if (!isnan(query_val)) { /* this is also true when -e is used */
334 if (! isnan (query_val)) /* this is also true when -e is used */ 330 printf(" query=%f;%s;%s;;", query_val,
335 printf (" query=%f;%s;%s;;", query_val, 331 ((config.metric == METRIC_QUERY_RESULT) && config.warning_range)
336 ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "", 332 ? config.warning_range
337 ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : ""); 333 : "",
338 printf (" querytime=%fs;%s;%s;0;", query_time, 334 ((config.metric == METRIC_QUERY_RESULT) && config.critical_range)
339 ((metric == METRIC_QUERY_TIME) && warning_range) ? warning_range : "", 335 ? config.critical_range
340 ((metric == METRIC_QUERY_TIME) && critical_range) ? critical_range : ""); 336 : "");
341 } 337 }
342 printf ("\n"); 338 printf(" querytime=%fs;%s;%s;0;", query_time,
339 ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range
340 : "",
341 ((config.metric == METRIC_QUERY_TIME) && config.critical_range)
342 ? config.critical_range
343 : "");
344 }
345 printf("\n");
343 return status; 346 return status;
344} 347}
345 348
346/* process command-line arguments */ 349/* process command-line arguments */
347int 350check_dbi_config_wrapper process_arguments(int argc, char **argv) {
348process_arguments (int argc, char **argv)
349{
350 int c;
351 351
352 int option = 0; 352 int option = 0;
353 static struct option longopts[] = { 353 static struct option longopts[] = {STD_LONG_OPTS,
354 STD_LONG_OPTS, 354
355 355 {"expect", required_argument, 0, 'e'},
356 {"expect", required_argument, 0, 'e'}, 356 {"regex", required_argument, 0, 'r'},
357 {"regex", required_argument, 0, 'r'}, 357 {"regexi", required_argument, 0, 'R'},
358 {"regexi", required_argument, 0, 'R'}, 358 {"metric", required_argument, 0, 'm'},
359 {"metric", required_argument, 0, 'm'}, 359 {"driver", required_argument, 0, 'd'},
360 {"driver", required_argument, 0, 'd'}, 360 {"option", required_argument, 0, 'o'},
361 {"option", required_argument, 0, 'o'}, 361 {"query", required_argument, 0, 'q'},
362 {"query", required_argument, 0, 'q'}, 362 {"database", required_argument, 0, 'D'},
363 {"database", required_argument, 0, 'D'}, 363 {0, 0, 0, 0}};
364 {0, 0, 0, 0} 364
365 check_dbi_config_wrapper result = {
366 .config = check_dbi_config_init(),
367 .errorcode = OK,
365 }; 368 };
369 int option_char;
370 while (true) {
371 option_char = getopt_long(argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:", longopts, &option);
366 372
367 while (1) { 373 if (option_char == EOF) {
368 c = getopt_long (argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:",
369 longopts, &option);
370
371 if (c == EOF)
372 break; 374 break;
375 }
373 376
374 switch (c) { 377 switch (option_char) {
375 case '?': /* usage */ 378 case '?': /* usage */
376 usage5 (); 379 usage5();
377 case 'h': /* help */ 380 case 'h': /* help */
378 print_help (); 381 print_help();
379 exit (STATE_UNKNOWN); 382 exit(STATE_UNKNOWN);
380 case 'V': /* version */ 383 case 'V': /* version */
381 print_revision (progname, NP_VERSION); 384 print_revision(progname, NP_VERSION);
382 exit (STATE_UNKNOWN); 385 exit(STATE_UNKNOWN);
383 386
384 case 'c': /* critical range */ 387 case 'c': /* critical range */
385 critical_range = optarg; 388 result.config.critical_range = optarg;
386 type = TYPE_NUMERIC; 389 result.config.type = TYPE_NUMERIC;
387 break; 390 break;
388 case 'w': /* warning range */ 391 case 'w': /* warning range */
389 warning_range = optarg; 392 result.config.warning_range = optarg;
390 type = TYPE_NUMERIC; 393 result.config.type = TYPE_NUMERIC;
391 break; 394 break;
392 case 'e': 395 case 'e':
393 expect = optarg; 396 result.config.expect = optarg;
394 type = TYPE_STRING; 397 result.config.type = TYPE_STRING;
395 break; 398 break;
396 case 'R': 399 case 'R':
397 expect_re_cflags = REG_ICASE; 400 result.config.expect_re_cflags = REG_ICASE;
398 /* fall through */ 401 /* fall through */
399 case 'r': 402 case 'r': {
400 { 403 int err;
401 int err; 404
402 405 result.config.expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
403 expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 406 result.config.expect_re_str = optarg;
404 expect_re_str = optarg; 407 result.config.type = TYPE_STRING;
405 type = TYPE_STRING; 408
406 409 regex_t expect_re = {};
407 err = regcomp (&expect_re, expect_re_str, expect_re_cflags); 410 err = regcomp(&expect_re, result.config.expect_re_str, result.config.expect_re_cflags);
408 if (err) { 411 if (err) {
409 char errmsg[1024]; 412 char errmsg[1024];
410 regerror (err, &expect_re, errmsg, sizeof (errmsg)); 413 regerror(err, &expect_re, errmsg, sizeof(errmsg));
411 printf ("ERROR - failed to compile regular expression: %s\n", 414 printf("ERROR - failed to compile regular expression: %s\n", errmsg);
412 errmsg); 415
413 return ERROR; 416 result.errorcode = ERROR;
414 } 417 return result;
415 break;
416 } 418 }
419 break;
420 }
417 421
418 case 'm': 422 case 'm':
419 if (! strcasecmp (optarg, "CONN_TIME")) 423 if (!strcasecmp(optarg, "CONN_TIME")) {
420 metric = METRIC_CONN_TIME; 424 result.config.metric = METRIC_CONN_TIME;
421 else if (! strcasecmp (optarg, "SERVER_VERSION")) 425 } else if (!strcasecmp(optarg, "SERVER_VERSION")) {
422 metric = METRIC_SERVER_VERSION; 426 result.config.metric = METRIC_SERVER_VERSION;
423 else if (! strcasecmp (optarg, "QUERY_RESULT")) 427 } else if (!strcasecmp(optarg, "QUERY_RESULT")) {
424 metric = METRIC_QUERY_RESULT; 428 result.config.metric = METRIC_QUERY_RESULT;
425 else if (! strcasecmp (optarg, "QUERY_TIME")) 429 } else if (!strcasecmp(optarg, "QUERY_TIME")) {
426 metric = METRIC_QUERY_TIME; 430 result.config.metric = METRIC_QUERY_TIME;
427 else 431 } else {
428 usage2 (_("Invalid metric"), optarg); 432 usage2(_("Invalid metric"), optarg);
433 }
429 break; 434 break;
430 case 't': /* timeout */ 435 case 't': /* timeout */
431 if (!is_intnonneg (optarg)) 436 if (!is_intnonneg(optarg)) {
432 usage2 (_("Timeout interval must be a positive integer"), optarg); 437 usage2(_("Timeout interval must be a positive integer"), optarg);
433 else 438 } else {
434 timeout_interval = atoi (optarg); 439 timeout_interval = atoi(optarg);
440 }
435 441
436 break; 442 break;
437 case 'H': /* host */ 443 case 'H': /* host */
438 if (!is_host (optarg)) 444 if (!is_host(optarg)) {
439 usage2 (_("Invalid hostname/address"), optarg); 445 usage2(_("Invalid hostname/address"), optarg);
440 else 446 } else {
441 host = optarg; 447 result.config.host = optarg;
448 }
442 break; 449 break;
443 case 'v': 450 case 'v':
444 verbose++; 451 verbose++;
445 break; 452 break;
446 453
447 case 'd': 454 case 'd':
448 np_dbi_driver = optarg; 455 result.config.dbi_driver = optarg;
449 break; 456 break;
450 case 'o': 457 case 'o': {
451 { 458 driver_option_t *new = NULL;
452 driver_option_t *new;
453
454 char *k, *v;
455 459
456 k = optarg; 460 char *key = optarg;
457 v = strchr (k, (int)'='); 461 char *value = strchr(key, '=');
458 462
459 if (! v) 463 if (!value) {
460 usage2 (_("Option must be '<key>=<value>'"), optarg); 464 usage2(_("Option must be '<key>=<value>'"), optarg);
465 }
461 466
462 *v = '\0'; 467 *value = '\0';
463 ++v; 468 ++value;
464 469
465 new = realloc (np_dbi_options, 470 new = realloc(result.config.dbi_options,
466 (np_dbi_options_num + 1) * sizeof (*new)); 471 (result.config.dbi_options_num + 1) * sizeof(*new));
467 if (! new) { 472 if (!new) {
468 printf ("UNKNOWN - failed to reallocate memory\n"); 473 printf("UNKNOWN - failed to reallocate memory\n");
469 exit (STATE_UNKNOWN); 474 exit(STATE_UNKNOWN);
470 } 475 }
471 476
472 np_dbi_options = new; 477 result.config.dbi_options = new;
473 new = np_dbi_options + np_dbi_options_num; 478 new = result.config.dbi_options + result.config.dbi_options_num;
474 ++np_dbi_options_num; 479 result.config.dbi_options_num++;
475 480
476 new->key = k; 481 new->key = key;
477 new->value = v; 482 new->value = value;
478 } 483 } break;
479 break;
480 case 'q': 484 case 'q':
481 np_dbi_query = optarg; 485 result.config.dbi_query = optarg;
482 break; 486 break;
483 case 'D': 487 case 'D':
484 np_dbi_database = optarg; 488 result.config.dbi_database = optarg;
485 break; 489 break;
486 } 490 }
487 } 491 }
488 492
489 set_thresholds (&dbi_thresholds, warning_range, critical_range); 493 set_thresholds(&result.config.dbi_thresholds, result.config.warning_range,
494 result.config.critical_range);
490 495
491 return validate_arguments (); 496 return validate_arguments(result);
492} 497}
493 498
494int 499check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrapper) {
495validate_arguments () 500 if (!config_wrapper.config.dbi_driver) {
496{ 501 usage("Must specify a DBI driver");
497 if (! np_dbi_driver) 502 }
498 usage ("Must specify a DBI driver");
499 503
500 if (((metric == METRIC_QUERY_RESULT) || (metric == METRIC_QUERY_TIME)) 504 if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) ||
501 && (! np_dbi_query)) 505 (config_wrapper.config.metric == METRIC_QUERY_TIME)) &&
502 usage ("Must specify a query to execute (metric == QUERY_RESULT)"); 506 (!config_wrapper.config.dbi_query)) {
507 usage("Must specify a query to execute (metric == QUERY_RESULT)");
508 }
503 509
504 if ((metric != METRIC_CONN_TIME) 510 if ((config_wrapper.config.metric != METRIC_CONN_TIME) &&
505 && (metric != METRIC_SERVER_VERSION) 511 (config_wrapper.config.metric != METRIC_SERVER_VERSION) &&
506 && (metric != METRIC_QUERY_RESULT) 512 (config_wrapper.config.metric != METRIC_QUERY_RESULT) &&
507 && (metric != METRIC_QUERY_TIME)) 513 (config_wrapper.config.metric != METRIC_QUERY_TIME)) {
508 usage ("Invalid metric specified"); 514 usage("Invalid metric specified");
515 }
509 516
510 if (expect && (warning_range || critical_range || expect_re_str)) 517 if (config_wrapper.config.expect &&
511 usage ("Do not mix -e and -w/-c/-r/-R"); 518 (config_wrapper.config.warning_range || config_wrapper.config.critical_range ||
519 config_wrapper.config.expect_re_str)) {
520 usage("Do not mix -e and -w/-c/-r/-R");
521 }
512 522
513 if (expect_re_str && (warning_range || critical_range || expect)) 523 if (config_wrapper.config.expect_re_str &&
514 usage ("Do not mix -r/-R and -w/-c/-e"); 524 (config_wrapper.config.warning_range || config_wrapper.config.critical_range ||
525 config_wrapper.config.expect)) {
526 usage("Do not mix -r/-R and -w/-c/-e");
527 }
515 528
516 if (expect && (metric != METRIC_QUERY_RESULT)) 529 if (config_wrapper.config.expect && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) {
517 usage ("Option -e requires metric QUERY_RESULT"); 530 usage("Option -e requires metric QUERY_RESULT");
531 }
518 532
519 if (expect_re_str && (metric != METRIC_QUERY_RESULT)) 533 if (config_wrapper.config.expect_re_str &&
520 usage ("Options -r/-R require metric QUERY_RESULT"); 534 (config_wrapper.config.metric != METRIC_QUERY_RESULT)) {
535 usage("Options -r/-R require metric QUERY_RESULT");
536 }
521 537
522 return OK; 538 config_wrapper.errorcode = OK;
539 return config_wrapper;
523} 540}
524 541
525void 542void print_help(void) {
526print_help (void) 543 print_revision(progname, NP_VERSION);
527{
528 print_revision (progname, NP_VERSION);
529 544
530 printf (COPYRIGHT, copyright, email); 545 printf(COPYRIGHT, copyright, email);
531 546
532 printf (_("This program connects to an (SQL) database using DBI and checks the\n" 547 printf(_("This program connects to an (SQL) database using DBI and checks the\n"
533 "specified metric against threshold levels. The default metric is\n" 548 "specified metric against threshold levels. The default metric is\n"
534 "the result of the specified query.\n")); 549 "the result of the specified query.\n"));
535 550
536 printf ("\n\n"); 551 printf("\n\n");
537 552
538 print_usage (); 553 print_usage();
539 554
540 printf (UT_HELP_VRSN); 555 printf(UT_HELP_VRSN);
541/* include this conditionally to avoid 'zero-length printf format string' 556/* include this conditionally to avoid 'zero-length printf format string'
542 * compiler warnings */ 557 * compiler warnings */
543#ifdef NP_EXTRA_OPTS 558#ifdef NP_EXTRA_OPTS
544 printf (UT_EXTRA_OPTS); 559 printf(UT_EXTRA_OPTS);
545#endif 560#endif
546 printf ("\n"); 561 printf("\n");
547 562
548 printf (" %s\n", "-d, --driver=STRING"); 563 printf(" %s\n", "-d, --driver=STRING");
549 printf (" %s\n", _("DBI driver to use")); 564 printf(" %s\n", _("DBI driver to use"));
550 printf (" %s\n", "-o, --option=STRING"); 565 printf(" %s\n", "-o, --option=STRING");
551 printf (" %s\n", _("DBI driver options")); 566 printf(" %s\n", _("DBI driver options"));
552 printf (" %s\n", "-q, --query=STRING"); 567 printf(" %s\n", "-q, --query=STRING");
553 printf (" %s\n", _("query to execute")); 568 printf(" %s\n", _("query to execute"));
554 printf ("\n"); 569 printf(" %s\n", "-H STRING");
555 570 printf(" %s\n", _("target database host"));
556 printf (UT_WARN_CRIT_RANGE); 571 printf("\n");
557 printf (" %s\n", "-e, --expect=STRING"); 572
558 printf (" %s\n", _("String to expect as query result")); 573 printf(UT_WARN_CRIT_RANGE);
559 printf (" %s\n", _("Do not mix with -w, -c, -r, or -R!")); 574 printf(" %s\n", "-e, --expect=STRING");
560 printf (" %s\n", "-r, --regex=REGEX"); 575 printf(" %s\n", _("String to expect as query result"));
561 printf (" %s\n", _("Extended POSIX regular expression to check query result against")); 576 printf(" %s\n", _("Do not mix with -w, -c, -r, or -R!"));
562 printf (" %s\n", _("Do not mix with -w, -c, -e, or -R!")); 577 printf(" %s\n", "-r, --regex=REGEX");
563 printf (" %s\n", "-R, --regexi=REGEX"); 578 printf(" %s\n", _("Extended POSIX regular expression to check query result against"));
564 printf (" %s\n", _("Case-insensitive extended POSIX regex to check query result against")); 579 printf(" %s\n", _("Do not mix with -w, -c, -e, or -R!"));
565 printf (" %s\n", _("Do not mix with -w, -c, -e, or -r!")); 580 printf(" %s\n", "-R, --regexi=REGEX");
566 printf (" %s\n", "-m, --metric=METRIC"); 581 printf(" %s\n", _("Case-insensitive extended POSIX regex to check query result against"));
567 printf (" %s\n", _("Metric to check thresholds against. Available metrics:")); 582 printf(" %s\n", _("Do not mix with -w, -c, -e, or -r!"));
568 printf (" CONN_TIME - %s\n", _("time used for setting up the database connection")); 583 printf(" %s\n", "-m, --metric=METRIC");
569 printf (" QUERY_RESULT - %s\n", _("result (first column of first row) of the query")); 584 printf(" %s\n", _("Metric to check thresholds against. Available metrics:"));
570 printf (" QUERY_TIME - %s\n", _("time used to execute the query")); 585 printf(" CONN_TIME - %s\n", _("time used for setting up the database connection"));
571 printf (" %s\n", _("(ignore the query result)")); 586 printf(" QUERY_RESULT - %s\n", _("result (first column of first row) of the query"));
572 printf ("\n"); 587 printf(" QUERY_TIME - %s\n", _("time used to execute the query"));
573 588 printf(" %s\n", _("(ignore the query result)"));
574 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 589 printf("\n");
575 590
576 printf (UT_VERBOSE); 591 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
577 592
578 printf ("\n"); 593 printf(UT_VERBOSE);
579 printf (" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates")); 594
580 printf (" %s\n\n", _("on a query, one has to be specified (-q option).")); 595 printf("\n");
581 596 printf(" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates"));
582 printf (" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,")); 597 printf(" %s\n\n", _("on a query, one has to be specified (-q option)."));
583 printf (" %s\n", _("executes the specified query. The first column of the first row of the")); 598
584 printf (" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the")); 599 printf(" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,"));
585 printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric")); 600 printf(" %s\n", _("executes the specified query. The first column of the first row of the"));
586 printf (" %s\n\n", _("(strings representing numbers are fine).")); 601 printf(" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the"));
587 602 printf(" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
588 printf (" %s\n", _("The number and type of required DBI driver options depends on the actual")); 603 printf(" %s\n\n", _("(strings representing numbers are fine)."));
589 printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/")); 604
590 printf (" %s\n\n", _("for details.")); 605 printf(" %s\n", _("The number and type of required DBI driver options depends on the actual"));
591 606 printf(" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
592 printf (" %s\n", _("Examples:")); 607 printf(" %s\n\n", _("for details."));
593 printf (" check_dbi -d pgsql -o username=postgres -m QUERY_RESULT \\\n"); 608
594 printf (" -q 'SELECT COUNT(*) FROM pg_stat_activity' -w 5 -c 10\n"); 609 printf(" %s\n", _("Examples:"));
595 printf (" Warning if more than five connections; critical if more than ten.\n\n"); 610 printf(" check_dbi -d pgsql -o username=postgres -m QUERY_RESULT \\\n");
596 611 printf(" -q 'SELECT COUNT(*) FROM pg_stat_activity' -w 5 -c 10\n");
597 printf (" check_dbi -d mysql -H localhost -o username=user -o password=secret \\\n"); 612 printf(" Warning if more than five connections; critical if more than ten.\n\n");
598 printf (" -q 'SELECT COUNT(*) FROM logged_in_users -w 5:20 -c 0:50\n"); 613
599 printf (" Warning if less than 5 or more than 20 users are logged in; critical\n"); 614 printf(" check_dbi -d mysql -H localhost -o username=user -o password=secret \\\n");
600 printf (" if more than 50 users.\n\n"); 615 printf(" -q 'SELECT COUNT(*) FROM logged_in_users -w 5:20 -c 0:50\n");
601 616 printf(" Warning if less than 5 or more than 20 users are logged in; critical\n");
602 printf (" check_dbi -d firebird -o username=user -o password=secret -o dbname=foo \\\n"); 617 printf(" if more than 50 users.\n\n");
603 printf (" -m CONN_TIME -w 0.5 -c 2\n"); 618
604 printf (" Warning if connecting to the database takes more than half of a second;\n"); 619 printf(" check_dbi -d firebird -o username=user -o password=secret -o dbname=foo \\\n");
605 printf (" critical if it takes more than 2 seconds.\n\n"); 620 printf(" -m CONN_TIME -w 0.5 -c 2\n");
606 621 printf(" Warning if connecting to the database takes more than half of a second;\n");
607 printf (" check_dbi -d mysql -H localhost -o username=user \\\n"); 622 printf(" critical if it takes more than 2 seconds.\n\n");
608 printf (" -q 'SELECT concat(@@version, \" \", @@version_comment)' \\\n"); 623
609 printf (" -r '^5\\.[01].*MySQL Enterprise Server'\n"); 624 printf(" check_dbi -d mysql -H localhost -o username=user \\\n");
610 printf (" Critical if the database server is not a MySQL enterprise server in either\n"); 625 printf(" -q 'SELECT concat(@@version, \" \", @@version_comment)' \\\n");
611 printf (" version 5.0.x or 5.1.x.\n\n"); 626 printf(" -r '^5\\.[01].*MySQL Enterprise Server'\n");
612 627 printf(" Critical if the database server is not a MySQL enterprise server in either\n");
613 printf (" check_dbi -d pgsql -u username=user -m SERVER_VERSION \\\n"); 628 printf(" version 5.0.x or 5.1.x.\n\n");
614 printf (" -w 090000:090099 -c 090000:090199\n"); 629
615 printf (" Warn if the PostgreSQL server version is not 9.0.x; critical if the version\n"); 630 printf(" check_dbi -d pgsql -u username=user -m SERVER_VERSION \\\n");
616 printf (" is less than 9.x or higher than 9.1.x.\n"); 631 printf(" -w 090000:090099 -c 090000:090199\n");
617 632 printf(" Warn if the PostgreSQL server version is not 9.0.x; critical if the version\n");
618 printf (UT_SUPPORT); 633 printf(" is less than 9.x or higher than 9.1.x.\n");
634
635 printf(UT_SUPPORT);
619} 636}
620 637
621void 638void print_usage(void) {
622print_usage (void) 639 printf("%s\n", _("Usage:"));
623{ 640 printf("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname);
624 printf ("%s\n", _("Usage:")); 641 printf(" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
625 printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname); 642 printf(" [-e <string>] [-r|-R <regex>]\n");
626 printf (" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
627 printf (" [-e <string>] [-r|-R <regex>]\n");
628} 643}
629 644
630#define CHECK_IGNORE_ERROR(s) \ 645const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type,
631 do { \ 646 mp_dbi_metric metric, mp_dbi_type type) {
632 if (metric != METRIC_QUERY_RESULT) \
633 return (s); \
634 } while (0)
635
636const char *
637get_field_str (dbi_conn conn, dbi_result res, unsigned short field_type)
638{
639 const char *str; 647 const char *str;
640 648
641 if (field_type != DBI_TYPE_STRING) { 649 if (field_type != DBI_TYPE_STRING) {
642 printf ("CRITICAL - result value is not a string\n"); 650 printf("CRITICAL - result value is not a string\n");
643 return NULL; 651 return NULL;
644 } 652 }
645 653
646 str = dbi_result_get_string_idx (res, 1); 654 str = dbi_result_get_string_idx(res, 1);
647 if ((! str) || (strcmp (str, "ERROR") == 0)) { 655 if ((!str) || (strcmp(str, "ERROR") == 0)) {
648 CHECK_IGNORE_ERROR (NULL); 656 if (metric != METRIC_QUERY_RESULT) {
649 np_dbi_print_error (conn, "CRITICAL - failed to fetch string value"); 657 return NULL;
658 }
659 np_dbi_print_error(conn, "CRITICAL - failed to fetch string value");
650 return NULL; 660 return NULL;
651 } 661 }
652 662
653 if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) 663 if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) {
654 printf ("Query returned string '%s'\n", str); 664 printf("Query returned string '%s'\n", str);
665 }
655 return str; 666 return str;
656} 667}
657 668
658double 669double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric,
659get_field (dbi_conn conn, dbi_result res, unsigned short *field_type) 670 mp_dbi_type type) {
660{
661 double val = NAN; 671 double val = NAN;
662 672
663 if (*field_type == DBI_TYPE_INTEGER) { 673 if (*field_type == DBI_TYPE_INTEGER) {
664 val = (double)dbi_result_get_longlong_idx (res, 1); 674 val = (double)dbi_result_get_longlong_idx(res, 1);
665 } 675 } else if (*field_type == DBI_TYPE_DECIMAL) {
666 else if (*field_type == DBI_TYPE_DECIMAL) { 676 val = dbi_result_get_double_idx(res, 1);
667 val = dbi_result_get_double_idx (res, 1); 677 } else if (*field_type == DBI_TYPE_STRING) {
668 }
669 else if (*field_type == DBI_TYPE_STRING) {
670 const char *val_str; 678 const char *val_str;
671 char *endptr = NULL; 679 char *endptr = NULL;
672 680
673 val_str = get_field_str (conn, res, *field_type); 681 val_str = get_field_str(conn, res, *field_type, metric, type);
674 if (! val_str) { 682 if (!val_str) {
675 CHECK_IGNORE_ERROR (NAN); 683 if (metric != METRIC_QUERY_RESULT) {
684 return NAN;
685 }
676 *field_type = DBI_TYPE_ERROR; 686 *field_type = DBI_TYPE_ERROR;
677 return NAN; 687 return NAN;
678 } 688 }
679 689
680 val = strtod (val_str, &endptr); 690 val = strtod(val_str, &endptr);
681 if (endptr == val_str) { 691 if (endptr == val_str) {
682 CHECK_IGNORE_ERROR (NAN); 692 if (metric != METRIC_QUERY_RESULT) {
683 printf ("CRITICAL - result value is not a numeric: %s\n", val_str); 693 return NAN;
694 }
695 printf("CRITICAL - result value is not a numeric: %s\n", val_str);
684 *field_type = DBI_TYPE_ERROR; 696 *field_type = DBI_TYPE_ERROR;
685 return NAN; 697 return NAN;
686 } 698 }
687 else if ((endptr != NULL) && (*endptr != '\0')) { 699 if ((endptr != NULL) && (*endptr != '\0')) {
688 if (verbose) 700 if (verbose) {
689 printf ("Garbage after value: %s\n", endptr); 701 printf("Garbage after value: %s\n", endptr);
702 }
690 } 703 }
691 } 704 } else {
692 else { 705 if (metric != METRIC_QUERY_RESULT) {
693 CHECK_IGNORE_ERROR (NAN); 706 return NAN;
694 printf ("CRITICAL - cannot parse value of type %s (%i)\n", 707 }
695 (*field_type == DBI_TYPE_BINARY) 708 printf("CRITICAL - cannot parse value of type %s (%i)\n",
696 ? "BINARY" 709 (*field_type == DBI_TYPE_BINARY) ? "BINARY"
697 : (*field_type == DBI_TYPE_DATETIME) 710 : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME"
698 ? "DATETIME" 711 : "<unknown>",
699 : "<unknown>", 712 *field_type);
700 *field_type);
701 *field_type = DBI_TYPE_ERROR; 713 *field_type = DBI_TYPE_ERROR;
702 return NAN; 714 return NAN;
703 } 715 }
704 return val; 716 return val;
705} 717}
706 718
707double 719mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str,
708get_query_result (dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val) 720 double *res_val, mp_dbi_metric metric, mp_dbi_type type) {
709{
710 unsigned short field_type; 721 unsigned short field_type;
711 double val = NAN; 722 double val = NAN;
712 723
713 if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) { 724 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
714 CHECK_IGNORE_ERROR (STATE_OK); 725 if (metric != METRIC_QUERY_RESULT) {
715 np_dbi_print_error (conn, "CRITICAL - failed to fetch rows"); 726 return STATE_OK;
727 }
728 np_dbi_print_error(conn, "CRITICAL - failed to fetch rows");
716 return STATE_CRITICAL; 729 return STATE_CRITICAL;
717 } 730 }
718 731
719 if (dbi_result_get_numrows (res) < 1) { 732 if (dbi_result_get_numrows(res) < 1) {
720 CHECK_IGNORE_ERROR (STATE_OK); 733 if (metric != METRIC_QUERY_RESULT) {
721 printf ("WARNING - no rows returned\n"); 734 return STATE_OK;
735 }
736 printf("WARNING - no rows returned\n");
722 return STATE_WARNING; 737 return STATE_WARNING;
723 } 738 }
724 739
725 if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) { 740 if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) {
726 CHECK_IGNORE_ERROR (STATE_OK); 741 if (metric != METRIC_QUERY_RESULT) {
727 np_dbi_print_error (conn, "CRITICAL - failed to fetch fields"); 742 return STATE_OK;
743 }
744 np_dbi_print_error(conn, "CRITICAL - failed to fetch fields");
728 return STATE_CRITICAL; 745 return STATE_CRITICAL;
729 } 746 }
730 747
731 if (dbi_result_get_numfields (res) < 1) { 748 if (dbi_result_get_numfields(res) < 1) {
732 CHECK_IGNORE_ERROR (STATE_OK); 749 if (metric != METRIC_QUERY_RESULT) {
733 printf ("WARNING - no fields returned\n"); 750 return STATE_OK;
751 }
752 printf("WARNING - no fields returned\n");
734 return STATE_WARNING; 753 return STATE_WARNING;
735 } 754 }
736 755
737 if (dbi_result_first_row (res) != 1) { 756 if (dbi_result_first_row(res) != 1) {
738 CHECK_IGNORE_ERROR (STATE_OK); 757 if (metric != METRIC_QUERY_RESULT) {
739 np_dbi_print_error (conn, "CRITICAL - failed to fetch first row"); 758 return STATE_OK;
759 }
760 np_dbi_print_error(conn, "CRITICAL - failed to fetch first row");
740 return STATE_CRITICAL; 761 return STATE_CRITICAL;
741 } 762 }
742 763
743 field_type = dbi_result_get_field_type_idx (res, 1); 764 field_type = dbi_result_get_field_type_idx(res, 1);
744 if (field_type != DBI_TYPE_ERROR) { 765 if (field_type != DBI_TYPE_ERROR) {
745 if (type == TYPE_STRING) 766 if (type == TYPE_STRING) {
746 /* the value will be freed in dbi_result_free */ 767 /* the value will be freed in dbi_result_free */
747 *res_val_str = strdup (get_field_str (conn, res, field_type)); 768 *res_val_str = strdup(get_field_str(conn, res, field_type, metric, type));
748 else 769 } else {
749 val = get_field (conn, res, &field_type); 770 val = get_field(conn, res, &field_type, metric, type);
771 }
750 } 772 }
751 773
752 *res_val = val; 774 *res_val = val;
753 775
754 if (field_type == DBI_TYPE_ERROR) { 776 if (field_type == DBI_TYPE_ERROR) {
755 CHECK_IGNORE_ERROR (STATE_OK); 777 if (metric != METRIC_QUERY_RESULT) {
756 np_dbi_print_error (conn, "CRITICAL - failed to fetch data"); 778 return STATE_OK;
779 }
780 np_dbi_print_error(conn, "CRITICAL - failed to fetch data");
757 return STATE_CRITICAL; 781 return STATE_CRITICAL;
758 } 782 }
759 783
760 dbi_result_free (res); 784 dbi_result_free(res);
761 return STATE_OK; 785 return STATE_OK;
762} 786}
763 787
764#undef CHECK_IGNORE_ERROR 788mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time,
765 789 mp_dbi_metric metric, mp_dbi_type type, char *np_dbi_query) {
766int
767do_query (dbi_conn conn, const char **res_val_str, double *res_val, double *res_time)
768{
769 dbi_result res; 790 dbi_result res;
770 791
771 struct timeval timeval_start, timeval_end; 792 struct timeval timeval_start;
772 int status = STATE_OK; 793 struct timeval timeval_end;
794 mp_state_enum status = STATE_OK;
773 795
774 assert (np_dbi_query); 796 assert(np_dbi_query);
775 797
776 if (verbose) 798 if (verbose) {
777 printf ("Executing query '%s'\n", np_dbi_query); 799 printf("Executing query '%s'\n", np_dbi_query);
800 }
778 801
779 gettimeofday (&timeval_start, NULL); 802 gettimeofday(&timeval_start, NULL);
780 803
781 res = dbi_conn_query (conn, np_dbi_query); 804 res = dbi_conn_query(conn, np_dbi_query);
782 if (! res) { 805 if (!res) {
783 np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); 806 np_dbi_print_error(conn, "CRITICAL - failed to execute query '%s'", np_dbi_query);
784 return STATE_CRITICAL; 807 return STATE_CRITICAL;
785 } 808 }
786 809
787 status = get_query_result (conn, res, res_val_str, res_val); 810 status = get_query_result(conn, res, res_val_str, res_val, metric, type);
788 811
789 gettimeofday (&timeval_end, NULL); 812 gettimeofday(&timeval_end, NULL);
790 *res_time = timediff (timeval_start, timeval_end); 813 *res_time = timediff(timeval_start, timeval_end);
791 814
792 if (verbose) 815 if (verbose) {
793 printf ("Time elapsed: %f\n", *res_time); 816 printf("Time elapsed: %f\n", *res_time);
817 }
794 818
795 return status; 819 return status;
796} 820}
797 821
798double 822double timediff(struct timeval start, struct timeval end) {
799timediff (struct timeval start, struct timeval end)
800{
801 double diff; 823 double diff;
802 824
803 while (start.tv_usec > end.tv_usec) { 825 while (start.tv_usec > end.tv_usec) {
804 --end.tv_sec; 826 --end.tv_sec;
805 end.tv_usec += 1000000; 827 end.tv_usec += 1000000;
806 } 828 }
807 diff = (double)(end.tv_sec - start.tv_sec) 829 diff = (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
808 + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
809 return diff; 830 return diff;
810} 831}
811 832
812void 833void np_dbi_print_error(dbi_conn conn, char *fmt, ...) {
813np_dbi_print_error (dbi_conn conn, char *fmt, ...)
814{
815 const char *errmsg = NULL; 834 const char *errmsg = NULL;
816 va_list ap; 835 va_list ap;
817 836
818 va_start (ap, fmt); 837 va_start(ap, fmt);
819 838
820 dbi_conn_error (conn, &errmsg); 839 dbi_conn_error(conn, &errmsg);
821 vprintf (fmt, ap); 840 vprintf(fmt, ap);
822 printf (": %s\n", errmsg); 841 printf(": %s\n", errmsg);
823 842
824 va_end (ap); 843 va_end(ap);
825} 844}
826
diff --git a/plugins/check_dbi.d/config.h b/plugins/check_dbi.d/config.h
new file mode 100644
index 00000000..f6f0d7b3
--- /dev/null
+++ b/plugins/check_dbi.d/config.h
@@ -0,0 +1,63 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include "../../lib/monitoringplug.h"
6
7typedef enum {
8 METRIC_CONN_TIME,
9 METRIC_SERVER_VERSION,
10 METRIC_QUERY_RESULT,
11 METRIC_QUERY_TIME,
12} mp_dbi_metric;
13
14typedef enum {
15 TYPE_NUMERIC,
16 TYPE_STRING,
17} mp_dbi_type;
18
19typedef struct {
20 char *key;
21 char *value;
22} driver_option_t;
23
24typedef struct {
25 char *dbi_driver;
26 char *host;
27 driver_option_t *dbi_options;
28 size_t dbi_options_num;
29 char *dbi_database;
30 char *dbi_query;
31
32 char *expect;
33 char *expect_re_str;
34 int expect_re_cflags;
35 mp_dbi_metric metric;
36 mp_dbi_type type;
37 char *warning_range;
38 char *critical_range;
39 thresholds *dbi_thresholds;
40
41} check_dbi_config;
42
43check_dbi_config check_dbi_config_init() {
44 check_dbi_config tmp = {
45 .dbi_driver = NULL,
46 .host = NULL,
47 .dbi_options = NULL,
48 .dbi_options_num = 0,
49 .dbi_database = NULL,
50 .dbi_query = NULL,
51
52 .expect = NULL,
53 .expect_re_str = NULL,
54 .expect_re_cflags = 0,
55 .metric = METRIC_QUERY_RESULT,
56 .type = TYPE_NUMERIC,
57
58 .warning_range = NULL,
59 .critical_range = NULL,
60 .dbi_thresholds = NULL,
61 };
62 return tmp;
63}
diff --git a/plugins/check_dig.c b/plugins/check_dig.c
index be7a6101..c27e5f13 100644
--- a/plugins/check_dig.c
+++ b/plugins/check_dig.c
@@ -1,30 +1,30 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dig plugin 3 * Monitoring check_dig plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2002-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_dig plugin 10 * This file contains the check_dig plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 13 * This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14 * it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 23 * You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29/* Hackers note: 29/* Hackers note:
30 * There are typecasts to (char *) from _("foo bar") in this file. 30 * There are typecasts to (char *) from _("foo bar") in this file.
@@ -33,7 +33,7 @@
33 * because on some architectures those strings are in non-writable memory */ 33 * because on some architectures those strings are in non-writable memory */
34 34
35const char *progname = "check_dig"; 35const char *progname = "check_dig";
36const char *copyright = "2002-2008"; 36const char *copyright = "2002-2024";
37const char *email = "devel@monitoring-plugins.org"; 37const char *email = "devel@monitoring-plugins.org";
38 38
39#include "common.h" 39#include "common.h"
@@ -41,340 +41,323 @@ 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
44int process_arguments (int, char **); 44#include "check_dig.d/config.h"
45int validate_arguments (void); 45#include "states.h"
46void print_help (void); 46
47void print_usage (void); 47typedef struct {
48 48 int errorcode;
49#define UNDEFINED 0 49 check_dig_config config;
50#define DEFAULT_PORT 53 50} check_dig_config_wrapper;
51#define DEFAULT_TRIES 2 51static check_dig_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
52 52static check_dig_config_wrapper validate_arguments(check_dig_config_wrapper /*config_wrapper*/);
53char *query_address = NULL; 53
54char *record_type = "A"; 54static void print_help(void);
55char *expected_address = NULL; 55void print_usage(void);
56char *dns_server = NULL; 56
57char *dig_args = ""; 57static int verbose = 0;
58char *query_transport = ""; 58
59bool verbose = false; 59int main(int argc, char **argv) {
60int server_port = DEFAULT_PORT; 60 setlocale(LC_ALL, "");
61int number_tries = DEFAULT_TRIES; 61 bindtextdomain(PACKAGE, LOCALEDIR);
62double warning_interval = UNDEFINED; 62 textdomain(PACKAGE);
63double critical_interval = UNDEFINED; 63
64struct timeval tv; 64 /* Set signal handling and alarm */
65 65 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
66int 66 usage_va(_("Cannot catch SIGALRM"));
67main (int argc, char **argv) 67 }
68{ 68
69 char *command_line; 69 /* Parse extra opts if any */
70 output chld_out, chld_err; 70 argv = np_extra_opts(&argc, argv, progname);
71 char *msg = NULL; 71
72 size_t i; 72 check_dig_config_wrapper tmp_config = process_arguments(argc, argv);
73 char *t; 73 if (tmp_config.errorcode == ERROR) {
74 long microsec; 74 usage_va(_("Could not parse arguments"));
75 double elapsed_time; 75 }
76 int result = STATE_UNKNOWN; 76
77 int timeout_interval_dig; 77 const check_dig_config config = tmp_config.config;
78 78
79 setlocale (LC_ALL, ""); 79 /* dig applies the timeout to each try, so we need to work around this */
80 bindtextdomain (PACKAGE, LOCALEDIR); 80 int timeout_interval_dig = ((int)timeout_interval / config.number_tries) + config.number_tries;
81 textdomain (PACKAGE); 81
82 82 char *command_line;
83 /* Set signal handling and alarm */ 83 /* get the command to run */
84 if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) 84 xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG,
85 usage_va(_("Cannot catch SIGALRM")); 85 config.dig_args, config.query_transport, config.server_port, config.dns_server,
86 86 config.query_address, config.record_type, config.number_tries, timeout_interval_dig);
87 /* Parse extra opts if any */ 87
88 argv=np_extra_opts (&argc, argv, progname); 88 alarm(timeout_interval);
89 89 struct timeval start_time;
90 if (process_arguments (argc, argv) == ERROR) 90 gettimeofday(&start_time, NULL);
91 usage_va(_("Could not parse arguments")); 91
92 92 if (verbose) {
93 /* dig applies the timeout to each try, so we need to work around this */ 93 printf("%s\n", command_line);
94 timeout_interval_dig = timeout_interval / number_tries + number_tries; 94 if (config.expected_address != NULL) {
95 95 printf(_("Looking for: '%s'\n"), config.expected_address);
96 /* get the command to run */ 96 } else {
97 xasprintf (&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", 97 printf(_("Looking for: '%s'\n"), config.query_address);
98 PATH_TO_DIG, dig_args, query_transport, server_port, dns_server, query_address, record_type, number_tries, timeout_interval_dig); 98 }
99 99 }
100 alarm (timeout_interval); 100
101 gettimeofday (&tv, NULL); 101 output chld_out;
102 102 output chld_err;
103 if (verbose) { 103 char *msg = NULL;
104 printf ("%s\n", command_line); 104 mp_state_enum result = STATE_UNKNOWN;
105 if(expected_address != NULL) { 105 /* run the command */
106 printf (_("Looking for: '%s'\n"), expected_address); 106 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) {
107 } else { 107 result = STATE_WARNING;
108 printf (_("Looking for: '%s'\n"), query_address); 108 msg = (char *)_("dig returned an error status");
109 } 109 }
110 } 110
111 111 for (size_t i = 0; i < chld_out.lines; i++) {
112 /* run the command */ 112 /* the server is responding, we just got the host name... */
113 if(np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { 113 if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) {
114 result = STATE_WARNING; 114
115 msg = (char *)_("dig returned an error status"); 115 /* loop through the whole 'ANSWER SECTION' */
116 } 116 for (; i < chld_out.lines; i++) {
117 117 /* get the host address */
118 for(i = 0; i < chld_out.lines; i++) { 118 if (verbose) {
119 /* the server is responding, we just got the host name... */ 119 printf("%s\n", chld_out.line[i]);
120 if (strstr (chld_out.line[i], ";; ANSWER SECTION:")) { 120 }
121 121
122 /* loop through the whole 'ANSWER SECTION' */ 122 if (strcasestr(chld_out.line[i], (config.expected_address == NULL
123 for(; i < chld_out.lines; i++) { 123 ? config.query_address
124 /* get the host address */ 124 : config.expected_address)) != NULL) {
125 if (verbose) 125 msg = chld_out.line[i];
126 printf ("%s\n", chld_out.line[i]); 126 result = STATE_OK;
127 127
128 if (strcasestr (chld_out.line[i], (expected_address == NULL ? query_address : expected_address)) != NULL) { 128 /* Translate output TAB -> SPACE */
129 msg = chld_out.line[i]; 129 char *temp = msg;
130 result = STATE_OK; 130 while ((temp = strchr(temp, '\t')) != NULL) {
131 131 *temp = ' ';
132 /* Translate output TAB -> SPACE */ 132 }
133 t = msg; 133 break;
134 while ((t = strchr(t, '\t')) != NULL) *t = ' '; 134 }
135 break; 135 }
136 } 136
137 } 137 if (result == STATE_UNKNOWN) {
138 138 msg = (char *)_("Server not found in ANSWER SECTION");
139 if (result == STATE_UNKNOWN) { 139 result = STATE_WARNING;
140 msg = (char *)_("Server not found in ANSWER SECTION"); 140 }
141 result = STATE_WARNING; 141
142 } 142 /* we found the answer section, so break out of the loop */
143 143 break;
144 /* we found the answer section, so break out of the loop */ 144 }
145 break; 145 }
146 } 146
147 } 147 if (result == STATE_UNKNOWN) {
148 148 msg = (char *)_("No ANSWER SECTION found");
149 if (result == STATE_UNKNOWN) { 149 result = STATE_CRITICAL;
150 msg = (char *)_("No ANSWER SECTION found"); 150 }
151 result = STATE_CRITICAL; 151
152 } 152 /* If we get anything on STDERR, at least set warning */
153 153 if (chld_err.buflen > 0) {
154 /* If we get anything on STDERR, at least set warning */ 154 result = max_state(result, STATE_WARNING);
155 if(chld_err.buflen > 0) { 155 if (!msg) {
156 result = max_state(result, STATE_WARNING); 156 for (size_t i = 0; i < chld_err.lines; i++) {
157 if(!msg) for(i = 0; i < chld_err.lines; i++) { 157 msg = strchr(chld_err.line[0], ':');
158 msg = strchr(chld_err.line[0], ':'); 158 if (msg) {
159 if(msg) { 159 msg++;
160 msg++; 160 break;
161 break; 161 }
162 } 162 }
163 } 163 }
164 } 164 }
165 165
166 microsec = deltime (tv); 166 long microsec = deltime(start_time);
167 elapsed_time = (double)microsec / 1.0e6; 167 double elapsed_time = (double)microsec / 1.0e6;
168 168
169 if (critical_interval > UNDEFINED && elapsed_time > critical_interval) 169 if (config.critical_interval > UNDEFINED && elapsed_time > config.critical_interval) {
170 result = STATE_CRITICAL; 170 result = STATE_CRITICAL;
171 171 }
172 else if (warning_interval > UNDEFINED && elapsed_time > warning_interval) 172
173 result = STATE_WARNING; 173 else if (config.warning_interval > UNDEFINED && elapsed_time > config.warning_interval) {
174 174 result = STATE_WARNING;
175 printf ("DNS %s - %.3f seconds response time (%s)|%s\n", 175 }
176 state_text (result), elapsed_time, 176
177 msg ? msg : _("Probably a non-existent host/domain"), 177 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time,
178 fperfdata("time", elapsed_time, "s", 178 msg ? msg : _("Probably a non-existent host/domain"),
179 (warning_interval>UNDEFINED ? true:false), 179 fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED),
180 warning_interval, 180 config.warning_interval, (config.critical_interval > UNDEFINED),
181 (critical_interval>UNDEFINED ? true:false), 181 config.critical_interval, true, 0, false, 0));
182 critical_interval, 182 exit(result);
183 true, 0, false, 0));
184 return result;
185} 183}
186 184
187
188
189/* process command-line arguments */ 185/* process command-line arguments */
190int 186check_dig_config_wrapper process_arguments(int argc, char **argv) {
191process_arguments (int argc, char **argv) 187 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
192{ 188 {"query_address", required_argument, 0, 'l'},
193 int c; 189 {"warning", required_argument, 0, 'w'},
194 190 {"critical", required_argument, 0, 'c'},
195 int option = 0; 191 {"timeout", required_argument, 0, 't'},
196 static struct option longopts[] = { 192 {"dig-arguments", required_argument, 0, 'A'},
197 {"hostname", required_argument, 0, 'H'}, 193 {"verbose", no_argument, 0, 'v'},
198 {"query_address", required_argument, 0, 'l'}, 194 {"version", no_argument, 0, 'V'},
199 {"warning", required_argument, 0, 'w'}, 195 {"help", no_argument, 0, 'h'},
200 {"critical", required_argument, 0, 'c'}, 196 {"record_type", required_argument, 0, 'T'},
201 {"timeout", required_argument, 0, 't'}, 197 {"expected_address", required_argument, 0, 'a'},
202 {"dig-arguments", required_argument, 0, 'A'}, 198 {"port", required_argument, 0, 'p'},
203 {"verbose", no_argument, 0, 'v'}, 199 {"use-ipv4", no_argument, 0, '4'},
204 {"version", no_argument, 0, 'V'}, 200 {"use-ipv6", no_argument, 0, '6'},
205 {"help", no_argument, 0, 'h'}, 201 {0, 0, 0, 0}};
206 {"record_type", required_argument, 0, 'T'}, 202
207 {"expected_address", required_argument, 0, 'a'}, 203 check_dig_config_wrapper result = {
208 {"port", required_argument, 0, 'p'}, 204 .errorcode = OK,
209 {"use-ipv4", no_argument, 0, '4'}, 205 .config = check_dig_config_init(),
210 {"use-ipv6", no_argument, 0, '6'}, 206 };
211 {0, 0, 0, 0} 207
212 }; 208 if (argc < 2) {
213 209 result.errorcode = ERROR;
214 if (argc < 2) 210 return result;
215 return ERROR; 211 }
216 212
217 while (1) { 213 int option = 0;
218 c = getopt_long (argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option); 214 while (true) {
219 215 int option_index = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option);
220 if (c == -1 || c == EOF) 216
221 break; 217 if (option_index == -1 || option_index == EOF) {
222 218 break;
223 switch (c) { 219 }
224 case 'h': /* help */ 220
225 print_help (); 221 switch (option_index) {
226 exit (STATE_UNKNOWN); 222 case 'h': /* help */
227 case 'V': /* version */ 223 print_help();
228 print_revision (progname, NP_VERSION); 224 exit(STATE_UNKNOWN);
229 exit (STATE_UNKNOWN); 225 case 'V': /* version */
230 case 'H': /* hostname */ 226 print_revision(progname, NP_VERSION);
231 host_or_die(optarg); 227 exit(STATE_UNKNOWN);
232 dns_server = optarg; 228 case 'H': /* hostname */
233 break; 229 host_or_die(optarg);
234 case 'p': /* server port */ 230 result.config.dns_server = optarg;
235 if (is_intpos (optarg)) { 231 break;
236 server_port = atoi (optarg); 232 case 'p': /* server port */
237 } 233 if (is_intpos(optarg)) {
238 else { 234 result.config.server_port = atoi(optarg);
239 usage_va(_("Port must be a positive integer - %s"), optarg); 235 } else {
240 } 236 usage_va(_("Port must be a positive integer - %s"), optarg);
241 break; 237 }
242 case 'l': /* address to lookup */ 238 break;
243 query_address = optarg; 239 case 'l': /* address to lookup */
244 break; 240 result.config.query_address = optarg;
245 case 'w': /* warning */ 241 break;
246 if (is_nonnegative (optarg)) { 242 case 'w': /* warning */
247 warning_interval = strtod (optarg, NULL); 243 if (is_nonnegative(optarg)) {
248 } 244 result.config.warning_interval = strtod(optarg, NULL);
249 else { 245 } else {
250 usage_va(_("Warning interval must be a positive integer - %s"), optarg); 246 usage_va(_("Warning interval must be a positive integer - %s"), optarg);
251 } 247 }
252 break; 248 break;
253 case 'c': /* critical */ 249 case 'c': /* critical */
254 if (is_nonnegative (optarg)) { 250 if (is_nonnegative(optarg)) {
255 critical_interval = strtod (optarg, NULL); 251 result.config.critical_interval = strtod(optarg, NULL);
256 } 252 } else {
257 else { 253 usage_va(_("Critical interval must be a positive integer - %s"), optarg);
258 usage_va(_("Critical interval must be a positive integer - %s"), optarg); 254 }
259 } 255 break;
260 break; 256 case 't': /* timeout */
261 case 't': /* timeout */ 257 if (is_intnonneg(optarg)) {
262 if (is_intnonneg (optarg)) { 258 timeout_interval = atoi(optarg);
263 timeout_interval = atoi (optarg); 259 } else {
264 } 260 usage_va(_("Timeout interval must be a positive integer - %s"), optarg);
265 else { 261 }
266 usage_va(_("Timeout interval must be a positive integer - %s"), optarg); 262 break;
267 } 263 case 'A': /* dig arguments */
268 break; 264 result.config.dig_args = strdup(optarg);
269 case 'A': /* dig arguments */ 265 break;
270 dig_args = strdup(optarg); 266 case 'v': /* verbose */
271 break; 267 verbose++;
272 case 'v': /* verbose */ 268 break;
273 verbose = true; 269 case 'T':
274 break; 270 result.config.record_type = optarg;
275 case 'T': 271 break;
276 record_type = optarg; 272 case 'a':
277 break; 273 result.config.expected_address = optarg;
278 case 'a': 274 break;
279 expected_address = optarg; 275 case '4':
280 break; 276 result.config.query_transport = "-4";
281 case '4': 277 break;
282 query_transport = "-4"; 278 case '6':
283 break; 279 result.config.query_transport = "-6";
284 case '6': 280 break;
285 query_transport = "-6"; 281 default: /* usage5 */
286 break; 282 usage5();
287 default: /* usage5 */ 283 }
288 usage5(); 284 }
289 } 285
290 } 286 int index = optind;
291 287 if (result.config.dns_server == NULL) {
292 c = optind; 288 if (index < argc) {
293 if (dns_server == NULL) { 289 host_or_die(argv[index]);
294 if (c < argc) { 290 result.config.dns_server = argv[index];
295 host_or_die(argv[c]); 291 } else {
296 dns_server = argv[c]; 292 if (strcmp(result.config.query_transport, "-6") == 0) {
297 } 293 result.config.dns_server = strdup("::1");
298 else { 294 } else {
299 if (strcmp(query_transport,"-6") == 0) 295 result.config.dns_server = strdup("127.0.0.1");
300 dns_server = strdup("::1"); 296 }
301 else 297 }
302 dns_server = strdup ("127.0.0.1"); 298 }
303 } 299
304 } 300 return validate_arguments(result);
305
306 return validate_arguments ();
307} 301}
308 302
309 303check_dig_config_wrapper validate_arguments(check_dig_config_wrapper config_wrapper) {
310 304 if (config_wrapper.config.query_address == NULL) {
311int 305 config_wrapper.errorcode = ERROR;
312validate_arguments (void) 306 }
313{ 307 return config_wrapper;
314 if (query_address != NULL)
315 return OK;
316 else
317 return ERROR;
318} 308}
319 309
310void print_help(void) {
311 char *myport;
320 312
313 xasprintf(&myport, "%d", DEFAULT_PORT);
321 314
322void 315 print_revision(progname, NP_VERSION);
323print_help (void)
324{
325 char *myport;
326 316
327 xasprintf (&myport, "%d", DEFAULT_PORT); 317 printf("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
318 printf(COPYRIGHT, copyright, email);
328 319
329 print_revision (progname, NP_VERSION); 320 printf(_("This plugin tests the DNS service on the specified host using dig"));
330 321
331 printf ("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"); 322 printf("\n\n");
332 printf (COPYRIGHT, copyright, email);
333 323
334 printf (_("This plugin tests the DNS service on the specified host using dig")); 324 print_usage();
335 325
336 printf ("\n\n"); 326 printf(UT_HELP_VRSN);
337 327
338 print_usage (); 328 printf(UT_EXTRA_OPTS);
339 329
340 printf (UT_HELP_VRSN); 330 printf(UT_HOST_PORT, 'p', myport);
341 331
342 printf (UT_EXTRA_OPTS); 332 printf(" %s\n", "-4, --use-ipv4");
333 printf(" %s\n", _("Force dig to only use IPv4 query transport"));
334 printf(" %s\n", "-6, --use-ipv6");
335 printf(" %s\n", _("Force dig to only use IPv6 query transport"));
336 printf(" %s\n", "-l, --query_address=STRING");
337 printf(" %s\n", _("Machine name to lookup"));
338 printf(" %s\n", "-T, --record_type=STRING");
339 printf(" %s\n", _("Record type to lookup (default: A)"));
340 printf(" %s\n", "-a, --expected_address=STRING");
341 printf(" %s\n",
342 _("An address expected to be in the answer section. If not set, uses whatever"));
343 printf(" %s\n", _("was in -l"));
344 printf(" %s\n", "-A, --dig-arguments=STRING");
345 printf(" %s\n", _("Pass STRING as argument(s) to dig"));
346 printf(UT_WARN_CRIT);
347 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
348 printf(UT_VERBOSE);
343 349
344 printf (UT_HOST_PORT, 'p', myport); 350 printf("\n");
351 printf("%s\n", _("Examples:"));
352 printf(" %s\n", "check_dig -H DNSSERVER -l www.example.com -A \"+tcp\"");
353 printf(" %s\n", "This will send a tcp query to DNSSERVER for www.example.com");
345 354
346 printf (" %s\n","-4, --use-ipv4"); 355 printf(UT_SUPPORT);
347 printf (" %s\n",_("Force dig to only use IPv4 query transport"));
348 printf (" %s\n","-6, --use-ipv6");
349 printf (" %s\n",_("Force dig to only use IPv6 query transport"));
350 printf (" %s\n","-l, --query_address=STRING");
351 printf (" %s\n",_("Machine name to lookup"));
352 printf (" %s\n","-T, --record_type=STRING");
353 printf (" %s\n",_("Record type to lookup (default: A)"));
354 printf (" %s\n","-a, --expected_address=STRING");
355 printf (" %s\n",_("An address expected to be in the answer section. If not set, uses whatever"));
356 printf (" %s\n",_("was in -l"));
357 printf (" %s\n","-A, --dig-arguments=STRING");
358 printf (" %s\n",_("Pass STRING as argument(s) to dig"));
359 printf (UT_WARN_CRIT);
360 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
361 printf (UT_VERBOSE);
362
363 printf ("\n");
364 printf ("%s\n", _("Examples:"));
365 printf (" %s\n", "check_dig -H DNSSERVER -l www.example.com -A \"+tcp\"");
366 printf (" %s\n", "This will send a tcp query to DNSSERVER for www.example.com");
367
368 printf (UT_SUPPORT);
369} 356}
370 357
371 358void print_usage(void) {
372 359 printf("%s\n", _("Usage:"));
373void 360 printf("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname);
374print_usage (void) 361 printf(" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n");
375{ 362 printf(" [-t <timeout>] [-a <expected answer address>] [-v]\n");
376 printf ("%s\n", _("Usage:"));
377 printf ("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname);
378 printf (" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n");
379 printf (" [-t <timeout>] [-a <expected answer address>] [-v]\n");
380} 363}
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 24de2d45..d42b5486 100644
--- a/plugins/check_disk.c
+++ b/plugins/check_disk.c
@@ -1,1161 +1,1304 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_disk plugin 3 * Monitoring check_disk plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2008 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_disk plugin 10 * This file contains the check_disk plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 13 * This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14 * it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 23 * You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29const char *progname = "check_disk"; 29const char *progname = "check_disk";
30const char *program_name = "check_disk"; /* Required for coreutils libs */ 30const char *program_name = "check_disk"; /* Required for coreutils libs */
31const char *copyright = "1999-2008"; 31const char *copyright = "1999-2024";
32const char *email = "devel@monitoring-plugins.org"; 32const char *email = "devel@monitoring-plugins.org";
33 33
34 34#include "states.h"
35#include "common.h" 35#include "common.h"
36#include "output.h"
37#include "perfdata.h"
38#include "utils_base.h"
39#include "lib/thresholds.h"
40
36#ifdef HAVE_SYS_STAT_H 41#ifdef HAVE_SYS_STAT_H
37# include <sys/stat.h> 42# include <sys/stat.h>
38#endif 43#endif
44
39#if HAVE_INTTYPES_H 45#if HAVE_INTTYPES_H
40# include <inttypes.h> 46# include <inttypes.h>
41#endif 47#endif
48
42#include <assert.h> 49#include <assert.h>
43#include "popen.h"
44#include "utils.h"
45#include "utils_disk.h"
46#include <stdarg.h> 50#include <stdarg.h>
47#include "fsusage.h" 51#include <stdint.h>
48#include "mountlist.h"
49#include <float.h> 52#include <float.h>
53#include "./popen.h"
54#include "./utils.h"
55#include "../gl/fsusage.h"
56#include "../gl/mountlist.h"
57#include "./check_disk.d/utils_disk.h"
58
50#if HAVE_LIMITS_H 59#if HAVE_LIMITS_H
51# include <limits.h> 60# include <limits.h>
52#endif 61#endif
62
53#include "regex.h" 63#include "regex.h"
54 64
55#ifdef __CYGWIN__ 65#ifdef __CYGWIN__
56# include <windows.h> 66# include <windows.h>
57# undef ERROR 67# undef ERROR
58# define ERROR -1 68# define ERROR -1
59#endif 69#endif
60 70
61/* If nonzero, show even filesystems with zero size or 71#ifdef _AIX
62 uninteresting types. */ 72# pragma alloca
63static int show_all_fs = 1; 73#endif
64
65/* If nonzero, show only local filesystems. */
66static int show_local_fs = 0;
67
68/* If nonzero, show only local filesystems but call stat() on remote ones. */
69static int stat_remote_fs = 0;
70
71/* If positive, the units to use when printing sizes;
72 if negative, the human-readable base. */
73/* static int output_block_size; */
74
75/* If nonzero, invoke the `sync' system call before getting any usage data.
76 Using this option can make df very slow, especially with many or very
77 busy disks. Note that this may make a difference on some systems --
78 SunOs4.1.3, for one. It is *not* necessary on Linux. */
79/* static int require_sync = 0; */
80
81/* Linked list of filesystem types to display.
82 If `fs_select_list' is NULL, list all types.
83 This table is generated dynamically from command-line options,
84 rather than hardcoding into the program what it thinks are the
85 valid filesystem types; let the user specify any filesystem type
86 they want to, and if there are any filesystems of that type, they
87 will be shown.
88
89 Some filesystem types:
90 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
91
92/* static struct parameter_list *fs_select_list; */
93
94/* Linked list of filesystem types to omit.
95 If the list is empty, don't exclude any types. */
96static struct regex_list *fs_exclude_list = NULL;
97 74
98/* Linked list of filesystem types to check. 75typedef struct {
99 If the list is empty, include all types. */ 76 int errorcode;
100static struct regex_list *fs_include_list; 77 check_disk_config config;
78} check_disk_config_wrapper;
79static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
80
81static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
82 char *crit_freespace_units, char *warn_freespace_percent,
83 char *crit_freespace_percent, char *warn_freeinodes_percent,
84 char *crit_freeinodes_percent);
85static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/);
86static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/);
87
88/*
89 * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control
90 * how reserved and inodes should be judged (ignored or not)
91 */
92static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp,
93 bool freespace_ignore_reserved);
94static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit,
95 bool display_inodes_perfdata, byte_unit unit);
96
97void print_usage(void);
98static void print_help(void);
99
100static int verbose = 0;
101
102// This would not be necessary in C23!!
103const byte_unit Bytes_Factor = 1;
104const byte_unit KibiBytes_factor = 1024;
105const byte_unit MebiBytes_factor = 1048576;
106const byte_unit GibiBytes_factor = 1073741824;
107const byte_unit TebiBytes_factor = 1099511627776;
108const byte_unit PebiBytes_factor = 1125899906842624;
109const byte_unit ExbiBytes_factor = 1152921504606846976;
110const byte_unit KiloBytes_factor = 1000;
111const byte_unit MegaBytes_factor = 1000000;
112const byte_unit GigaBytes_factor = 1000000000;
113const byte_unit TeraBytes_factor = 1000000000000;
114const byte_unit PetaBytes_factor = 1000000000000000;
115const byte_unit ExaBytes_factor = 1000000000000000000;
116
117int main(int argc, char **argv) {
118 setlocale(LC_ALL, "");
119 bindtextdomain(PACKAGE, LOCALEDIR);
120 textdomain(PACKAGE);
101 121
102static struct name_list *dp_exclude_list; 122#ifdef __CYGWIN__
123 char mountdir[32];
124#endif
103 125
104static struct parameter_list *path_select_list = NULL; 126 // Parse extra opts if any
127 argv = np_extra_opts(&argc, argv, progname);
128
129 check_disk_config_wrapper tmp_config = process_arguments(argc, argv);
130 if (tmp_config.errorcode == ERROR) {
131 usage4(_("Could not parse arguments"));
132 }
133
134 check_disk_config config = tmp_config.config;
135
136 if (config.output_format_is_set) {
137 mp_set_format(config.output_format);
138 }
139
140 if (config.erronly) {
141 mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY);
142 }
143
144 if (!config.path_ignored) {
145 mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list,
146 config.exact_match);
147 }
148
149 // Error if no match found for specified paths
150 for (parameter_list_elem *elem = config.path_select_list.first; elem;) {
151 if (!elem->best_match && config.ignore_missing) {
152 /* Delete the path from the list so that it is not stat-checked later in the code. */
153 elem = mp_int_fs_list_del(&config.path_select_list, elem);
154 continue;
155 }
156 if (!elem->best_match) {
157 /* Without --ignore-missing option, exit with Critical state. */
158 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name);
159 }
160
161 elem = mp_int_fs_list_get_next(elem);
162 }
163
164 mp_check overall = mp_check_init();
165 if (config.path_select_list.length == 0) {
166 mp_subcheck none_sc = mp_subcheck_init();
167 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
168 if (config.ignore_missing) {
169 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
170 } else {
171 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
172 if (verbose >= 2) {
173 printf("None of the provided paths were found\n");
174 }
175 }
176 mp_add_subcheck_to_check(&overall, none_sc);
177 mp_exit(overall);
178 }
105 179
106/* Linked list of mounted filesystems. */ 180 // Filter list first
107static struct mount_entry *mount_list; 181 for (parameter_list_elem *path = config.path_select_list.first; path;) {
182 if (!path->best_match) {
183 path = mp_int_fs_list_del(&config.path_select_list, path);
184 continue;
185 }
108 186
109/* For long options that have no equivalent short option, use a 187 struct mount_entry *mount_entry = path->best_match;
110 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
111enum
112{
113 SYNC_OPTION = CHAR_MAX + 1,
114 NO_SYNC_OPTION,
115 BLOCK_SIZE_OPTION
116};
117 188
118#ifdef _AIX 189#ifdef __CYGWIN__
119#pragma alloca 190 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) {
191 path = mp_int_fs_list_del(&config.path_select_list, path);
192 continue;
193 }
194
195 char *mountdir = NULL;
196 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10);
197 if (GetDriveType(mountdir) != DRIVE_FIXED) {
198 mount_entry->me_remote = 1;
199 }
120#endif 200#endif
121 201
122int process_arguments (int, char **); 202 /* Remove filesystems already seen */
123void print_path (const char *mypath); 203 if (np_seen_name(config.seen, mount_entry->me_mountdir)) {
124void set_all_thresholds (struct parameter_list *path); 204 path = mp_int_fs_list_del(&config.path_select_list, path);
125int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *); 205 continue;
126void print_help (void); 206 }
127void print_usage (void); 207
128double calculate_percent(uintmax_t, uintmax_t); 208 if (path->group == NULL) {
129bool stat_path (struct parameter_list *p); 209 if (config.fs_exclude_list &&
130void get_stats (struct parameter_list *p, struct fs_usage *fsp); 210 np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) {
131void get_path_stats (struct parameter_list *p, struct fs_usage *fsp); 211 // Skip excluded fs's
132 212 path = mp_int_fs_list_del(&config.path_select_list, path);
133char *exclude_device; 213 continue;
134char *units; 214 }
135uintmax_t mult = 1024 * 1024;
136int verbose = 0;
137bool erronly = false;
138bool display_mntp = false;
139bool exact_match = false;
140bool ignore_missing = false;
141bool freespace_ignore_reserved = false;
142bool display_inodes_perfdata = false;
143char *warn_freespace_units = NULL;
144char *crit_freespace_units = NULL;
145char *warn_freespace_percent = NULL;
146char *crit_freespace_percent = NULL;
147char *warn_usedspace_units = NULL;
148char *crit_usedspace_units = NULL;
149char *warn_usedspace_percent = NULL;
150char *crit_usedspace_percent = NULL;
151char *warn_usedinodes_percent = NULL;
152char *crit_usedinodes_percent = NULL;
153char *warn_freeinodes_percent = NULL;
154char *crit_freeinodes_percent = NULL;
155bool path_selected = false;
156bool path_ignored = false;
157char *group = NULL;
158struct stat *stat_buf;
159struct name_list *seen = NULL;
160
161
162int
163main (int argc, char **argv)
164{
165 int result = STATE_UNKNOWN;
166 int disk_result = STATE_UNKNOWN;
167 char *output;
168 char *ignored;
169 char *details;
170 char *perf;
171 char *perf_ilabel;
172 char *preamble = " - free space:";
173 char *ignored_preamble = " - ignored paths:";
174 char *flag_header;
175 int temp_result;
176
177 struct mount_entry *me;
178 struct fs_usage fsp;
179 struct parameter_list *temp_list, *path;
180 215
181#ifdef __CYGWIN__ 216 if (config.device_path_exclude_list &&
182 char mountdir[32]; 217 (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) ||
183#endif 218 np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) {
219 // Skip excluded device or mount paths
220 path = mp_int_fs_list_del(&config.path_select_list, path);
221 continue;
222 }
184 223
185 output = strdup (""); 224 if (config.fs_include_list &&
186 ignored = strdup (""); 225 !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) {
187 details = strdup (""); 226 // Skip not included fstypes
188 perf = strdup (""); 227 path = mp_int_fs_list_del(&config.path_select_list, path);
189 perf_ilabel = strdup (""); 228 continue;
190 stat_buf = malloc(sizeof *stat_buf); 229 }
191
192 setlocale (LC_ALL, "");
193 bindtextdomain (PACKAGE, LOCALEDIR);
194 textdomain (PACKAGE);
195
196 mount_list = read_file_system_list (0);
197
198 /* Parse extra opts if any */
199 argv = np_extra_opts (&argc, argv, progname);
200
201 if (process_arguments (argc, argv) == ERROR)
202 usage4 (_("Could not parse arguments"));
203
204 /* If a list of paths has not been selected, find entire
205 mount list and create list of paths
206 */
207 if (path_selected == false && path_ignored == false) {
208 for (me = mount_list; me; me = me->me_next) {
209 if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) {
210 path = np_add_parameter(&path_select_list, me->me_mountdir);
211 }
212 path->best_match = me;
213 path->group = group;
214 set_all_thresholds(path);
215 }
216 }
217
218 if (path_ignored == false) {
219 np_set_best_match(path_select_list, mount_list, exact_match);
220 }
221
222 /* Error if no match found for specified paths */
223 temp_list = path_select_list;
224
225 while (path_select_list) {
226 if (! path_select_list->best_match && ignore_missing == true) {
227 /* If the first element will be deleted, the temp_list must be updated with the new start address as well */
228 if (path_select_list == temp_list) {
229 temp_list = path_select_list->name_next;
230 }
231 /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */
232 xasprintf (&ignored, "%s %s;", ignored, path_select_list->name);
233 /* Delete the path from the list so that it is not stat-checked later in the code. */
234 path_select_list = np_del_parameter(path_select_list, path_select_list->name_prev);
235 } else if (! path_select_list->best_match) {
236 /* Without --ignore-missing option, exit with Critical state. */
237 die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list->name);
238 } else {
239 /* Continue jumping through the list */
240 path_select_list = path_select_list->name_next;
241 }
242 }
243
244 path_select_list = temp_list;
245
246 if (! path_select_list && ignore_missing == true) {
247 result = STATE_OK;
248 if (verbose >= 2) {
249 printf ("None of the provided paths were found\n");
250 }
251 }
252
253 /* Process for every path in list */
254 for (path = path_select_list; path; path=path->name_next) {
255 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL)
256 printf("Thresholds(pct) for %s warn: %f crit %f\n",
257 path->name,
258 path->freespace_percent->warning->end,
259 path->freespace_percent->critical->end);
260
261 if (verbose >= 3 && path->group != NULL)
262 printf("Group of %s: %s\n",path->name,path->group);
263
264 /* reset disk result */
265 disk_result = STATE_UNKNOWN;
266
267 me = path->best_match;
268
269 if (!me) {
270 continue;
271 }
272 230
273#ifdef __CYGWIN__ 231 /* Skip remote filesystems if we're not interested in them */
274 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) 232 if (mount_entry->me_remote && config.show_local_fs) {
275 continue; 233 if (config.stat_remote_fs) {
276 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); 234 // TODO Stat here
277 if (GetDriveType(mountdir) != DRIVE_FIXED) 235 if (!stat_path(path, config.ignore_missing) && config.ignore_missing) {
278 me->me_remote = 1; 236 }
279#endif 237 }
280 /* Filters */ 238 continue;
281 239 }
282 /* Remove filesystems already seen */
283 if (np_seen_name(seen, me->me_mountdir)) {
284 continue;
285 }
286 np_add_name(&seen, me->me_mountdir);
287
288 if (path->group == NULL) {
289 /* Skip remote filesystems if we're not interested in them */
290 if (me->me_remote && show_local_fs) {
291 if (stat_remote_fs) {
292 if (!stat_path(path) && ignore_missing == true) {
293 result = STATE_OK;
294 xasprintf (&ignored, "%s %s;", ignored, path->name);
295 }
296 }
297 continue;
298 /* Skip pseudo fs's if we haven't asked for all fs's */
299 } else if (me->me_dummy && !show_all_fs) {
300 continue;
301 /* Skip excluded fstypes */
302 } else if (fs_exclude_list && np_find_regmatch (fs_exclude_list, me->me_type)) {
303 continue;
304 /* Skip excluded fs's */
305 } else if (dp_exclude_list &&
306 (np_find_name (dp_exclude_list, me->me_devname) ||
307 np_find_name (dp_exclude_list, me->me_mountdir))) {
308 continue;
309 /* Skip not included fstypes */
310 } else if (fs_include_list && !np_find_regmatch(fs_include_list, me->me_type)) {
311 continue;
312 }
313 }
314
315 if (!stat_path(path)) {
316 if (ignore_missing == true) {
317 result = STATE_OK;
318 xasprintf (&ignored, "%s %s;", ignored, path->name);
319 }
320 continue;
321 }
322 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
323
324 if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
325 get_stats (path, &fsp);
326
327 if (verbose >= 3) {
328 printf ("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n",
329 me->me_mountdir,
330 path->dused_pct,
331 path->dfree_pct,
332 path->dused_units,
333 path->dfree_units,
334 path->dtotal_units,
335 path->dused_inodes_percent,
336 path->dfree_inodes_percent,
337 fsp.fsu_blocksize,
338 mult);
339 }
340
341 /* Threshold comparisons */
342
343 temp_result = get_status(path->dfree_units, path->freespace_units);
344 if (verbose >=3) printf("Freespace_units result=%d\n", temp_result);
345 disk_result = max_state( disk_result, temp_result );
346
347 temp_result = get_status(path->dfree_pct, path->freespace_percent);
348 if (verbose >=3) printf("Freespace%% result=%d\n", temp_result);
349 disk_result = max_state( disk_result, temp_result );
350
351 temp_result = get_status(path->dused_units, path->usedspace_units);
352 if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result);
353 disk_result = max_state( disk_result, temp_result );
354
355 temp_result = get_status(path->dused_pct, path->usedspace_percent);
356 if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result);
357 disk_result = max_state( disk_result, temp_result );
358
359 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
360 if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result);
361 disk_result = max_state( disk_result, temp_result );
362
363 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
364 if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result);
365 disk_result = max_state( disk_result, temp_result );
366
367 result = max_state(result, disk_result);
368
369 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
370 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
371 data. Assumption that start=0. Roll on new syntax...
372 */
373
374 /* *_high_tide must be reinitialized at each run */
375 uint64_t warning_high_tide = UINT64_MAX;
376
377 if (path->freespace_units->warning != NULL) {
378 warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * mult;
379 }
380 if (path->freespace_percent->warning != NULL) {
381 warning_high_tide = min( warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end/100) * (path->dtotal_units * mult)) );
382 }
383
384 uint64_t critical_high_tide = UINT64_MAX;
385
386 if (path->freespace_units->critical != NULL) {
387 critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * mult;
388 }
389 if (path->freespace_percent->critical != NULL) {
390 critical_high_tide = min( critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end/100) * (path->dtotal_units * mult)) );
391 }
392
393 /* Nb: *_high_tide are unset when == UINT64_MAX */
394 xasprintf (&perf, "%s %s", perf,
395 perfdata_uint64 (
396 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
397 path->dused_units * mult, "B",
398 (warning_high_tide == UINT64_MAX ? false : true), warning_high_tide,
399 (critical_high_tide == UINT64_MAX ? false : true), critical_high_tide,
400 true, 0,
401 true, path->dtotal_units * mult));
402
403 if (display_inodes_perfdata) {
404 /* *_high_tide must be reinitialized at each run */
405 warning_high_tide = UINT64_MAX;
406 critical_high_tide = UINT64_MAX;
407
408 if (path->freeinodes_percent->warning != NULL) {
409 warning_high_tide = (uint64_t) fabs( min( (double) warning_high_tide, (double) (1.0 - path->freeinodes_percent->warning->end/100)*path->inodes_total ));
410 }
411 if (path->freeinodes_percent->critical != NULL) {
412 critical_high_tide = (uint64_t) fabs( min( (double) critical_high_tide, (double) (1.0 - path->freeinodes_percent->critical->end/100)*path->inodes_total ));
413 }
414
415 xasprintf (&perf_ilabel, "%s (inodes)", (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir);
416 /* Nb: *_high_tide are unset when == UINT64_MAX */
417 xasprintf (&perf, "%s %s", perf,
418 perfdata_uint64 (perf_ilabel,
419 path->inodes_used, "",
420 (warning_high_tide != UINT64_MAX ? true : false), warning_high_tide,
421 (critical_high_tide != UINT64_MAX ? true : false), critical_high_tide,
422 true, 0,
423 true, path->inodes_total));
424 }
425
426 if (disk_result==STATE_OK && erronly && !verbose)
427 continue;
428
429 if(disk_result && verbose >= 1) {
430 xasprintf(&flag_header, " %s [", state_text (disk_result));
431 } else {
432 xasprintf(&flag_header, "");
433 }
434 xasprintf (&output, "%s%s %s %llu%s (%.1f%%",
435 output, flag_header,
436 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
437 path->dfree_units,
438 units,
439 path->dfree_pct);
440 if (path->dused_inodes_percent < 0) {
441 xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : ""));
442 } else {
443 xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : ""));
444 }
445 free(flag_header);
446 }
447 }
448
449 if (verbose >= 2)
450 xasprintf (&output, "%s%s", output, details);
451
452 if (strcmp(output, "") == 0 && ! erronly) {
453 preamble = "";
454 xasprintf (&output, " - No disks were found for provided parameters");
455 }
456
457 printf ("DISK %s%s%s%s%s|%s\n", state_text (result), ((erronly && result==STATE_OK)) ? "" : preamble, output, (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf);
458 return result;
459}
460 240
241 // TODO why stat here? remove unstatable fs?
242 if (!stat_path(path, config.ignore_missing)) {
243 // if (config.ignore_missing) {
244 // xasprintf(&ignored, "%s %s;", ignored, path->name);
245 // }
246 // not accessible, remove from list
247 path = mp_int_fs_list_del(&config.path_select_list, path);
248 continue;
249 }
250 }
251
252 path = mp_int_fs_list_get_next(path);
253 }
254
255 // now get the actual measurements
256 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) {
257 // Get actual metrics here
258 struct mount_entry *mount_entry = filesystem->best_match;
259 struct fs_usage fsp = {0};
260 get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp);
261
262 if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) {
263 *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved);
264
265 if (verbose >= 3) {
266 printf("For %s, used_units=%lu free_units=%lu total_units=%lu "
267 "fsp.fsu_blocksize=%lu\n",
268 mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes,
269 filesystem->total_bytes, fsp.fsu_blocksize);
270 }
271 } else {
272 // failed to retrieve file system data or not mounted?
273 filesystem = mp_int_fs_list_del(&config.path_select_list, filesystem);
274 continue;
275 }
276 filesystem = mp_int_fs_list_get_next(filesystem);
277 }
278
279 if (verbose > 2) {
280 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
281 filesystem = mp_int_fs_list_get_next(filesystem)) {
282 assert(filesystem->best_match != NULL);
283 if (filesystem->best_match == NULL) {
284 printf("Filesystem path %s has no mount_entry!\n", filesystem->name);
285 } else {
286 // printf("Filesystem path %s has a mount_entry!\n", filesystem->name);
287 }
288 }
289 }
290
291 measurement_unit_list *measurements = NULL;
292 measurement_unit_list *current = NULL;
293 // create measuring units, because of groups
294 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
295 filesystem = mp_int_fs_list_get_next(filesystem)) {
296 assert(filesystem->best_match != NULL);
297
298 if (filesystem->group == NULL) {
299 // create a measurement unit for the fs
300 measurement_unit unit =
301 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
302 if (measurements == NULL) {
303 measurements = current = add_measurement_list(NULL, unit);
304 } else {
305 current = add_measurement_list(measurements, unit);
306 }
307 } else {
308 // Grouped elements are consecutive
309 if (measurements == NULL) {
310 // first entry
311 measurement_unit unit =
312 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
313 unit.name = strdup(filesystem->group);
314 measurements = current = add_measurement_list(NULL, unit);
315 } else {
316 // if this is the first element of a group, the name of the previous entry is
317 // different
318 if (strcmp(filesystem->group, current->unit.name) != 0) {
319 // so, this must be the first element of a group
320 measurement_unit unit =
321 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
322 unit.name = filesystem->group;
323 current = add_measurement_list(measurements, unit);
324
325 } else {
326 // NOT the first entry of a group, add info to the other one
327 current->unit = add_filesystem_to_measurement_unit(current->unit, *filesystem);
328 }
329 }
330 }
331 }
332
333 /* Process for every path in list */
334 if (measurements != NULL) {
335 for (measurement_unit_list *unit = measurements; unit; unit = unit->next) {
336 mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata,
337 config.display_unit);
338 mp_add_subcheck_to_check(&overall, unit_sc);
339 }
340 } else {
341 // Apparently no machting fs found
342 mp_subcheck none_sc = mp_subcheck_init();
343 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
344
345 if (config.ignore_missing) {
346 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
347 } else {
348 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
349 }
350 mp_add_subcheck_to_check(&overall, none_sc);
351 }
352
353 mp_exit(overall);
354}
461 355
462double calculate_percent(uintmax_t value, uintmax_t total) { 356double calculate_percent(uintmax_t value, uintmax_t total) {
463 double pct = -1; 357 double pct = -1;
464 if(value <= DBL_MAX && total != 0) { 358 if (value <= DBL_MAX && total != 0) {
465 pct = (double)value / total * 100.0; 359 pct = (double)value / (double)total * 100.0;
466 } 360 }
467 return pct; 361
362 return pct;
468} 363}
469 364
470/* process command-line arguments */ 365/* process command-line arguments */
471int 366check_disk_config_wrapper process_arguments(int argc, char **argv) {
472process_arguments (int argc, char **argv) 367
473{ 368 check_disk_config_wrapper result = {
474 int c, err; 369 .errorcode = OK,
475 struct parameter_list *se; 370 .config = check_disk_config_init(),
476 struct parameter_list *temp_list = NULL, *previous = NULL; 371 };
477 struct mount_entry *me; 372
478 regex_t re; 373 if (argc < 2) {
479 int cflags = REG_NOSUB | REG_EXTENDED; 374 result.errorcode = ERROR;
480 int default_cflags = cflags; 375 return result;
481 char errbuf[MAX_INPUT_BUFFER]; 376 }
482 int fnd = 0; 377
483 378 enum {
484 int option = 0; 379 output_format_index = CHAR_MAX + 1,
485 static struct option longopts[] = { 380 display_unit_index,
486 {"timeout", required_argument, 0, 't'}, 381 };
487 {"warning", required_argument, 0, 'w'}, 382
488 {"critical", required_argument, 0, 'c'}, 383 static struct option longopts[] = {{"timeout", required_argument, 0, 't'},
489 {"iwarning", required_argument, 0, 'W'}, 384 {"warning", required_argument, 0, 'w'},
490 /* Dang, -C is taken. We might want to reshuffle this. */ 385 {"critical", required_argument, 0, 'c'},
491 {"icritical", required_argument, 0, 'K'}, 386 {"iwarning", required_argument, 0, 'W'},
492 {"kilobytes", no_argument, 0, 'k'}, 387 {"icritical", required_argument, 0, 'K'},
493 {"megabytes", no_argument, 0, 'm'}, 388 {"kilobytes", no_argument, 0, 'k'},
494 {"units", required_argument, 0, 'u'}, 389 {"megabytes", no_argument, 0, 'm'},
495 {"path", required_argument, 0, 'p'}, 390 {"units", required_argument, 0, 'u'},
496 {"partition", required_argument, 0, 'p'}, 391 {"path", required_argument, 0, 'p'},
497 {"exclude_device", required_argument, 0, 'x'}, 392 {"partition", required_argument, 0, 'p'},
498 {"exclude-type", required_argument, 0, 'X'}, 393 {"exclude_device", required_argument, 0, 'x'},
499 {"include-type", required_argument, 0, 'N'}, 394 {"exclude-type", required_argument, 0, 'X'},
500 {"group", required_argument, 0, 'g'}, 395 {"include-type", required_argument, 0, 'N'},
501 {"eregi-path", required_argument, 0, 'R'}, 396 {"group", required_argument, 0, 'g'},
502 {"eregi-partition", required_argument, 0, 'R'}, 397 {"eregi-path", required_argument, 0, 'R'},
503 {"ereg-path", required_argument, 0, 'r'}, 398 {"eregi-partition", required_argument, 0, 'R'},
504 {"ereg-partition", required_argument, 0, 'r'}, 399 {"ereg-path", required_argument, 0, 'r'},
505 {"freespace-ignore-reserved", no_argument, 0, 'f'}, 400 {"ereg-partition", required_argument, 0, 'r'},
506 {"ignore-ereg-path", required_argument, 0, 'i'}, 401 {"freespace-ignore-reserved", no_argument, 0, 'f'},
507 {"ignore-ereg-partition", required_argument, 0, 'i'}, 402 {"ignore-ereg-path", required_argument, 0, 'i'},
508 {"ignore-eregi-path", required_argument, 0, 'I'}, 403 {"ignore-ereg-partition", required_argument, 0, 'i'},
509 {"ignore-eregi-partition", required_argument, 0, 'I'}, 404 {"ignore-eregi-path", required_argument, 0, 'I'},
510 {"ignore-missing", no_argument, 0, 'n'}, 405 {"ignore-eregi-partition", required_argument, 0, 'I'},
511 {"local", no_argument, 0, 'l'}, 406 {"ignore-missing", no_argument, 0, 'n'},
512 {"stat-remote-fs", no_argument, 0, 'L'}, 407 {"local", no_argument, 0, 'l'},
513 {"iperfdata", no_argument, 0, 'P'}, 408 {"stat-remote-fs", no_argument, 0, 'L'},
514 {"mountpoint", no_argument, 0, 'M'}, 409 {"iperfdata", no_argument, 0, 'P'},
515 {"errors-only", no_argument, 0, 'e'}, 410 {"mountpoint", no_argument, 0, 'M'},
516 {"exact-match", no_argument, 0, 'E'}, 411 {"errors-only", no_argument, 0, 'e'},
517 {"all", no_argument, 0, 'A'}, 412 {"exact-match", no_argument, 0, 'E'},
518 {"verbose", no_argument, 0, 'v'}, 413 {"all", no_argument, 0, 'A'},
519 {"quiet", no_argument, 0, 'q'}, 414 {"verbose", no_argument, 0, 'v'},
520 {"clear", no_argument, 0, 'C'}, 415 {"quiet", no_argument, 0, 'q'},
521 {"version", no_argument, 0, 'V'}, 416 {"clear", no_argument, 0, 'C'},
522 {"help", no_argument, 0, 'h'}, 417 {"version", no_argument, 0, 'V'},
523 {0, 0, 0, 0} 418 {"help", no_argument, 0, 'h'},
524 }; 419 {"output-format", required_argument, 0, output_format_index},
525 420 {"display-unit", required_argument, 0, display_unit_index},
526 if (argc < 2) 421 {0, 0, 0, 0}};
527 return ERROR; 422
528 423 for (int index = 1; index < argc; index++) {
529 np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); 424 if (strcmp("-to", argv[index]) == 0) {
530 425 strcpy(argv[index], "-t");
531 for (c = 1; c < argc; c++) 426 }
532 if (strcmp ("-to", argv[c]) == 0) 427 }
533 strcpy (argv[c], "-t"); 428
534 429 int cflags = REG_NOSUB | REG_EXTENDED;
535 while (1) { 430 int default_cflags = cflags;
536 c = getopt_long (argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); 431 char *warn_freespace_units = NULL;
537 432 char *crit_freespace_units = NULL;
538 if (c == -1 || c == EOF) 433 char *warn_freespace_percent = NULL;
539 break; 434 char *crit_freespace_percent = NULL;
540 435 char *warn_freeinodes_percent = NULL;
541 switch (c) { 436 char *crit_freeinodes_percent = NULL;
542 case 't': /* timeout period */ 437
543 if (is_integer (optarg)) { 438 bool path_selected = false;
544 timeout_interval = atoi (optarg); 439 char *group = NULL;
545 break; 440 byte_unit unit = MebiBytes_factor;
546 } 441
547 else { 442 result.config.mount_list = read_file_system_list(false);
548 usage2 (_("Timeout interval must be a positive integer"), optarg); 443
549 } 444 np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED);
550 445
551 /* See comments for 'c' */ 446 while (true) {
552 case 'w': /* warning threshold */ 447 int option = 0;
448 int option_index = getopt_long(
449 argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
450
451 if (option_index == -1 || option_index == EOF) {
452 break;
453 }
454
455 switch (option_index) {
456 case 't': /* timeout period */
457 if (is_integer(optarg)) {
458 timeout_interval = atoi(optarg);
459 break;
460 } else {
461 usage2(_("Timeout interval must be a positive integer"), optarg);
462 }
463
464 /* See comments for 'c' */
465 case 'w': /* warning threshold */
553 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { 466 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
554 die(STATE_UNKNOWN, "Argument for --warning invalid or missing: %s\n", optarg); 467 die(STATE_UNKNOWN, "Argument for --warning invalid or missing: %s\n", optarg);
555 } 468 }
556 469
557 if (strstr(optarg, "%")) { 470 if (strstr(optarg, "%")) {
558 if (*optarg == '@') { 471 if (*optarg == '@') {
559 warn_freespace_percent = optarg; 472 warn_freespace_percent = optarg;
560 } else { 473 } else {
561 xasprintf(&warn_freespace_percent, "@%s", optarg); 474 xasprintf(&warn_freespace_percent, "@%s", optarg);
562 } 475 }
563 } else { 476 } else {
564 if (*optarg == '@') { 477 if (*optarg == '@') {
565 warn_freespace_units = optarg; 478 warn_freespace_units = optarg;
566 } else { 479 } else {
567 xasprintf(&warn_freespace_units, "@%s", optarg); 480 xasprintf(&warn_freespace_units, "@%s", optarg);
568 } 481 }
569 } 482 }
570 break; 483 break;
571 484
572 /* Awful mistake where the range values do not make sense. Normally, 485 /* Awful mistake where the range values do not make sense. Normally,
573 you alert if the value is within the range, but since we are using 486 * you alert if the value is within the range, but since we are using
574 freespace, we have to alert if outside the range. Thus we artificially 487 * freespace, we have to alert if outside the range. Thus we artificially
575 force @ at the beginning of the range, so that it is backwards compatible 488 * force @ at the beginning of the range, so that it is backwards compatible
576 */ 489 */
577 case 'c': /* critical threshold */ 490 case 'c': /* critical threshold */
578 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { 491 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
579 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); 492 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg);
580 } 493 }
581 494
582 if (strstr(optarg, "%")) { 495 if (strstr(optarg, "%")) {
583 if (*optarg == '@') { 496 if (*optarg == '@') {
584 crit_freespace_percent = optarg; 497 crit_freespace_percent = optarg;
585 } else { 498 } else {
586 xasprintf(&crit_freespace_percent, "@%s", optarg); 499 xasprintf(&crit_freespace_percent, "@%s", optarg);
587 } 500 }
588 } else { 501 } else {
589 if (*optarg == '@') { 502 if (*optarg == '@') {
590 crit_freespace_units = optarg; 503 crit_freespace_units = optarg;
591 } else { 504 } else {
592 xasprintf(&crit_freespace_units, "@%s", optarg); 505 xasprintf(&crit_freespace_units, "@%s", optarg);
593 } 506 }
594 } 507 }
595 break; 508 break;
596
597 case 'W': /* warning inode threshold */
598 if (*optarg == '@') {
599 warn_freeinodes_percent = optarg;
600 } else {
601 xasprintf(&warn_freeinodes_percent, "@%s", optarg);
602 }
603 break;
604 case 'K': /* critical inode threshold */
605 if (*optarg == '@') {
606 crit_freeinodes_percent = optarg;
607 } else {
608 xasprintf(&crit_freeinodes_percent, "@%s", optarg);
609 }
610 break;
611 case 'u':
612 if (units)
613 free(units);
614 if (! strcasecmp (optarg, "bytes")) {
615 mult = (uintmax_t)1;
616 units = strdup ("B");
617 } else if (!strcmp(optarg, "KiB")) {
618 mult = (uintmax_t)1024;
619 units = strdup ("KiB");
620 } else if (! strcmp (optarg, "kB")) {
621 mult = (uintmax_t)1000;
622 units = strdup ("kB");
623 } else if (!strcmp(optarg, "MiB")) {
624 mult = (uintmax_t)1024 * 1024;
625 units = strdup ("MiB");
626 } else if (! strcmp (optarg, "MB")) {
627 mult = (uintmax_t)1000 * 1000;
628 units = strdup ("MB");
629 } else if (!strcmp(optarg, "GiB")) {
630 mult = (uintmax_t)1024 * 1024 * 1024;
631 units = strdup ("GiB");
632 } else if (! strcmp (optarg, "GB")){
633 mult = (uintmax_t)1000 * 1000 * 1000;
634 units = strdup ("GB");
635 } else if (!strcmp(optarg, "TiB")) {
636 mult = (uintmax_t)1024 * 1024 * 1024 * 1024;
637 units = strdup ("TiB");
638 } else if (! strcmp (optarg, "TB")) {
639 mult = (uintmax_t)1000 * 1000 * 1000 * 1000;
640 units = strdup ("TB");
641 } else if (!strcmp(optarg, "PiB")) {
642 mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024;
643 units = strdup ("PiB");
644 } else if (! strcmp (optarg, "PB")){
645 mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000;
646 units = strdup ("PB");
647 } else {
648 die (STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
649 }
650 if (units == NULL)
651 die (STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
652 break;
653 case 'k': /* display mountpoint */
654 mult = 1024;
655 if (units)
656 free(units);
657 units = strdup ("kiB");
658 break;
659 case 'm': /* display mountpoint */
660 mult = 1024 * 1024;
661 if (units)
662 free(units);
663 units = strdup ("MiB");
664 break;
665 case 'L':
666 stat_remote_fs = 1;
667 /* fallthrough */
668 case 'l':
669 show_local_fs = 1;
670 break;
671 case 'P':
672 display_inodes_perfdata = 1;
673 break;
674 case 'p': /* select path */
675 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
676 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
677 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
678 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
679 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
680 }
681
682 /* add parameter if not found. overwrite thresholds if path has already been added */
683 if (! (se = np_find_parameter(path_select_list, optarg))) {
684 se = np_add_parameter(&path_select_list, optarg);
685
686 if (stat(optarg, &stat_buf[0]) && ignore_missing == true) {
687 path_ignored = true;
688 break;
689 }
690 }
691 se->group = group;
692 set_all_thresholds(se);
693
694 /* With autofs, it is required to stat() the path before re-populating the mount_list */
695 if (!stat_path(se)) {
696 break;
697 }
698 /* NB: We can't free the old mount_list "just like that": both list pointers and struct
699 * pointers are copied around. One of the reason it wasn't done yet is that other parts
700 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
701 mount_list = read_file_system_list (0);
702 np_set_best_match(se, mount_list, exact_match);
703
704 path_selected = true;
705 break;
706 case 'x': /* exclude path or partition */
707 np_add_name(&dp_exclude_list, optarg);
708 break;
709 case 'X': /* exclude file system type */
710 err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED);
711 if (err != 0) {
712 regerror (err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
713 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
714 }
715 break;
716 case 'N': /* include file system type */
717 err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED);
718 if (err != 0) {
719 regerror (err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
720 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
721 }
722 break;
723 case 'v': /* verbose */
724 verbose++;
725 break;
726 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
727 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
728 erronly = true;
729 break;
730 case 'e':
731 erronly = true;
732 break;
733 case 'E':
734 if (path_selected)
735 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
736 exact_match = true;
737 break;
738 case 'f':
739 freespace_ignore_reserved = true;
740 break;
741 case 'g':
742 if (path_selected)
743 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
744 group = optarg;
745 break;
746 case 'I':
747 cflags |= REG_ICASE;
748 // Intentional fallthrough
749 case 'i':
750 if (!path_selected)
751 die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
752 err = regcomp(&re, optarg, cflags);
753 if (err != 0) {
754 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
755 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
756 }
757
758 temp_list = path_select_list;
759
760 previous = NULL;
761 while (temp_list) {
762 if (temp_list->best_match) {
763 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
764
765 if (verbose >=3)
766 printf("ignoring %s matching regex\n", temp_list->name);
767
768 temp_list = np_del_parameter(temp_list, previous);
769 /* pointer to first element needs to be updated if first item gets deleted */
770 if (previous == NULL)
771 path_select_list = temp_list;
772 } else {
773 previous = temp_list;
774 temp_list = temp_list->name_next;
775 }
776 } else {
777 previous = temp_list;
778 temp_list = temp_list->name_next;
779 }
780 }
781
782
783 cflags = default_cflags;
784 break;
785
786 case 'n':
787 ignore_missing = true;
788 break;
789 case 'A':
790 optarg = strdup(".*");
791 // Intentional fallthrough
792 case 'R':
793 cflags |= REG_ICASE;
794 // Intentional fallthrough
795 case 'r':
796 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
797 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
798 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
799 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
800 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n"));
801 }
802
803 err = regcomp(&re, optarg, cflags);
804 if (err != 0) {
805 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
806 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
807 }
808
809 for (me = mount_list; me; me = me->me_next) {
810 if (np_regex_match_mount_entry(me, &re)) {
811 fnd = true;
812 if (verbose >= 3)
813 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
814
815 /* add parameter if not found. overwrite thresholds if path has already been added */
816 if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
817 se = np_add_parameter(&path_select_list, me->me_mountdir);
818 }
819 se->group = group;
820 set_all_thresholds(se);
821 }
822 }
823
824 if (!fnd && ignore_missing == true) {
825 path_ignored = true;
826 path_selected = true;
827 break;
828 } else if (!fnd)
829 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
830 _("Regular expression did not match any path or disk"), optarg);
831
832 fnd = false;
833 path_selected = true;
834 np_set_best_match(path_select_list, mount_list, exact_match);
835 cflags = default_cflags;
836
837 break;
838 case 'M': /* display mountpoint */
839 display_mntp = true;
840 break;
841 case 'C':
842 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
843 if (path_selected == false) {
844 struct parameter_list *path;
845 for (me = mount_list; me; me = me->me_next) {
846 if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
847 path = np_add_parameter(&path_select_list, me->me_mountdir);
848 path->best_match = me;
849 path->group = group;
850 set_all_thresholds(path);
851 }
852 }
853 warn_freespace_units = NULL;
854 crit_freespace_units = NULL;
855 warn_usedspace_units = NULL;
856 crit_usedspace_units = NULL;
857 warn_freespace_percent = NULL;
858 crit_freespace_percent = NULL;
859 warn_usedspace_percent = NULL;
860 crit_usedspace_percent = NULL;
861 warn_usedinodes_percent = NULL;
862 crit_usedinodes_percent = NULL;
863 warn_freeinodes_percent = NULL;
864 crit_freeinodes_percent = NULL;
865
866 path_selected = false;
867 group = NULL;
868 break;
869 case 'V': /* version */
870 print_revision (progname, NP_VERSION);
871 exit (STATE_UNKNOWN);
872 case 'h': /* help */
873 print_help ();
874 exit (STATE_UNKNOWN);
875 case '?': /* help */
876 usage (_("Unknown argument"));
877 }
878 }
879
880 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
881 c = optind;
882 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
883 warn_usedspace_percent = argv[c++];
884
885 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
886 crit_usedspace_percent = argv[c++];
887
888 if (argc > c) {
889 se = np_add_parameter(&path_select_list, strdup(argv[c++]));
890 path_selected = true;
891 set_all_thresholds(se);
892 }
893
894 if (units == NULL) {
895 units = strdup ("MiB");
896 mult = (uintmax_t)1024 * 1024;
897 }
898
899 return true;
900}
901 509
510 case 'W': /* warning inode threshold */
511 if (*optarg == '@') {
512 warn_freeinodes_percent = optarg;
513 } else {
514 xasprintf(&warn_freeinodes_percent, "@%s", optarg);
515 }
516 break;
517 case 'K': /* critical inode threshold */
518 if (*optarg == '@') {
519 crit_freeinodes_percent = optarg;
520 } else {
521 xasprintf(&crit_freeinodes_percent, "@%s", optarg);
522 }
523 break;
524 case 'u':
525 if (!strcasecmp(optarg, "bytes")) {
526 unit = Bytes_Factor;
527 } else if (!strcmp(optarg, "KiB")) {
528 unit = KibiBytes_factor;
529 } else if (!strcmp(optarg, "kB")) {
530 unit = KiloBytes_factor;
531 } else if (!strcmp(optarg, "MiB")) {
532 unit = MebiBytes_factor;
533 } else if (!strcmp(optarg, "MB")) {
534 unit = MegaBytes_factor;
535 } else if (!strcmp(optarg, "GiB")) {
536 unit = GibiBytes_factor;
537 } else if (!strcmp(optarg, "GB")) {
538 unit = GigaBytes_factor;
539 } else if (!strcmp(optarg, "TiB")) {
540 unit = TebiBytes_factor;
541 } else if (!strcmp(optarg, "TB")) {
542 unit = TeraBytes_factor;
543 } else if (!strcmp(optarg, "PiB")) {
544 unit = PebiBytes_factor;
545 } else if (!strcmp(optarg, "PB")) {
546 unit = PetaBytes_factor;
547 } else {
548 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
549 }
550 break;
551 case 'k':
552 unit = KibiBytes_factor;
553 break;
554 case 'm':
555 unit = MebiBytes_factor;
556 break;
557 case display_unit_index:
558 if (!strcasecmp(optarg, "bytes")) {
559 result.config.display_unit = Bytes;
560 } else if (!strcmp(optarg, "KiB")) {
561 result.config.display_unit = KibiBytes;
562 } else if (!strcmp(optarg, "kB")) {
563 result.config.display_unit = KiloBytes;
564 } else if (!strcmp(optarg, "MiB")) {
565 result.config.display_unit = MebiBytes;
566 } else if (!strcmp(optarg, "MB")) {
567 result.config.display_unit = MegaBytes;
568 } else if (!strcmp(optarg, "GiB")) {
569 result.config.display_unit = GibiBytes;
570 } else if (!strcmp(optarg, "GB")) {
571 result.config.display_unit = GigaBytes;
572 } else if (!strcmp(optarg, "TiB")) {
573 result.config.display_unit = TebiBytes;
574 } else if (!strcmp(optarg, "TB")) {
575 result.config.display_unit = TeraBytes;
576 } else if (!strcmp(optarg, "PiB")) {
577 result.config.display_unit = PebiBytes;
578 } else if (!strcmp(optarg, "PB")) {
579 result.config.display_unit = PetaBytes;
580 } else {
581 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
582 }
583 break;
584 case 'L':
585 result.config.stat_remote_fs = true;
586 /* fallthrough */
587 case 'l':
588 result.config.show_local_fs = true;
589 break;
590 case 'P':
591 result.config.display_inodes_perfdata = true;
592 break;
593 case 'p': /* select path */ {
594 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
595 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
596 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
597 _("Must set a threshold value before using -p\n"));
598 }
902 599
600 /* add parameter if not found. overwrite thresholds if path has already been added */
601 parameter_list_elem *search_entry;
602 if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) {
603 search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg);
903 604
904void 605 // struct stat stat_buf = {};
905print_path (const char *mypath) 606 // if (stat(optarg, &stat_buf) && result.config.ignore_missing) {
906{ 607 // result.config.path_ignored = true;
907 if (mypath == NULL) 608 // break;
908 printf ("\n"); 609 // }
909 else 610 }
910 printf (_(" for %s\n"), mypath); 611 search_entry->group = group;
911} 612 set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units,
613 warn_freespace_percent, crit_freespace_percent,
614
615 warn_freeinodes_percent, crit_freeinodes_percent);
616
617 /* With autofs, it is required to stat() the path before re-populating the mount_list */
618 // if (!stat_path(se, result.config.ignore_missing)) {
619 // break;
620 // }
621 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
622 result.config.exact_match);
623
624 path_selected = true;
625 } break;
626 case 'x': /* exclude path or partition */
627 np_add_name(&result.config.device_path_exclude_list, optarg);
628 break;
629 case 'X': /* exclude file system type */ {
630 int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED);
631 if (err != 0) {
632 char errbuf[MAX_INPUT_BUFFER];
633 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
634 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
635 _("Could not compile regular expression"), errbuf);
636 }
637 break;
638 case 'N': /* include file system type */
639 err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED);
640 if (err != 0) {
641 char errbuf[MAX_INPUT_BUFFER];
642 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
643 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
644 _("Could not compile regular expression"), errbuf);
645 }
646 } break;
647 case 'v': /* verbose */
648 verbose++;
649 break;
650 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
651 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
652 result.config.erronly = true;
653 break;
654 case 'e':
655 result.config.erronly = true;
656 break;
657 case 'E':
658 if (path_selected) {
659 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
660 _("Must set -E before selecting paths\n"));
661 }
662 result.config.exact_match = true;
663 break;
664 case 'f':
665 result.config.freespace_ignore_reserved = true;
666 break;
667 case 'g':
668 if (path_selected) {
669 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
670 _("Must set group value before selecting paths\n"));
671 }
672 group = optarg;
673 break;
674 case 'I':
675 cflags |= REG_ICASE;
676 // Intentional fallthrough
677 case 'i': {
678 if (!path_selected) {
679 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"),
680 _("Paths need to be selected before using -i/-I. Use -A to select all paths "
681 "explicitly"));
682 }
683 regex_t regex;
684 int err = regcomp(&regex, optarg, cflags);
685 if (err != 0) {
686 char errbuf[MAX_INPUT_BUFFER];
687 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
688 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
689 _("Could not compile regular expression"), errbuf);
690 }
912 691
692 for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) {
693 if (elem->best_match) {
694 if (np_regex_match_mount_entry(elem->best_match, &regex)) {
913 695
914void 696 if (verbose >= 3) {
915set_all_thresholds (struct parameter_list *path) 697 printf("ignoring %s matching regex\n", elem->name);
916{ 698 }
917 if (path->freespace_units != NULL) free(path->freespace_units);
918 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
919 if (path->freespace_percent != NULL) free (path->freespace_percent);
920 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
921 if (path->usedspace_units != NULL) free (path->usedspace_units);
922 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
923 if (path->usedspace_percent != NULL) free (path->usedspace_percent);
924 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
925 if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
926 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
927 if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
928 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
929}
930 699
931void 700 elem = mp_int_fs_list_del(&result.config.path_select_list, elem);
932print_help (void) 701 continue;
933{ 702 }
934 print_revision (progname, NP_VERSION); 703 }
935
936 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
937 printf (COPYRIGHT, copyright, email);
938
939 printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
940 printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
941
942 printf ("\n\n");
943
944 print_usage ();
945
946 printf (UT_HELP_VRSN);
947 printf (UT_EXTRA_OPTS);
948
949 printf (" %s\n", "-w, --warning=INTEGER");
950 printf (" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
951 printf (" %s\n", "-w, --warning=PERCENT%");
952 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
953 printf (" %s\n", "-c, --critical=INTEGER");
954 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
955 printf (" %s\n", "-c, --critical=PERCENT%");
956 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of disk space is free"));
957 printf (" %s\n", "-W, --iwarning=PERCENT%");
958 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
959 printf (" %s\n", "-K, --icritical=PERCENT%");
960 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
961 printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
962 printf (" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
963 printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
964 printf (" %s\n", _("Ignore device (only works if -p unspecified)"));
965 printf (" %s\n", "-C, --clear");
966 printf (" %s\n", _("Clear thresholds"));
967 printf (" %s\n", "-E, --exact-match");
968 printf (" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
969 printf (" %s\n", "-e, --errors-only");
970 printf (" %s\n", _("Display only devices/mountpoints with errors"));
971 printf (" %s\n", "-f, --freespace-ignore-reserved");
972 printf (" %s\n", _("Don't account root-reserved blocks into freespace in perfdata"));
973 printf (" %s\n", "-P, --iperfdata");
974 printf (" %s\n", _("Display inode usage in perfdata"));
975 printf (" %s\n", "-g, --group=NAME");
976 printf (" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
977 printf (" %s\n", "-k, --kilobytes");
978 printf (" %s\n", _("Same as '--units kB'"));
979 printf (" %s\n", "-l, --local");
980 printf (" %s\n", _("Only check local filesystems"));
981 printf (" %s\n", "-L, --stat-remote-fs");
982 printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
983 printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
984 printf (" %s\n", "-M, --mountpoint");
985 printf (" %s\n", _("Display the (block) device instead of the mount point"));
986 printf (" %s\n", "-m, --megabytes");
987 printf (" %s\n", _("Same as '--units MB'"));
988 printf (" %s\n", "-A, --all");
989 printf (" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
990 printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
991 printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
992 printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
993 printf (" %s\n", _("Regular expression for path or partition (may be repeated)"));
994 printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
995 printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
996 printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
997 printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
998 printf (" %s\n", "-n, --ignore-missing");
999 printf (" %s\n", _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
1000 printf (" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
1001 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1002 printf (" %s\n", "-u, --units=STRING");
1003 printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
1004 printf (UT_VERBOSE);
1005 printf (" %s\n", "-X, --exclude-type=TYPE_REGEX");
1006 printf (" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
1007 printf (" %s\n", "-N, --include-type=TYPE_REGEX");
1008 printf (" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)"));
1009
1010 printf ("\n");
1011 printf ("%s\n", _("General usage hints:"));
1012 printf (" %s\n", _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
1013 printf (" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
1014 printf (" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} {thresholds b} ...\""));
1015
1016
1017
1018 printf ("\n");
1019 printf ("%s\n", _("Examples:"));
1020 printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
1021 printf (" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
1022 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
1023 printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
1024 printf (" %s\n\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
1025 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
1026 printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
1027
1028 printf (UT_SUPPORT);
1029}
1030 704
705 elem = mp_int_fs_list_get_next(elem);
706 }
1031 707
708 cflags = default_cflags;
709 } break;
710 case 'n':
711 result.config.ignore_missing = true;
712 break;
713 case 'A':
714 optarg = strdup(".*");
715 // Intentional fallthrough
716 case 'R':
717 cflags |= REG_ICASE;
718 // Intentional fallthrough
719 case 'r': {
720 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
721 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
722 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
723 _("Must set a threshold value before using -r/-R/-A "
724 "(--ereg-path/--eregi-path/--all)\n"));
725 }
1032 726
1033void 727 regex_t regex;
1034print_usage (void) 728 int err = regcomp(&regex, optarg, cflags);
1035{ 729 if (err != 0) {
1036 printf ("%s\n", _("Usage:")); 730 char errbuf[MAX_INPUT_BUFFER];
1037 printf (" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c absolute_limit|-c percentage_limit%% | -K inode_percentage_limit } {-p path | -x device}\n", progname); 731 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
1038 printf ("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); 732 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
1039 printf ("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); 733 _("Could not compile regular expression"), errbuf);
734 }
735
736 bool found = false;
737 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
738 if (np_regex_match_mount_entry(me, &regex)) {
739 found = true;
740 if (verbose >= 3) {
741 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir,
742 optarg);
743 }
744
745 /* add parameter if not found. overwrite thresholds if path has already been
746 * added */
747 parameter_list_elem *se = NULL;
748 if (!(se = mp_int_fs_list_find(result.config.path_select_list,
749 me->me_mountdir))) {
750 se =
751 mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
752 }
753 se->group = group;
754 set_all_thresholds(se, warn_freespace_units, crit_freespace_units,
755 warn_freespace_percent, crit_freespace_percent,
756 warn_freeinodes_percent, crit_freeinodes_percent);
757 }
758 }
759
760 if (!found) {
761 if (result.config.ignore_missing) {
762 result.config.path_ignored = true;
763 path_selected = true;
764 break;
765 }
766
767 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
768 _("Regular expression did not match any path or disk"), optarg);
769 }
770
771 path_selected = true;
772 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
773 result.config.exact_match);
774 cflags = default_cflags;
775
776 } break;
777 case 'M': /* display mountpoint */
778 result.config.display_mntp = true;
779 break;
780 case 'C': {
781 /* add all mount entries to path_select list if no partitions have been explicitly
782 * defined using -p */
783 if (!path_selected) {
784 parameter_list_elem *path;
785 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
786 if (!(path = mp_int_fs_list_find(result.config.path_select_list,
787 me->me_mountdir))) {
788 path =
789 mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
790 }
791 path->best_match = me;
792 path->group = group;
793 set_all_thresholds(path, warn_freespace_units, crit_freespace_units,
794 warn_freespace_percent, crit_freespace_percent,
795 warn_freeinodes_percent, crit_freeinodes_percent);
796 }
797 }
798
799 warn_freespace_units = NULL;
800 crit_freespace_units = NULL;
801 warn_freespace_percent = NULL;
802 crit_freespace_percent = NULL;
803 warn_freeinodes_percent = NULL;
804 crit_freeinodes_percent = NULL;
805
806 path_selected = false;
807 group = NULL;
808 } break;
809 case 'V': /* version */
810 print_revision(progname, NP_VERSION);
811 exit(STATE_UNKNOWN);
812 case 'h': /* help */
813 print_help();
814 exit(STATE_UNKNOWN);
815 case '?': /* help */
816 usage(_("Unknown argument"));
817 case output_format_index: {
818 parsed_output_format parser = mp_parse_output_format(optarg);
819 if (!parser.parsing_success) {
820 // TODO List all available formats here, maybe add anothoer usage function
821 printf("Invalid output format: %s\n", optarg);
822 exit(STATE_UNKNOWN);
823 }
824
825 result.config.output_format_is_set = true;
826 result.config.output_format = parser.output_format;
827 break;
828 }
829 }
830 }
831
832 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
833 int index = optind;
834
835 if (argc > index && is_intnonneg(argv[index])) {
836 if (verbose > 0) {
837 printf("Got an positional warn threshold: %s\n", argv[index]);
838 }
839 char *range = argv[index++];
840 mp_range_parsed tmp = mp_parse_range_string(range);
841 if (tmp.error != MP_PARSING_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;
848
849 warn_freespace_percent = mp_range_to_string(tmp_range);
850
851 if (verbose > 0) {
852 printf("Positional warning threshold transformed to: %s\n", warn_freespace_percent);
853 }
854 }
855
856 if (argc > index && is_intnonneg(argv[index])) {
857 if (verbose > 0) {
858 printf("Got an positional crit threshold: %s\n", argv[index]);
859 }
860 char *range = argv[index++];
861 mp_range_parsed tmp = mp_parse_range_string(range);
862 if (tmp.error != MP_PARSING_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++]));
883 path_selected = true;
884 set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent,
885 crit_freespace_percent, warn_freeinodes_percent,
886 crit_freeinodes_percent);
887 }
888
889 // If a list of paths has not been explicitly selected, find entire
890 // mount list and create list of paths
891 if (!path_selected && !result.config.path_ignored) {
892 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
893 if (me->me_dummy != 0) {
894 // just do not add dummy filesystems
895 continue;
896 }
897
898 parameter_list_elem *path = NULL;
899 if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) {
900 path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
901 }
902 path->best_match = me;
903 path->group = group;
904 set_all_thresholds(path, warn_freespace_units, crit_freespace_units,
905 warn_freespace_percent, crit_freespace_percent,
906 warn_freeinodes_percent, crit_freeinodes_percent);
907 }
908 }
909
910 // Set thresholds to the appropriate unit
911 for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp;
912 tmp = mp_int_fs_list_get_next(tmp)) {
913
914 mp_perfdata_value factor = mp_create_pd_value(unit);
915
916 if (tmp->freespace_units.critical_is_set) {
917 tmp->freespace_units.critical =
918 mp_range_multiply(tmp->freespace_units.critical, factor);
919 }
920 if (tmp->freespace_units.warning_is_set) {
921 tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor);
922 }
923 }
924
925 return result;
1040} 926}
1041 927
1042bool 928void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
1043stat_path (struct parameter_list *p) 929 char *crit_freespace_units, char *warn_freespace_percent,
1044{ 930 char *crit_freespace_percent, char *warn_freeinodes_percent,
1045 /* Stat entry to check that dir exists and is accessible */ 931 char *crit_freeinodes_percent) {
1046 if (verbose >= 3) 932 mp_range_parsed tmp;
1047 printf("calling stat on %s\n", p->name); 933
1048 if (stat (p->name, &stat_buf[0])) { 934 if (warn_freespace_units) {
1049 if (verbose >= 3) 935 tmp = mp_parse_range_string(warn_freespace_units);
1050 printf("stat failed on %s\n", p->name); 936 path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range);
1051 if (ignore_missing == true) { 937 }
1052 return false; 938
1053 } else { 939 if (crit_freespace_units) {
1054 printf("DISK %s - ", _("CRITICAL")); 940 tmp = mp_parse_range_string(crit_freespace_units);
1055 die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); 941 path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range);
1056 } 942 }
1057 } 943
1058 return true; 944 if (warn_freespace_percent) {
945 tmp = mp_parse_range_string(warn_freespace_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 }
1059} 963}
1060 964
965void print_help(void) {
966 print_revision(progname, NP_VERSION);
967
968 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
969 printf(COPYRIGHT, copyright, email);
970
971 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
972 printf("%s\n",
973 _("and generates an alert if free space is less than one of the threshold values"));
974
975 printf("\n\n");
976
977 print_usage();
978
979 printf(UT_HELP_VRSN);
980 printf(UT_EXTRA_OPTS);
981
982 printf(" %s\n", "-w, --warning=INTEGER");
983 printf(" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
984 printf(" %s\n", "-w, --warning=PERCENT%");
985 printf(" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
986 printf(" %s\n", "-c, --critical=INTEGER");
987 printf(" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
988 printf(" %s\n", "-c, --critical=PERCENT%");
989 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of disk space is free"));
990 printf(" %s\n", "-W, --iwarning=PERCENT%");
991 printf(" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
992 printf(" %s\n", "-K, --icritical=PERCENT%");
993 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
994 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION");
995 printf(" %s\n",
996 _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
997 printf(" %s\n", "-x, --exclude_device=PATH <STRING>");
998 printf(" %s\n", _("Ignore device (only works if -p unspecified)"));
999 printf(" %s\n", "-C, --clear");
1000 printf(" %s\n", _("Clear thresholds"));
1001 printf(" %s\n", "-E, --exact-match");
1002 printf(" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
1003 printf(" %s\n", "-e, --errors-only");
1004 printf(" %s\n", _("Display only devices/mountpoints with errors"));
1005 printf(" %s\n", "-f, --freespace-ignore-reserved");
1006 printf(" %s\n", _("Don't account root-reserved blocks into freespace in perfdata"));
1007 printf(" %s\n", "-P, --iperfdata");
1008 printf(" %s\n", _("Display inode usage in perfdata"));
1009 printf(" %s\n", "-g, --group=NAME");
1010 printf(" %s\n",
1011 _("Group paths. Thresholds apply to (free-)space of all partitions together"));
1012 printf(" %s\n", "-l, --local");
1013 printf(" %s\n", _("Only check local filesystems"));
1014 printf(" %s\n", "-L, --stat-remote-fs");
1015 printf(
1016 " %s\n",
1017 _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
1018 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
1019 printf(" %s\n", "-M, --mountpoint");
1020 printf(" %s\n", _("Display the (block) device instead of the mount point"));
1021 printf(" %s\n", "-A, --all");
1022 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
1023 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
1024 printf(" %s\n",
1025 _("Case insensitive regular expression for path/partition (may be repeated)"));
1026 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
1027 printf(" %s\n", _("Regular expression for path or partition (may be repeated)"));
1028 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
1029 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) "
1030 "(may be repeated)"));
1031 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
1032 printf(" %s\n",
1033 _("Regular expression to ignore selected path or partition (may be repeated)"));
1034 printf(" %s\n", "-n, --ignore-missing");
1035 printf(" %s\n",
1036 _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
1037 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
1038 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1039 printf(" %s\n", "-u, --units=STRING");
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'"));
1051 printf(UT_VERBOSE);
1052 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX");
1053 printf(" %s\n",
1054 _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
1055 printf(" %s\n", "-N, --include-type=TYPE_REGEX");
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);
1060
1061 printf("\n");
1062 printf("%s\n", _("General usage hints:"));
1063 printf(
1064 " %s\n",
1065 _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
1066 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
1067 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} "
1068 "{thresholds b} ...\""));
1069
1070 printf("\n");
1071 printf("%s\n", _("Examples:"));
1072 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
1073 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
1074 printf(" %s\n",
1075 "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
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"));
1081 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
1082 printf(" %s\n",
1083 _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
1084
1085 printf(UT_SUPPORT);
1086}
1061 1087
1062void 1088void print_usage(void) {
1063get_stats (struct parameter_list *p, struct fs_usage *fsp) { 1089 printf("%s\n", _("Usage:"));
1064 struct parameter_list *p_list; 1090 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c "
1065 struct fs_usage tmpfsp; 1091 "absolute_limit|-c percentage_limit%% | -K "
1066 int first = 1; 1092 "inode_percentage_limit } {-p path | -x device}\n",
1093 progname);
1094 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
1095 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n");
1096}
1067 1097
1068 if (p->group == NULL) { 1098bool stat_path(parameter_list_elem *parameters, bool ignore_missing) {
1069 get_path_stats(p,fsp); 1099 /* Stat entry to check that dir exists and is accessible */
1070 } else { 1100 if (verbose >= 3) {
1071 /* find all group members */ 1101 printf("calling stat on %s\n", parameters->name);
1072 for (p_list = path_select_list; p_list; p_list=p_list->name_next) { 1102 }
1073#ifdef __CYGWIN__ 1103
1074 if (strncmp(p_list->name, "/cygdrive/", 10) != 0) 1104 struct stat stat_buf = {0};
1075 continue; 1105 if (stat(parameters->name, &stat_buf)) {
1076#endif 1106 if (verbose >= 3) {
1077 if (p_list->group && ! (strcmp(p_list->group, p->group))) { 1107 printf("stat failed on %s\n", parameters->name);
1078 if (! stat_path(p_list)) 1108 }
1079 continue; 1109 if (ignore_missing) {
1080 get_fs_usage (p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp); 1110 return false;
1081 get_path_stats(p_list, &tmpfsp); 1111 }
1082 if (verbose >= 3) 1112 printf("DISK %s - ", _("CRITICAL"));
1083 printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n", 1113 die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"),
1084 p_list->group, 1114 strerror(errno));
1085 tmpfsp.fsu_blocks, 1115 }
1086 tmpfsp.fsu_blocksize, 1116
1087 p_list->best_match->me_mountdir, 1117 return true;
1088 p_list->dused_units, 1118}
1089 p_list->dfree_units,
1090 p_list->dtotal_units,
1091 mult);
1092
1093 /* prevent counting the first FS of a group twice since its parameter_list entry
1094 * is used to carry the information of all file systems of the entire group */
1095 if (! first) {
1096 p->total += p_list->total;
1097 p->available += p_list->available;
1098 p->available_to_root += p_list->available_to_root;
1099 p->used += p_list->used;
1100
1101 p->dused_units += p_list->dused_units;
1102 p->dfree_units += p_list->dfree_units;
1103 p->dtotal_units += p_list->dtotal_units;
1104 p->inodes_total += p_list->inodes_total;
1105 p->inodes_free += p_list->inodes_free;
1106 p->inodes_free_to_root += p_list->inodes_free_to_root;
1107 p->inodes_used += p_list->inodes_used;
1108 }
1109 first = 0;
1110 }
1111 if (verbose >= 3)
1112 printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n",
1113 p->group,
1114 p->dused_units,
1115 p->dfree_units,
1116 p->dtotal_units,
1117 tmpfsp.fsu_blocksize,
1118 mult);
1119 }
1120 /* modify devname and mountdir for output */
1121 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
1122 }
1123 /* finally calculate percentages for either plain FS or summed up group */
1124 p->dused_pct = calculate_percent( p->used, p->used + p->available ); /* used + available can never be > uintmax */
1125 p->dfree_pct = 100.0 - p->dused_pct;
1126 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1127 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1128 1119
1120static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp,
1121 bool freespace_ignore_reserved) {
1122 uintmax_t available = fsp.fsu_bavail;
1123 uintmax_t available_to_root = fsp.fsu_bfree;
1124 uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree;
1125 uintmax_t total;
1126
1127 if (freespace_ignore_reserved) {
1128 /* option activated : we subtract the root-reserved space from the total */
1129 total = fsp.fsu_blocks - available_to_root + available;
1130 } else {
1131 /* default behaviour : take all the blocks into account */
1132 total = fsp.fsu_blocks;
1133 }
1134
1135 parameters.used_bytes = used * fsp.fsu_blocksize;
1136 parameters.free_bytes = available * fsp.fsu_blocksize;
1137 parameters.total_bytes = total * fsp.fsu_blocksize;
1138
1139 /* Free file nodes. Not sure the workaround is required, but in case...*/
1140 parameters.inodes_free = fsp.fsu_ffree;
1141 parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */
1142 parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree;
1143
1144 if (freespace_ignore_reserved) {
1145 /* option activated : we subtract the root-reserved inodes from the total */
1146 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */
1147 /* for others, fsp->fsu_ffree == fsp->fsu_favail */
1148 parameters.inodes_total =
1149 fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free;
1150 } else {
1151 /* default behaviour : take all the inodes into account */
1152 parameters.inodes_total = fsp.fsu_files;
1153 }
1154
1155 return parameters;
1129} 1156}
1130 1157
1131void 1158mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata,
1132get_path_stats (struct parameter_list *p, struct fs_usage *fsp) { 1159 byte_unit unit) {
1133 p->available = fsp->fsu_bavail; 1160 mp_subcheck result = mp_subcheck_init();
1134 p->available_to_root = fsp->fsu_bfree; 1161 result = mp_set_subcheck_default_state(result, STATE_UNKNOWN);
1135 p->used = fsp->fsu_blocks - fsp->fsu_bfree; 1162 xasprintf(&result.output, "%s", measurement_unit.name);
1136 if (freespace_ignore_reserved) { 1163
1137 /* option activated : we subtract the root-reserved space from the total */ 1164 if (!measurement_unit.is_group && measurement_unit.filesystem_type) {
1138 p->total = fsp->fsu_blocks - p->available_to_root + p->available; 1165 xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type);
1139 } else { 1166 }
1140 /* default behaviour : take all the blocks into account */ 1167
1141 p->total = fsp->fsu_blocks; 1168 /* Threshold comparisons */
1142 } 1169
1143 1170 // ===============================
1144 p->dused_units = p->used*fsp->fsu_blocksize/mult; 1171 // Free space absolute values test
1145 p->dfree_units = p->available*fsp->fsu_blocksize/mult; 1172 mp_subcheck freespace_bytes_sc = mp_subcheck_init();
1146 p->dtotal_units = p->total*fsp->fsu_blocksize/mult; 1173 freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK);
1147 /* Free file nodes. Not sure the workaround is required, but in case...*/ 1174
1148 p->inodes_free = fsp->fsu_ffree; 1175 if (unit != Humanized) {
1149 p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ 1176 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)",
1150 p->inodes_used = fsp->fsu_files - fsp->fsu_ffree; 1177 (uintmax_t)(measurement_unit.free_bytes / unit), get_unit_string(unit),
1151 if (freespace_ignore_reserved) { 1178 (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit));
1152 /* option activated : we subtract the root-reserved inodes from the total */ 1179 } else {
1153 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ 1180 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)",
1154 /* for others, fsp->fsu_ffree == fsp->fsu_favail */ 1181 humanize_byte_value(measurement_unit.free_bytes, false),
1155 p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free; 1182 humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false));
1156 } else { 1183 }
1157 /* default behaviour : take all the inodes into account */ 1184
1158 p->inodes_total = fsp->fsu_files; 1185 mp_perfdata used_space = perfdata_init();
1159 } 1186 used_space.label = measurement_unit.name;
1160 np_add_name(&seen, p->best_match->me_mountdir); 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;
1161} 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 468bc958..56f91dae 100644
--- a/plugins/check_dns.c
+++ b/plugins/check_dns.c
@@ -1,36 +1,36 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dns plugin 3 * Monitoring check_dns plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_dns plugin 10 * This file contains the check_dns plugin
11* 11 *
12* LIMITATION: nslookup on Solaris 7 can return output over 2 lines, which 12 * LIMITATION: nslookup on Solaris 7 can return output over 2 lines, which
13* will not be picked up by this plugin 13 * will not be picked up by this plugin
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_dns"; 32const char *progname = "check_dns";
33const char *copyright = "2000-2008"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
@@ -39,579 +39,634 @@ const char *email = "devel@monitoring-plugins.org";
39#include "netutils.h" 39#include "netutils.h"
40#include "runcmd.h" 40#include "runcmd.h"
41 41
42int process_arguments (int, char **); 42#include "states.h"
43int validate_arguments (void); 43#include "check_dns.d/config.h"
44int error_scan (char *, bool *); 44
45bool ip_match_cidr(const char *, const char *); 45typedef struct {
46unsigned long ip2long(const char *); 46 int errorcode;
47void print_help (void); 47 check_dns_config config;
48void print_usage (void); 48} check_dns_config_wrapper;
49 49static check_dns_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50#define ADDRESS_LENGTH 256 50static check_dns_config_wrapper validate_arguments(check_dns_config_wrapper /*config_wrapper*/);
51char query_address[ADDRESS_LENGTH] = ""; 51static mp_state_enum error_scan(char * /*input_buffer*/, bool * /*is_nxdomain*/,
52char dns_server[ADDRESS_LENGTH] = ""; 52 const char /*dns_server*/[ADDRESS_LENGTH]);
53char ptr_server[ADDRESS_LENGTH] = ""; 53static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/);
54bool verbose = false; 54static unsigned long ip2long(const char * /*src*/);
55char **expected_address = NULL; 55static void print_help(void);
56int expected_address_cnt = 0; 56void print_usage(void);
57bool expect_nxdomain = false; 57
58 58static bool verbose = false;
59bool expect_authority = false; 59
60bool all_match = false; 60static int qstrcmp(const void *p1, const void *p2) {
61thresholds *time_thresholds = NULL;
62
63static int
64qstrcmp(const void *p1, const void *p2)
65{
66 /* The actual arguments to this function are "pointers to 61 /* The actual arguments to this function are "pointers to
67 pointers to char", but strcmp() arguments are "pointers 62 pointers to char", but strcmp() arguments are "pointers
68 to char", hence the following cast plus dereference */ 63 to char", hence the following cast plus dereference */
69 return strcmp(* (char * const *) p1, * (char * const *) p2); 64 return strcmp(*(char *const *)p1, *(char *const *)p2);
70} 65}
71 66
67int main(int argc, char **argv) {
68 setlocale(LC_ALL, "");
69 bindtextdomain(PACKAGE, LOCALEDIR);
70 textdomain(PACKAGE);
72 71
73int 72 /* Set signal handling and alarm */
74main (int argc, char **argv) 73 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
75{ 74 usage_va(_("Cannot catch SIGALRM"));
76 char *command_line = NULL; 75 }
77 char input_buffer[MAX_INPUT_BUFFER]; 76
78 char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */ 77 /* Parse extra opts if any */
79 char **addresses = NULL; 78 argv = np_extra_opts(&argc, argv, progname);
80 int n_addresses = 0; 79
81 char *msg = NULL; 80 check_dns_config_wrapper tmp = process_arguments(argc, argv);
82 char *temp_buffer = NULL; 81
83 bool non_authoritative = false; 82 if (tmp.errorcode == ERROR) {
84 int result = STATE_UNKNOWN; 83 usage_va(_("Could not parse arguments"));
85 double elapsed_time; 84 }
86 long microsec; 85
87 struct timeval tv; 86 const check_dns_config config = tmp.config;
88 bool parse_address = false; /* This flag scans for Address: but only after Name: */ 87
89 output chld_out, chld_err; 88 char *command_line = NULL;
90 bool is_nxdomain = false; 89 /* get the command to run */
91 90 xasprintf(&command_line, "%s %s %s", NSLOOKUP_COMMAND, config.query_address, config.dns_server);
92 setlocale (LC_ALL, ""); 91
93 bindtextdomain (PACKAGE, LOCALEDIR); 92 struct timeval tv;
94 textdomain (PACKAGE); 93 alarm(timeout_interval);
95 94 gettimeofday(&tv, NULL);
96 /* Set signal handling and alarm */ 95
97 if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 96 if (verbose) {
98 usage_va(_("Cannot catch SIGALRM")); 97 printf("%s\n", command_line);
99 } 98 }
100 99
101 /* Parse extra opts if any */ 100 output chld_out;
102 argv=np_extra_opts (&argc, argv, progname); 101 output chld_err;
103 102 char *msg = NULL;
104 if (process_arguments (argc, argv) == ERROR) { 103 mp_state_enum result = STATE_UNKNOWN;
105 usage_va(_("Could not parse arguments")); 104 /* run the command */
106 } 105 if ((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) {
107 106 msg = (char *)_("nslookup returned an error status");
108 /* get the command to run */ 107 result = STATE_WARNING;
109 xasprintf (&command_line, "%s %s %s", NSLOOKUP_COMMAND, query_address, dns_server); 108 }
110 109
111 alarm (timeout_interval); 110 /* =====
112 gettimeofday (&tv, NULL); 111 * scan stdout, main results get retrieved here
113 112 * =====
114 if (verbose) 113 */
115 printf ("%s\n", command_line); 114 char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */
116 115 char **addresses = NULL; // All addresses parsed from stdout
117 /* run the command */ 116 size_t n_addresses = 0; // counter for retrieved addresses
118 if((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) { 117 bool non_authoritative = false;
119 msg = (char *)_("nslookup returned an error status"); 118 bool is_nxdomain = false;
120 result = STATE_WARNING; 119 bool parse_address = false; /* This flag scans for Address: but only after Name: */
121 } 120 for (size_t i = 0; i < chld_out.lines; i++) {
122 121 if (addresses == NULL) {
123 /* scan stdout */ 122 addresses = malloc(sizeof(*addresses) * 10);
124 for(size_t i = 0; i < chld_out.lines; i++) { 123 } else if (!(n_addresses % 10)) {
125 if (addresses == NULL) 124 addresses = realloc(addresses, sizeof(*addresses) * (n_addresses + 10));
126 addresses = malloc(sizeof(*addresses)*10); 125 }
127 else if (!(n_addresses % 10)) 126
128 addresses = realloc(addresses,sizeof(*addresses) * (n_addresses + 10)); 127 if (verbose) {
129 128 puts(chld_out.line[i]);
130 if (verbose) 129 }
131 puts(chld_out.line[i]); 130
132 131 if (strcasestr(chld_out.line[i], ".in-addr.arpa") ||
133 if (strcasestr (chld_out.line[i], ".in-addr.arpa") || strcasestr (chld_out.line[i], ".ip6.arpa")) { 132 strcasestr(chld_out.line[i], ".ip6.arpa")) {
134 if ((temp_buffer = strstr (chld_out.line[i], "name = "))) 133 if ((strstr(chld_out.line[i], "canonical name = ") != NULL)) {
135 addresses[n_addresses++] = strdup (temp_buffer + 7); 134 continue;
136 else { 135 }
137 msg = (char *)_("Warning plugin error"); 136 char *temp_buffer = NULL;
138 result = STATE_WARNING; 137 if ((temp_buffer = strstr(chld_out.line[i], "name = "))) {
139 } 138 addresses[n_addresses++] = strdup(temp_buffer + 7);
140 } 139 } else {
141 140 msg = (char *)_("Warning plugin error");
142 /* bug ID: 2946553 - Older versions of bind will use all available dns 141 result = STATE_WARNING;
143 servers, we have to match the one specified */ 142 }
144 if (strstr (chld_out.line[i], "Server:") && strlen(dns_server) > 0) { 143 }
145 temp_buffer = strchr (chld_out.line[i], ':'); 144
146 temp_buffer++; 145 /* bug ID: 2946553 - Older versions of bind will use all available dns
147 146 servers, we have to match the one specified */
148 /* Strip leading tabs */ 147 if (strstr(chld_out.line[i], "Server:") && strlen(config.dns_server) > 0) {
149 for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) 148 char *temp_buffer = strchr(chld_out.line[i], ':');
150 /* NOOP */; 149 if (temp_buffer == NULL) {
151 150 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Server line\n"),
152 strip(temp_buffer); 151 NSLOOKUP_COMMAND);
153 if (temp_buffer==NULL || strlen(temp_buffer)==0) { 152 }
154 die (STATE_CRITICAL, 153
155 _("DNS CRITICAL - '%s' returned empty server string\n"), 154 temp_buffer++;
156 NSLOOKUP_COMMAND); 155
157 } 156 /* Strip leading tabs */
158 157 for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) {
159 if (strcmp(temp_buffer, dns_server) != 0) { 158 /* NOOP */;
160 die (STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), dns_server); 159 }
161 } 160
162 } 161 strip(temp_buffer);
163 162 if (strlen(temp_buffer) == 0) {
164 /* the server is responding, we just got the host name... */ 163 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"),
165 if (strstr (chld_out.line[i], "Name:")) 164 NSLOOKUP_COMMAND);
166 parse_address = true; 165 }
167 else if (parse_address && (strstr (chld_out.line[i], "Address:") || 166
168 strstr (chld_out.line[i], "Addresses:"))) { 167 if (strcmp(temp_buffer, config.dns_server) != 0) {
169 temp_buffer = index (chld_out.line[i], ':'); 168 die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"),
170 temp_buffer++; 169 config.dns_server);
171 170 }
172 /* Strip leading spaces */ 171 }
173 while (*temp_buffer == ' ') 172
174 temp_buffer++; 173 /* the server is responding, we just got the host name... */
175 174 if (strstr(chld_out.line[i], "Name:")) {
176 strip(temp_buffer); 175 parse_address = true;
177 if (temp_buffer==NULL || strlen(temp_buffer)==0) { 176 } else if (parse_address && (strstr(chld_out.line[i], "Address:") ||
178 die (STATE_CRITICAL, 177 strstr(chld_out.line[i], "Addresses:"))) {
179 _("DNS CRITICAL - '%s' returned empty host name string\n"), 178 char *temp_buffer = strchr(chld_out.line[i], ':');
180 NSLOOKUP_COMMAND); 179 if (temp_buffer == NULL) {
181 } 180 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Address line\n"),
182 181 NSLOOKUP_COMMAND);
183 addresses[n_addresses++] = strdup(temp_buffer); 182 }
184 } 183
185 else if (strstr (chld_out.line[i], _("Non-authoritative answer:"))) { 184 temp_buffer++;
186 non_authoritative = true; 185
187 } 186 /* Strip leading spaces */
188 187 while (*temp_buffer == ' ') {
189 188 temp_buffer++;
190 result = error_scan (chld_out.line[i], &is_nxdomain); 189 }
191 if (result != STATE_OK) { 190
192 msg = strchr (chld_out.line[i], ':'); 191 strip(temp_buffer);
193 if(msg) msg++; 192 if (strlen(temp_buffer) == 0) {
194 break; 193 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"),
195 } 194 NSLOOKUP_COMMAND);
196 } 195 }
197 196
198 /* scan stderr */ 197 addresses[n_addresses++] = strdup(temp_buffer);
199 for(size_t i = 0; i < chld_err.lines; i++) { 198 } else if (strstr(chld_out.line[i], _("Non-authoritative answer:"))) {
200 if (verbose) 199 non_authoritative = true;
201 puts(chld_err.line[i]); 200 }
202 201
203 if (error_scan (chld_err.line[i], &is_nxdomain) != STATE_OK) { 202 result = error_scan(chld_out.line[i], &is_nxdomain, config.dns_server);
204 result = max_state (result, error_scan (chld_err.line[i], &is_nxdomain)); 203 if (result != STATE_OK) {
205 msg = strchr(input_buffer, ':'); 204 msg = strchr(chld_out.line[i], ':');
206 if(msg) 205 if (msg) {
207 msg++; 206 msg++;
208 else 207 }
209 msg = input_buffer; 208 break;
210 } 209 }
211 } 210 }
212 211
213 if (is_nxdomain && !expect_nxdomain) { 212 char input_buffer[MAX_INPUT_BUFFER];
214 die (STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), query_address); 213 /* scan stderr */
215 } 214 for (size_t i = 0; i < chld_err.lines; i++) {
216 215 if (verbose) {
217 if (addresses) { 216 puts(chld_err.line[i]);
218 int i,slen; 217 }
219 char *adrp; 218
220 qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp); 219 if (error_scan(chld_err.line[i], &is_nxdomain, config.dns_server) != STATE_OK) {
221 for(i=0, slen=1; i < n_addresses; i++) { 220 result =
222 slen += strlen(addresses[i])+1; 221 max_state(result, error_scan(chld_err.line[i], &is_nxdomain, config.dns_server));
223 } 222 msg = strchr(input_buffer, ':');
224 adrp = address = malloc(slen); 223 if (msg) {
225 for(i=0; i < n_addresses; i++) { 224 msg++;
226 if (i) *adrp++ = ','; 225 } else {
227 strcpy(adrp, addresses[i]); 226 msg = input_buffer;
228 adrp += strlen(addresses[i]); 227 }
229 } 228 }
230 *adrp = 0; 229 }
231 } else 230
232 die (STATE_CRITICAL, 231 if (is_nxdomain && !config.expect_nxdomain) {
233 _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), 232 die(STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), config.query_address);
234 NSLOOKUP_COMMAND); 233 }
235 234
236 /* compare to expected address */ 235 if (addresses) {
237 if (result == STATE_OK && expected_address_cnt > 0) { 236 size_t slen = 1;
238 result = STATE_CRITICAL; 237 char *adrp = NULL;
239 temp_buffer = ""; 238 qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp);
240 unsigned long expect_match = (1 << expected_address_cnt) - 1; 239 for (size_t i = 0; i < n_addresses; i++) {
241 unsigned long addr_match = (1 << n_addresses) - 1; 240 slen += strlen(addresses[i]) + 1;
242 241 }
243 for (int i=0; i<expected_address_cnt; i++) { 242
244 int j; 243 // Temporary pointer adrp gets moved, address stays on the beginning
245 /* check if we get a match on 'raw' ip or cidr */ 244 adrp = address = malloc(slen);
246 for (j=0; j<n_addresses; j++) { 245 for (size_t i = 0; i < n_addresses; i++) {
247 if ( strcmp(addresses[j], expected_address[i]) == 0 246 if (i) {
248 || ip_match_cidr(addresses[j], expected_address[i]) ) { 247 *adrp++ = ',';
249 result = STATE_OK; 248 }
250 addr_match &= ~(1 << j); 249 strcpy(adrp, addresses[i]);
251 expect_match &= ~(1 << i); 250 adrp += strlen(addresses[i]);
252 } 251 }
253 } 252 *adrp = 0;
254 253 } else {
255 /* prepare an error string */ 254 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"),
256 xasprintf(&temp_buffer, "%s%s; ", temp_buffer, expected_address[i]); 255 NSLOOKUP_COMMAND);
257 } 256 }
258 /* check if expected_address must cover all in addresses and none may be missing */ 257
259 if (all_match && (expect_match != 0 || addr_match != 0)) 258 /* compare to expected address */
260 result = STATE_CRITICAL; 259 if (result == STATE_OK && config.expected_address_cnt > 0) {
261 if (result == STATE_CRITICAL) { 260 result = STATE_CRITICAL;
262 /* Strip off last semicolon... */ 261 char *temp_buffer = "";
263 temp_buffer[strlen(temp_buffer)-2] = '\0'; 262 unsigned long expect_match = (1 << config.expected_address_cnt) - 1;
264 xasprintf(&msg, _("expected '%s' but got '%s'"), temp_buffer, address); 263 unsigned long addr_match = (1 << n_addresses) - 1;
265 } 264
266 } 265 for (size_t i = 0; i < config.expected_address_cnt; i++) {
267 266 /* check if we get a match on 'raw' ip or cidr */
268 if (expect_nxdomain) { 267 for (size_t j = 0; j < n_addresses; j++) {
269 if (!is_nxdomain) { 268 if (strcmp(addresses[j], config.expected_address[i]) == 0 ||
270 result = STATE_CRITICAL; 269 ip_match_cidr(addresses[j], config.expected_address[i])) {
271 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), query_address, address); 270 result = STATE_OK;
272 } else { 271 addr_match &= ~(1 << j);
273 if (address != NULL) free(address); 272 expect_match &= ~(1 << i);
274 address = "NXDOMAIN"; 273 }
275 } 274 }
276 } 275
277 276 /* prepare an error string */
278 /* check if authoritative */ 277 xasprintf(&temp_buffer, "%s%s; ", temp_buffer, config.expected_address[i]);
279 if (result == STATE_OK && expect_authority && non_authoritative) { 278 }
280 result = STATE_CRITICAL; 279 /* check if expected_address must cover all in addresses and none may be missing */
281 xasprintf(&msg, _("server %s is not authoritative for %s"), dns_server, query_address); 280 if (config.all_match && (expect_match != 0 || addr_match != 0)) {
282 } 281 result = STATE_CRITICAL;
283 282 }
284 microsec = deltime (tv); 283 if (result == STATE_CRITICAL) {
285 elapsed_time = (double)microsec / 1.0e6; 284 /* Strip off last semicolon... */
286 285 temp_buffer[strlen(temp_buffer) - 2] = '\0';
287 if (result == STATE_OK) { 286 xasprintf(&msg, _("expected '%s' but got '%s'"), temp_buffer, address);
288 result = get_status(elapsed_time, time_thresholds); 287 }
289 if (result == STATE_OK) { 288 }
290 printf ("DNS %s: ", _("OK")); 289
291 } else if (result == STATE_WARNING) { 290 if (config.expect_nxdomain) {
292 printf ("DNS %s: ", _("WARNING")); 291 if (!is_nxdomain) {
293 } else if (result == STATE_CRITICAL) { 292 result = STATE_CRITICAL;
294 printf ("DNS %s: ", _("CRITICAL")); 293 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), config.query_address,
295 } 294 address);
296 printf (ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), elapsed_time); 295 } else {
297 printf (_(". %s returns %s"), query_address, address); 296 if (address != NULL) {
298 if ((time_thresholds->warning != NULL) && (time_thresholds->critical != NULL)) { 297 free(address);
299 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", 298 }
300 true, time_thresholds->warning->end, 299 address = "NXDOMAIN";
301 true, time_thresholds->critical->end, 300 }
302 true, 0, false, 0)); 301 }
303 } else if ((time_thresholds->warning == NULL) && (time_thresholds->critical != NULL)) { 302
304 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", 303 /* check if authoritative */
305 false, 0, 304 if (result == STATE_OK && config.expect_authority && non_authoritative) {
306 true, time_thresholds->critical->end, 305 result = STATE_CRITICAL;
307 true, 0, false, 0)); 306 xasprintf(&msg, _("server %s is not authoritative for %s"), config.dns_server,
308 } else if ((time_thresholds->warning != NULL) && (time_thresholds->critical == NULL)) { 307 config.query_address);
309 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", 308 }
310 true, time_thresholds->warning->end, 309
311 false, 0, 310 long microsec = deltime(tv);
312 true, 0, false, 0)); 311 double elapsed_time = (double)microsec / 1.0e6;
313 } else 312
314 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0)); 313 if (result == STATE_OK) {
315 } 314 result = get_status(elapsed_time, config.time_thresholds);
316 else if (result == STATE_WARNING) 315 if (result == STATE_OK) {
317 printf (_("DNS WARNING - %s\n"), 316 printf("DNS %s: ", _("OK"));
318 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg); 317 } else if (result == STATE_WARNING) {
319 else if (result == STATE_CRITICAL) 318 printf("DNS %s: ", _("WARNING"));
320 printf (_("DNS CRITICAL - %s\n"), 319 } else if (result == STATE_CRITICAL) {
321 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg); 320 printf("DNS %s: ", _("CRITICAL"));
322 else 321 }
323 printf (_("DNS UNKNOWN - %s\n"), 322 printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time),
324 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg); 323 elapsed_time);
325 324 printf(_(". %s returns %s"), config.query_address, address);
326 return result; 325 if ((config.time_thresholds->warning != NULL) &&
326 (config.time_thresholds->critical != NULL)) {
327 printf("|%s\n",
328 fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end,
329 true, config.time_thresholds->critical->end, true, 0, false, 0));
330 } else if ((config.time_thresholds->warning == NULL) &&
331 (config.time_thresholds->critical != NULL)) {
332 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true,
333 config.time_thresholds->critical->end, true, 0, false, 0));
334 } else if ((config.time_thresholds->warning != NULL) &&
335 (config.time_thresholds->critical == NULL)) {
336 printf("|%s\n",
337 fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end,
338 false, 0, true, 0, false, 0));
339 } else {
340 printf("|%s\n",
341 fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0));
342 }
343 } else if (result == STATE_WARNING) {
344 printf(_("DNS WARNING - %s\n"),
345 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
346 } else if (result == STATE_CRITICAL) {
347 printf(_("DNS CRITICAL - %s\n"),
348 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
349 } else {
350 printf(_("DNS UNKNOWN - %s\n"),
351 !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
352 }
353
354 exit(result);
327} 355}
328 356
329bool ip_match_cidr(const char *addr, const char *cidr_ro) { 357bool ip_match_cidr(const char *addr, const char *cidr_ro) {
330 char *subnet, *mask_c, *cidr = strdup(cidr_ro); 358 char *subnet;
331 int mask; 359 char *mask_c;
332 subnet = strtok(cidr, "/"); 360 char *cidr = strdup(cidr_ro);
333 mask_c = strtok(NULL, "\0"); 361 int mask;
334 if (!subnet || !mask_c) { 362 subnet = strtok(cidr, "/");
335 return false; 363 mask_c = strtok(NULL, "\0");
364 if (!subnet || !mask_c) {
365 return false;
336 } 366 }
337 mask = atoi(mask_c); 367 mask = atoi(mask_c);
338 368
339 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ 369 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */
340 return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) << (32 - mask); 370 return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask))
371 << (32 - mask);
341} 372}
342 373
343unsigned long 374unsigned long ip2long(const char *src) {
344ip2long(const char* src) { 375 unsigned long ip[4];
345 unsigned long ip[4]; 376 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */
346 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ 377 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 &&
347 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", 378 ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && ip[3] < 256)
348 &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && 379 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]
349 ip[0] < 256 && ip[1] < 256 && 380 : 0;
350 ip[2] < 256 && ip[3] < 256)
351 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]
352 : 0;
353} 381}
354 382
355int 383mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain,
356error_scan (char *input_buffer, bool *is_nxdomain) 384 const char dns_server[ADDRESS_LENGTH]) {
357{
358
359 const int nxdomain = strstr (input_buffer, "Non-existent") ||
360 strstr (input_buffer, "** server can't find") ||
361 strstr (input_buffer, "** Can't find") ||
362 strstr (input_buffer, "NXDOMAIN");
363 if (nxdomain) *is_nxdomain = true;
364
365 /* the DNS lookup timed out */
366 if (strstr (input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) ||
367 strstr (input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) ||
368 strstr (input_buffer, _("the `-sil[ent]' option to prevent this message from appearing.")))
369 return STATE_OK;
370
371 /* DNS server is not running... */
372 else if (strstr (input_buffer, "No response from server"))
373 die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
374 else if (strstr (input_buffer, "no servers could be reached"))
375 die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
376
377 /* Host name is valid, but server doesn't have records... */
378 else if (strstr (input_buffer, "No records"))
379 die (STATE_CRITICAL, _("DNS %s has no records\n"), dns_server);
380
381 /* Connection was refused */
382 else if (strstr (input_buffer, "Connection refused") ||
383 strstr (input_buffer, "Couldn't find server") ||
384 strstr (input_buffer, "Refused") ||
385 (strstr (input_buffer, "** server can't find") &&
386 strstr (input_buffer, ": REFUSED")))
387 die (STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server);
388
389 /* Query refused (usually by an ACL in the namserver) */
390 else if (strstr (input_buffer, "Query refused"))
391 die (STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server);
392
393 /* No information (e.g. nameserver IP has two PTR records) */
394 else if (strstr (input_buffer, "No information"))
395 die (STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server);
396
397 /* Network is unreachable */
398 else if (strstr (input_buffer, "Network is unreachable"))
399 die (STATE_CRITICAL, _("Network is unreachable\n"));
400
401 /* Internal server failure */
402 else if (strstr (input_buffer, "Server failure"))
403 die (STATE_CRITICAL, _("DNS failure for %s\n"), dns_server);
404
405 /* Request error or the DNS lookup timed out */
406 else if (strstr (input_buffer, "Format error") ||
407 strstr (input_buffer, "Timed out"))
408 return STATE_WARNING;
409
410 return STATE_OK;
411 385
412} 386 const int nxdomain = strstr(input_buffer, "Non-existent") ||
387 strstr(input_buffer, "** server can't find") ||
388 strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN");
389 if (nxdomain) {
390 *is_nxdomain = true;
391 }
413 392
393 /* the DNS lookup timed out */
394 if (strstr(input_buffer,
395 _("Note: nslookup is deprecated and may be removed from future releases.")) ||
396 strstr(input_buffer,
397 _("Consider using the `dig' or `host' programs instead. Run nslookup with")) ||
398 strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing."))) {
399 return STATE_OK;
400 }
414 401
415/* process command-line arguments */ 402 /* DNS server is not running... */
416int 403 else if (strstr(input_buffer, "No response from server")) {
417process_arguments (int argc, char **argv) 404 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
418{ 405 } else if (strstr(input_buffer, "no servers could be reached")) {
419 int c; 406 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
420 char *warning = NULL; 407 }
421 char *critical = NULL; 408
422 409 /* Host name is valid, but server doesn't have records... */
423 int opt_index = 0; 410 else if (strstr(input_buffer, "No records")) {
424 static struct option long_opts[] = { 411 die(STATE_CRITICAL, _("DNS %s has no records\n"), dns_server);
425 {"help", no_argument, 0, 'h'}, 412 }
426 {"version", no_argument, 0, 'V'}, 413
427 {"verbose", no_argument, 0, 'v'}, 414 /* Connection was refused */
428 {"timeout", required_argument, 0, 't'}, 415 else if (strstr(input_buffer, "Connection refused") ||
429 {"hostname", required_argument, 0, 'H'}, 416 strstr(input_buffer, "Couldn't find server") || strstr(input_buffer, "Refused") ||
430 {"server", required_argument, 0, 's'}, 417 (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) {
431 {"reverse-server", required_argument, 0, 'r'}, 418 die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server);
432 {"expected-address", required_argument, 0, 'a'}, 419 }
433 {"expect-nxdomain", no_argument, 0, 'n'}, 420
434 {"expect-authority", no_argument, 0, 'A'}, 421 /* Query refused (usually by an ACL in the namserver) */
435 {"all", no_argument, 0, 'L'}, 422 else if (strstr(input_buffer, "Query refused")) {
436 {"warning", required_argument, 0, 'w'}, 423 die(STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server);
437 {"critical", required_argument, 0, 'c'}, 424 }
438 {0, 0, 0, 0} 425
439 }; 426 /* No information (e.g. nameserver IP has two PTR records) */
440 427 else if (strstr(input_buffer, "No information")) {
441 if (argc < 2) 428 die(STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server);
442 return ERROR; 429 }
443 430
444 for (c = 1; c < argc; c++) 431 /* Network is unreachable */
445 if (strcmp ("-to", argv[c]) == 0) 432 else if (strstr(input_buffer, "Network is unreachable")) {
446 strcpy (argv[c], "-t"); 433 die(STATE_CRITICAL, _("Network is unreachable\n"));
447 434 }
448 while (1) { 435
449 c = getopt_long (argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index); 436 /* Internal server failure */
450 437 else if (strstr(input_buffer, "Server failure")) {
451 if (c == -1 || c == EOF) 438 die(STATE_CRITICAL, _("DNS failure for %s\n"), dns_server);
452 break; 439 }
453 440
454 switch (c) { 441 /* Request error or the DNS lookup timed out */
455 case 'h': /* help */ 442 else if (strstr(input_buffer, "Format error") || strstr(input_buffer, "Timed out")) {
456 print_help (); 443 return STATE_WARNING;
457 exit (STATE_UNKNOWN);
458 case 'V': /* version */
459 print_revision (progname, NP_VERSION);
460 exit (STATE_UNKNOWN);
461 case 'v': /* version */
462 verbose = true;
463 break;
464 case 't': /* timeout period */
465 timeout_interval = atoi (optarg);
466 break;
467 case 'H': /* hostname */
468 if (strlen (optarg) >= ADDRESS_LENGTH)
469 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
470 strcpy (query_address, optarg);
471 break;
472 case 's': /* server name */
473 /* TODO: this host_or_die check is probably unnecessary.
474 * Better to confirm nslookup response matches */
475 host_or_die(optarg);
476 if (strlen (optarg) >= ADDRESS_LENGTH)
477 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
478 strcpy (dns_server, optarg);
479 break;
480 case 'r': /* reverse server name */
481 /* TODO: Is this host_or_die necessary? */
482 host_or_die(optarg);
483 if (strlen (optarg) >= ADDRESS_LENGTH)
484 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
485 strcpy (ptr_server, optarg);
486 break;
487 case 'a': /* expected address */
488 if (strlen (optarg) >= ADDRESS_LENGTH)
489 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
490 if (strchr(optarg, ',') != NULL) {
491 char *comma = strchr(optarg, ',');
492 while (comma != NULL) {
493 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**));
494 expected_address[expected_address_cnt] = strndup(optarg, comma - optarg);
495 expected_address_cnt++;
496 optarg = comma + 1;
497 comma = strchr(optarg, ',');
498 } 444 }
499 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**)); 445
500 expected_address[expected_address_cnt] = strdup(optarg); 446 return STATE_OK;
501 expected_address_cnt++;
502 } else {
503 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**));
504 expected_address[expected_address_cnt] = strdup(optarg);
505 expected_address_cnt++;
506 }
507 break;
508 case 'n': /* expect NXDOMAIN */
509 expect_nxdomain = true;
510 break;
511 case 'A': /* expect authority */
512 expect_authority = true;
513 break;
514 case 'L': /* all must match */
515 all_match = true;
516 break;
517 case 'w':
518 warning = optarg;
519 break;
520 case 'c':
521 critical = optarg;
522 break;
523 default: /* args not parsable */
524 usage5();
525 }
526 }
527
528 c = optind;
529 if (strlen(query_address)==0 && c<argc) {
530 if (strlen(argv[c])>=ADDRESS_LENGTH)
531 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
532 strcpy (query_address, argv[c++]);
533 }
534
535 if (strlen(dns_server)==0 && c<argc) {
536 /* TODO: See -s option */
537 host_or_die(argv[c]);
538 if (strlen(argv[c]) >= ADDRESS_LENGTH)
539 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
540 strcpy (dns_server, argv[c++]);
541 }
542
543 set_thresholds(&time_thresholds, warning, critical);
544
545 return validate_arguments ();
546} 447}
547 448
449/* process command-line arguments */
450check_dns_config_wrapper process_arguments(int argc, char **argv) {
451 static struct option long_opts[] = {{"help", no_argument, 0, 'h'},
452 {"version", no_argument, 0, 'V'},
453 {"verbose", no_argument, 0, 'v'},
454 {"timeout", required_argument, 0, 't'},
455 {"hostname", required_argument, 0, 'H'},
456 {"server", required_argument, 0, 's'},
457 {"reverse-server", required_argument, 0, 'r'},
458 {"expected-address", required_argument, 0, 'a'},
459 {"expect-nxdomain", no_argument, 0, 'n'},
460 {"expect-authority", no_argument, 0, 'A'},
461 {"all", no_argument, 0, 'L'},
462 {"warning", required_argument, 0, 'w'},
463 {"critical", required_argument, 0, 'c'},
464 {0, 0, 0, 0}};
465
466 check_dns_config_wrapper result = {
467 .config = check_dns_config_init(),
468 .errorcode = OK,
469 };
470
471 if (argc < 2) {
472 result.errorcode = ERROR;
473 return result;
474 }
475
476 for (int index = 1; index < argc; index++) {
477 if (strcmp("-to", argv[index]) == 0) {
478 strcpy(argv[index], "-t");
479 }
480 }
548 481
549int 482 char *warning = NULL;
550validate_arguments () 483 char *critical = NULL;
551{ 484 int opt_index = 0;
552 if (query_address[0] == 0) { 485 int index = 0;
553 printf ("missing --host argument\n"); 486 while (true) {
554 return ERROR; 487 index = getopt_long(argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index);
555 } 488
489 if (index == -1 || index == EOF) {
490 break;
491 }
492
493 switch (index) {
494 case 'h': /* help */
495 print_help();
496 exit(STATE_UNKNOWN);
497 case 'V': /* version */
498 print_revision(progname, NP_VERSION);
499 exit(STATE_UNKNOWN);
500 case 'v': /* version */
501 verbose = true;
502 break;
503 case 't': /* timeout period */
504 timeout_interval = atoi(optarg);
505 break;
506 case 'H': /* hostname */
507 if (strlen(optarg) >= ADDRESS_LENGTH) {
508 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
509 }
510 strcpy(result.config.query_address, optarg);
511 break;
512 case 's': /* server name */
513 /* TODO: this host_or_die check is probably unnecessary.
514 * Better to confirm nslookup response matches */
515 host_or_die(optarg);
516 if (strlen(optarg) >= ADDRESS_LENGTH) {
517 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
518 }
519 strcpy(result.config.dns_server, optarg);
520 break;
521 case 'r': /* reverse server name */
522 /* TODO: Is this host_or_die necessary? */
523 // TODO This does not do anything!!! 2025-03-08 rincewind
524 host_or_die(optarg);
525 if (strlen(optarg) >= ADDRESS_LENGTH) {
526 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
527 }
528 static char ptr_server[ADDRESS_LENGTH] = "";
529 strcpy(ptr_server, optarg);
530 break;
531 case 'a': /* expected address */
532 if (strlen(optarg) >= ADDRESS_LENGTH) {
533 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
534 }
535 if (strchr(optarg, ',') != NULL) {
536 char *comma = strchr(optarg, ',');
537 while (comma != NULL) {
538 result.config.expected_address = (char **)realloc(
539 result.config.expected_address,
540 (result.config.expected_address_cnt + 1) * sizeof(char **));
541 result.config.expected_address[result.config.expected_address_cnt] =
542 strndup(optarg, comma - optarg);
543 result.config.expected_address_cnt++;
544 optarg = comma + 1;
545 comma = strchr(optarg, ',');
546 }
547 result.config.expected_address =
548 (char **)realloc(result.config.expected_address,
549 (result.config.expected_address_cnt + 1) * sizeof(char **));
550 result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg);
551 result.config.expected_address_cnt++;
552 } else {
553 result.config.expected_address =
554 (char **)realloc(result.config.expected_address,
555 (result.config.expected_address_cnt + 1) * sizeof(char **));
556 result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg);
557 result.config.expected_address_cnt++;
558 }
559 break;
560 case 'n': /* expect NXDOMAIN */
561 result.config.expect_nxdomain = true;
562 break;
563 case 'A': /* expect authority */
564 result.config.expect_authority = true;
565 break;
566 case 'L': /* all must match */
567 result.config.all_match = true;
568 break;
569 case 'w':
570 warning = optarg;
571 break;
572 case 'c':
573 critical = optarg;
574 break;
575 default: /* args not parsable */
576 usage5();
577 }
578 }
556 579
557 if (expected_address_cnt > 0 && expect_nxdomain) { 580 index = optind;
558 printf ("--expected-address and --expect-nxdomain cannot be combined\n"); 581 if (strlen(result.config.query_address) == 0 && index < argc) {
559 return ERROR; 582 if (strlen(argv[index]) >= ADDRESS_LENGTH) {
560 } 583 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
584 }
585 strcpy(result.config.query_address, argv[index++]);
586 }
561 587
562 return OK; 588 if (strlen(result.config.dns_server) == 0 && index < argc) {
589 /* TODO: See -s option */
590 host_or_die(argv[index]);
591 if (strlen(argv[index]) >= ADDRESS_LENGTH) {
592 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
593 }
594 strcpy(result.config.dns_server, argv[index++]);
595 }
596
597 set_thresholds(&result.config.time_thresholds, warning, critical);
598
599 return validate_arguments(result);
563} 600}
564 601
602check_dns_config_wrapper validate_arguments(check_dns_config_wrapper config_wrapper) {
603 if (config_wrapper.config.query_address[0] == 0) {
604 printf("missing --host argument\n");
605 config_wrapper.errorcode = ERROR;
606 return config_wrapper;
607 }
565 608
566void 609 if (config_wrapper.config.expected_address_cnt > 0 && config_wrapper.config.expect_nxdomain) {
567print_help (void) 610 printf("--expected-address and --expect-nxdomain cannot be combined\n");
568{ 611 config_wrapper.errorcode = ERROR;
569 print_revision (progname, NP_VERSION); 612 return config_wrapper;
570 613 }
571 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 614
572 printf (COPYRIGHT, copyright, email); 615 return config_wrapper;
573
574 printf ("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given host/domain query."));
575 printf ("%s\n", _("An optional DNS server to use may be specified."));
576 printf ("%s\n", _("If no DNS server is specified, the default server(s) specified in /etc/resolv.conf will be used."));
577
578 printf ("\n\n");
579
580 print_usage ();
581
582 printf (UT_HELP_VRSN);
583 printf (UT_EXTRA_OPTS);
584
585 printf (" -H, --hostname=HOST\n");
586 printf (" %s\n", _("The name or address you want to query"));
587 printf (" -s, --server=HOST\n");
588 printf (" %s\n", _("Optional DNS server you want to use for the lookup"));
589 printf (" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n");
590 printf (" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end"));
591 printf (" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any"));
592 printf (" %s\n", _("value matches)."));
593 printf (" -n, --expect-nxdomain\n");
594 printf (" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)"));
595 printf (" %s\n", _("Cannot be used together with -a"));
596 printf (" -A, --expect-authority\n");
597 printf (" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup"));
598 printf (" -w, --warning=seconds\n");
599 printf (" %s\n", _("Return warning if elapsed time exceeds value. Default off"));
600 printf (" -c, --critical=seconds\n");
601 printf (" %s\n", _("Return critical if elapsed time exceeds value. Default off"));
602 printf (" -L, --all\n");
603 printf (" %s\n", _("Return critical if the list of expected addresses does not match all addresses"));
604 printf (" %s\n", _("returned. Default off"));
605
606 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
607
608 printf (UT_SUPPORT);
609} 616}
610 617
618void print_help(void) {
619 print_revision(progname, NP_VERSION);
620
621 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
622 printf(COPYRIGHT, copyright, email);
623
624 printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given "
625 "host/domain query."));
626 printf("%s\n", _("An optional DNS server to use may be specified."));
627 printf("%s\n", _("If no DNS server is specified, the default server(s) specified in "
628 "/etc/resolv.conf will be used."));
629
630 printf("\n\n");
631
632 print_usage();
633
634 printf(UT_HELP_VRSN);
635 printf(UT_EXTRA_OPTS);
636
637 printf(" -H, --hostname=HOST\n");
638 printf(" %s\n", _("The name or address you want to query"));
639 printf(" -s, --server=HOST\n");
640 printf(" %s\n", _("Optional DNS server you want to use for the lookup"));
641 printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n");
642 printf(" %s\n",
643 _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end"));
644 printf(" %s\n",
645 _("with a dot (.). This option can be repeated multiple times (Returns OK if any"));
646 printf(" %s\n", _("value matches)."));
647 printf(" -n, --expect-nxdomain\n");
648 printf(" %s\n",
649 _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)"));
650 printf(" %s\n", _("Cannot be used together with -a"));
651 printf(" -A, --expect-authority\n");
652 printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup"));
653 printf(" -w, --warning=seconds\n");
654 printf(" %s\n", _("Return warning if elapsed time exceeds value. Default off"));
655 printf(" -c, --critical=seconds\n");
656 printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off"));
657 printf(" -L, --all\n");
658 printf(" %s\n",
659 _("Return critical if the list of expected addresses does not match all addresses"));
660 printf(" %s\n", _("returned. Default off"));
661
662 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
663
664 printf(UT_SUPPORT);
665}
611 666
612void 667void print_usage(void) {
613print_usage (void) 668 printf("%s\n", _("Usage:"));
614{ 669 printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c "
615 printf ("%s\n", _("Usage:")); 670 "crit] [-L]\n",
616 printf ("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname); 671 progname);
617} 672}
diff --git a/plugins/check_dns.d/config.h b/plugins/check_dns.d/config.h
new file mode 100644
index 00000000..9ec4eb82
--- /dev/null
+++ b/plugins/check_dns.d/config.h
@@ -0,0 +1,34 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7#define ADDRESS_LENGTH 256
8
9typedef struct {
10 bool all_match;
11 char dns_server[ADDRESS_LENGTH];
12 char query_address[ADDRESS_LENGTH];
13 bool expect_nxdomain;
14 bool expect_authority;
15 char **expected_address;
16 size_t expected_address_cnt;
17
18 thresholds *time_thresholds;
19} check_dns_config;
20
21check_dns_config check_dns_config_init() {
22 check_dns_config tmp = {
23 .all_match = false,
24 .dns_server = "",
25 .query_address = "",
26 .expect_nxdomain = false,
27 .expect_authority = false,
28 .expected_address = NULL,
29 .expected_address_cnt = 0,
30
31 .time_thresholds = NULL,
32 };
33 return tmp;
34}
diff --git a/plugins/check_dummy.c b/plugins/check_dummy.c
index 212a1344..602d5868 100644
--- a/plugins/check_dummy.c
+++ b/plugins/check_dummy.c
@@ -1,124 +1,114 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dummy plugin 3 * Monitoring check_dummy plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_dummy plugin 10 * This file contains the check_dummy plugin
11* 11 *
12* This plugin will simply return the state corresponding to the numeric value 12 * This plugin will simply return the state corresponding to the numeric value
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_dummy"; 31const char *progname = "check_dummy";
32const char *copyright = "1999-2007"; 32const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "common.h"
36#include "utils.h" 36#include "utils.h"
37 37
38void print_help (void); 38static void print_help(void);
39void print_usage (void); 39void print_usage(void);
40 40
41 41int main(int argc, char **argv) {
42int 42 int result = STATE_UNKNOWN;
43main (int argc, char **argv) 43
44{ 44 setlocale(LC_ALL, "");
45 int result = STATE_UNKNOWN; 45 bindtextdomain(PACKAGE, LOCALEDIR);
46 46 textdomain(PACKAGE);
47 setlocale (LC_ALL, ""); 47
48 bindtextdomain (PACKAGE, LOCALEDIR); 48 if (argc < 2) {
49 textdomain (PACKAGE); 49 usage4(_("Could not parse arguments"));
50 50 } else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) {
51 if (argc < 2) 51 print_revision(progname, NP_VERSION);
52 usage4 (_("Could not parse arguments")); 52 exit(STATE_UNKNOWN);
53 else if (strcmp (argv[1], "-V") == 0 || strcmp (argv[1], "--version") == 0) { 53 } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
54 print_revision (progname, NP_VERSION); 54 print_help();
55 exit (STATE_UNKNOWN); 55 exit(STATE_UNKNOWN);
56 } 56 } else if (!is_integer(argv[1])) {
57 else if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) { 57 usage4(_("Arguments to check_dummy must be an integer"));
58 print_help (); 58 } else {
59 exit (STATE_UNKNOWN); 59 result = atoi(argv[1]);
60 } 60 }
61 else if (!is_integer (argv[1])) 61
62 usage4 (_("Arguments to check_dummy must be an integer")); 62 switch (result) {
63 else 63 case STATE_OK:
64 result = atoi (argv[1]); 64 printf(_("OK"));
65 65 break;
66 switch (result) { 66 case STATE_WARNING:
67 case STATE_OK: 67 printf(_("WARNING"));
68 printf (_("OK")); 68 break;
69 break; 69 case STATE_CRITICAL:
70 case STATE_WARNING: 70 printf(_("CRITICAL"));
71 printf (_("WARNING")); 71 break;
72 break; 72 case STATE_UNKNOWN:
73 case STATE_CRITICAL: 73 printf(_("UNKNOWN"));
74 printf (_("CRITICAL")); 74 break;
75 break; 75 default:
76 case STATE_UNKNOWN: 76 printf(_("UNKNOWN"));
77 printf (_("UNKNOWN")); 77 printf(": ");
78 break; 78 printf(_("Status %d is not a supported error state\n"), result);
79 default: 79 return STATE_UNKNOWN;
80 printf (_("UNKNOWN")); 80 }
81 printf (": "); 81
82 printf (_("Status %d is not a supported error state\n"), result); 82 if (argc >= 3) {
83 return STATE_UNKNOWN; 83 printf(": %s", argv[2]);
84 } 84 }
85 85
86 if (argc >= 3) 86 printf("\n");
87 printf (": %s", argv[2]); 87
88 88 return result;
89 printf("\n");
90
91 return result;
92} 89}
93 90
91void print_help(void) {
92 print_revision(progname, NP_VERSION);
94 93
94 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
95 printf(COPYRIGHT, copyright, email);
95 96
96void 97 printf("%s\n",
97print_help (void) 98 _("This plugin will simply return the state corresponding to the numeric value"));
98{
99 print_revision (progname, NP_VERSION);
100 99
101 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 100 printf("%s\n", _("of the <state> argument with optional text"));
102 printf (COPYRIGHT, copyright, email);
103 101
104 printf ("%s\n", _("This plugin will simply return the state corresponding to the numeric value")); 102 printf("\n\n");
105 103
106 printf ("%s\n", _("of the <state> argument with optional text")); 104 print_usage();
107 105
108 printf ("\n\n"); 106 printf(UT_HELP_VRSN);
109 107
110 print_usage (); 108 printf(UT_SUPPORT);
111
112 printf (UT_HELP_VRSN);
113
114 printf (UT_SUPPORT);
115} 109}
116 110
117 111void print_usage(void) {
118 112 printf("%s\n", _("Usage:"));
119void 113 printf(" %s <integer state> [optional text]\n", progname);
120print_usage (void)
121{
122 printf ("%s\n", _("Usage:"));
123 printf (" %s <integer state> [optional text]\n", progname);
124} 114}
diff --git a/plugins/check_fping.c b/plugins/check_fping.c
index 70d6f9fc..6160c2cb 100644
--- a/plugins/check_fping.c
+++ b/plugins/check_fping.c
@@ -1,36 +1,36 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_fping plugin 3 * Monitoring check_fping plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_disk plugin 10 * This file contains the check_fping plugin
11* 11 *
12* This plugin will use the fping command to ping the specified host for a 12 * This plugin will use the fping command to ping the specified host for a
13* fast check 13 * fast check
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_fping"; 32const char *progname = "check_fping";
33const char *copyright = "2000-2007"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
@@ -38,490 +38,560 @@ const char *email = "devel@monitoring-plugins.org";
38#include "netutils.h" 38#include "netutils.h"
39#include "utils.h" 39#include "utils.h"
40#include <stdbool.h> 40#include <stdbool.h>
41#include "check_fping.d/config.h"
42#include "states.h"
41 43
42enum { 44enum {
43 PACKET_COUNT = 1, 45 PL = 0,
44 PACKET_SIZE = 56, 46 RTA = 1
45 PL = 0,
46 RTA = 1
47}; 47};
48 48
49int textscan (char *buf); 49static mp_state_enum textscan(char *buf, const char * /*server_name*/, bool /*crta_p*/,
50int process_arguments (int, char **); 50 double /*crta*/, bool /*wrta_p*/, double /*wrta*/, bool /*cpl_p*/,
51int get_threshold (char *arg, char *rv[2]); 51 int /*cpl*/, bool /*wpl_p*/, int /*wpl*/, bool /*alive_p*/);
52void print_help (void); 52
53void print_usage (void); 53typedef struct {
54 54 int errorcode;
55char *server_name = NULL; 55 check_fping_config config;
56char *sourceip = NULL; 56} check_fping_config_wrapper;
57char *sourceif = NULL; 57static check_fping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
58int packet_size = PACKET_SIZE; 58static int get_threshold(char *arg, char *rv[2]);
59int packet_count = PACKET_COUNT; 59static void print_help(void);
60int target_timeout = 0; 60void print_usage(void);
61int packet_interval = 0; 61
62bool verbose = false; 62static bool verbose = false;
63int cpl; 63
64int wpl; 64int main(int argc, char **argv) {
65double crta; 65 setlocale(LC_ALL, "");
66double wrta; 66 bindtextdomain(PACKAGE, LOCALEDIR);
67bool cpl_p = false; 67 textdomain(PACKAGE);
68bool wpl_p = false; 68
69bool alive_p = false; 69 /* Parse extra opts if any */
70bool crta_p = false; 70 argv = np_extra_opts(&argc, argv, progname);
71bool wrta_p = false; 71
72 72 check_fping_config_wrapper tmp_config = process_arguments(argc, argv);
73int 73 if (tmp_config.errorcode == ERROR) {
74main (int argc, char **argv) 74 usage4(_("Could not parse arguments"));
75{ 75 }
76/* normally should be int result = STATE_UNKNOWN; */ 76
77 77 const check_fping_config config = tmp_config.config;
78 int status = STATE_UNKNOWN; 78
79 int result = 0; 79 char *server = NULL;
80 char *fping_prog = NULL; 80 server = strscpy(server, config.server_name);
81 char *server = NULL; 81
82 char *command_line = NULL; 82 char *option_string = "";
83 char *input_buffer = NULL; 83 char *fping_prog = NULL;
84 char *option_string = ""; 84
85 input_buffer = malloc (MAX_INPUT_BUFFER); 85 /* First determine if the target is dualstack or ipv6 only. */
86 86 bool server_is_inet6_addr = is_inet6_addr(server);
87 setlocale (LC_ALL, ""); 87
88 bindtextdomain (PACKAGE, LOCALEDIR); 88 /*
89 textdomain (PACKAGE); 89 * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack
90 90 * -> we use ipv6
91 /* Parse extra opts if any */ 91 * If the user requested -4 OR the user made no assertion and the address is v4 ONLY
92 argv=np_extra_opts (&argc, argv, progname); 92 * -> we use ipv4
93 93 */
94 if (process_arguments (argc, argv) == ERROR) 94 if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) {
95 usage4 (_("Could not parse arguments")); 95 xasprintf(&option_string, "%s-6 ", option_string);
96 96 } else {
97 server = strscpy (server, server_name); 97 xasprintf(&option_string, "%s-4 ", option_string);
98 98 }
99 /* compose the command */ 99 fping_prog = strdup(PATH_TO_FPING);
100 if (target_timeout) 100
101 xasprintf(&option_string, "%s-t %d ", option_string, target_timeout); 101 /* compose the command */
102 if (packet_interval) 102 if (config.target_timeout) {
103 xasprintf(&option_string, "%s-p %d ", option_string, packet_interval); 103 xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout);
104 if (sourceip) 104 }
105 xasprintf(&option_string, "%s-S %s ", option_string, sourceip); 105 if (config.packet_interval) {
106 if (sourceif) 106 xasprintf(&option_string, "%s-p %d ", option_string, config.packet_interval);
107 xasprintf(&option_string, "%s-I %s ", option_string, sourceif); 107 }
108 108 if (config.sourceip) {
109#ifdef PATH_TO_FPING6 109 xasprintf(&option_string, "%s-S %s ", option_string, config.sourceip);
110 if (address_family != AF_INET && is_inet6_addr(server)) 110 }
111 fping_prog = strdup(PATH_TO_FPING6); 111 if (config.sourceif) {
112 else 112 xasprintf(&option_string, "%s-I %s ", option_string, config.sourceif);
113 fping_prog = strdup(PATH_TO_FPING); 113 }
114#else 114 if (config.dontfrag) {
115 fping_prog = strdup(PATH_TO_FPING); 115 xasprintf(&option_string, "%s-M ", option_string);
116#endif 116 }
117 117 if (config.randomize_packet_data) {
118 xasprintf (&command_line, "%s %s-b %d -c %d %s", fping_prog, 118 xasprintf(&option_string, "%s-R ", option_string);
119 option_string, packet_size, packet_count, server); 119 }
120 120
121 if (verbose) 121 if (config.fwmark_set) {
122 printf ("%s\n", command_line); 122 xasprintf(&option_string, "%s--fwmark %u ", option_string, config.fwmark);
123 123 }
124 /* run the command */ 124
125 child_process = spopen (command_line); 125 if (config.icmp_timestamp) {
126 if (child_process == NULL) { 126 xasprintf(&option_string, "%s--icmp-timestamp ", option_string);
127 printf (_("Could not open pipe: %s\n"), command_line); 127 }
128 return STATE_UNKNOWN; 128
129 } 129 if (config.check_source) {
130 130 xasprintf(&option_string, "%s--check-source ", option_string);
131 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 131 }
132 if (child_stderr == NULL) { 132
133 printf (_("Could not open stderr for %s\n"), command_line); 133 char *command_line = NULL;
134 } 134
135 135 if (config.icmp_timestamp) {
136 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 136 // no packet size settable for ICMP timestamp
137 if (verbose) 137 xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count,
138 printf ("%s", input_buffer); 138 server);
139 status = max_state (status, textscan (input_buffer)); 139 } else {
140 } 140 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string,
141 141 config.packet_size, config.packet_count, server);
142 /* If we get anything on STDERR, at least set warning */ 142 }
143 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { 143
144 status = max_state (status, STATE_WARNING); 144 if (verbose) {
145 if (verbose) 145 printf("%s\n", command_line);
146 printf ("%s", input_buffer); 146 }
147 status = max_state (status, textscan (input_buffer)); 147
148 } 148 /* run the command */
149 (void) fclose (child_stderr); 149 child_process = spopen(command_line);
150 150 if (child_process == NULL) {
151 /* close the pipe */ 151 printf(_("Could not open pipe: %s\n"), command_line);
152 result = spclose (child_process); 152 return STATE_UNKNOWN;
153 if (result) { 153 }
154 /* need to use max_state not max */ 154
155 status = max_state (status, STATE_WARNING); 155 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
156 } 156 if (child_stderr == NULL) {
157 157 printf(_("Could not open stderr for %s\n"), command_line);
158 if (result > 1 ) { 158 }
159 status = max_state (status, STATE_UNKNOWN); 159
160 if (result == 2) { 160 char *input_buffer = malloc(MAX_INPUT_BUFFER);
161 die (STATE_UNKNOWN, _("FPING UNKNOWN - IP address not found\n")); 161 mp_state_enum status = STATE_UNKNOWN;
162 } 162 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
163 if (result == 3) { 163 if (verbose) {
164 die (STATE_UNKNOWN, _("FPING UNKNOWN - invalid commandline argument\n")); 164 printf("%s", input_buffer);
165 } 165 }
166 if (result == 4) { 166 status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p,
167 die (STATE_UNKNOWN, _("FPING UNKNOWN - failed system call\n")); 167 config.crta, config.wrta_p, config.wrta, config.cpl_p,
168 } 168 config.cpl, config.wpl_p, config.wpl, config.alive_p));
169 169 }
170 } 170
171 171 /* If we get anything on STDERR, at least set warning */
172 printf ("FPING %s - %s\n", state_text (status), server_name); 172 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
173 173 status = max_state(status, STATE_WARNING);
174 return status; 174 if (verbose) {
175 printf("%s", input_buffer);
176 }
177 status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p,
178 config.crta, config.wrta_p, config.wrta, config.cpl_p,
179 config.cpl, config.wpl_p, config.wpl, config.alive_p));
180 }
181 (void)fclose(child_stderr);
182
183 /* close the pipe */
184 int result = spclose(child_process);
185 if (result) {
186 /* need to use max_state not max */
187 status = max_state(status, STATE_WARNING);
188 }
189
190 if (result > 1) {
191 status = max_state(status, STATE_UNKNOWN);
192 if (result == 2) {
193 die(STATE_UNKNOWN, _("FPING UNKNOWN - IP address not found\n"));
194 }
195 if (result == 3) {
196 die(STATE_UNKNOWN, _("FPING UNKNOWN - invalid commandline argument\n"));
197 }
198 if (result == 4) {
199 die(STATE_UNKNOWN, _("FPING UNKNOWN - failed system call\n"));
200 }
201 }
202
203 printf("FPING %s - %s\n", state_text(status), config.server_name);
204
205 return status;
175} 206}
176 207
177 208mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double crta, bool wrta_p,
178int textscan (char *buf) { 209 double wrta, bool cpl_p, int cpl, bool wpl_p, int wpl, bool alive_p) {
179 char *rtastr = NULL; 210 /* stops testing after the first successful reply. */
180 char *losstr = NULL; 211 double rta;
181 char *xmtstr = NULL; 212 double loss;
182 double loss; 213 char *rtastr = NULL;
183 double rta; 214 if (alive_p && strstr(buf, "avg, 0% loss)")) {
184 double xmt; 215 rtastr = strstr(buf, "ms (");
185 int status = STATE_UNKNOWN; 216 rtastr = 1 + index(rtastr, '(');
186 217 rta = strtod(rtastr, NULL);
187 /* stops testing after the first successful reply. */ 218 loss = strtod("0", NULL);
188 if (alive_p && strstr(buf, "avg, 0% loss)")) { 219 die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta,
189 rtastr = strstr (buf, "ms ("); 220 /* No loss since we only waited for the first reply
190 rtastr = 1 + index(rtastr, '('); 221 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */
191 rta = strtod(rtastr, NULL); 222 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0,
192 loss=strtod("0",NULL); 223 false, 0));
193 die (STATE_OK, 224 }
194 _("FPING %s - %s (rta=%f ms)|%s\n"), 225
195 state_text (STATE_OK), server_name,rta, 226 mp_state_enum status = STATE_UNKNOWN;
196 /* No loss since we only waited for the first reply 227 char *xmtstr = NULL;
197 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */ 228 double xmt;
198 fperfdata ("rta", rta/1.0e3, "s", wrta_p, wrta/1.0e3, crta_p, crta/1.0e3, true, 0, false, 0)); 229 char *losstr = NULL;
199 } 230 if (strstr(buf, "not found")) {
200 231 die(STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name);
201 if (strstr (buf, "not found")) { 232
202 die (STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name); 233 } else if (strstr(buf, "is unreachable") || strstr(buf, "Unreachable")) {
203 234 die(STATE_CRITICAL, _("FPING CRITICAL - %s is unreachable\n"), "host");
204 } 235
205 else if (strstr (buf, "is unreachable") || strstr (buf, "Unreachable")) { 236 } else if (strstr(buf, "Operation not permitted") || strstr(buf, "No such device")) {
206 die (STATE_CRITICAL, _("FPING CRITICAL - %s is unreachable\n"), 237 die(STATE_UNKNOWN, _("FPING UNKNOWN - %s parameter error\n"), "host");
207 "host"); 238 } else if (strstr(buf, "is down")) {
208 239 die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name);
209 } 240
210 else if (strstr (buf, "Operation not permitted") || strstr (buf, "No such device") ) { 241 } else if (strstr(buf, "is alive")) {
211 die (STATE_UNKNOWN, _("FPING UNKNOWN - %s parameter error\n"), 242 status = STATE_OK;
212 "host"); 243
213 } 244 } else if (strstr(buf, "xmt/rcv/%loss") && strstr(buf, "min/avg/max")) {
214 else if (strstr (buf, "is down")) { 245 losstr = strstr(buf, "=");
215 die (STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name); 246 losstr = 1 + strstr(losstr, "/");
216 247 losstr = 1 + strstr(losstr, "/");
217 } 248 rtastr = strstr(buf, "min/avg/max");
218 else if (strstr (buf, "is alive")) { 249 rtastr = strstr(rtastr, "=");
219 status = STATE_OK; 250 rtastr = 1 + index(rtastr, '/');
220 251 loss = strtod(losstr, NULL);
221 } 252 rta = strtod(rtastr, NULL);
222 else if (strstr (buf, "xmt/rcv/%loss") && strstr (buf, "min/avg/max")) { 253 if (cpl_p && loss > cpl) {
223 losstr = strstr (buf, "="); 254 status = STATE_CRITICAL;
224 losstr = 1 + strstr (losstr, "/"); 255 } else if (crta_p && rta > crta) {
225 losstr = 1 + strstr (losstr, "/"); 256 status = STATE_CRITICAL;
226 rtastr = strstr (buf, "min/avg/max"); 257 } else if (wpl_p && loss > wpl) {
227 rtastr = strstr (rtastr, "="); 258 status = STATE_WARNING;
228 rtastr = 1 + index (rtastr, '/'); 259 } else if (wrta_p && rta > wrta) {
229 loss = strtod (losstr, NULL); 260 status = STATE_WARNING;
230 rta = strtod (rtastr, NULL); 261 } else {
231 if (cpl_p && loss > cpl) 262 status = STATE_OK;
232 status = STATE_CRITICAL; 263 }
233 else if (crta_p && rta > crta) 264 die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status),
234 status = STATE_CRITICAL; 265 server_name, loss, rta,
235 else if (wpl_p && loss > wpl) 266 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0),
236 status = STATE_WARNING; 267 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0,
237 else if (wrta_p && rta > wrta) 268 false, 0));
238 status = STATE_WARNING; 269
239 else 270 } else if (strstr(buf, "xmt/rcv/%loss")) {
240 status = STATE_OK; 271 /* no min/max/avg if host was unreachable in fping v2.2.b1 */
241 die (status, 272 /* in v2.4b2: 10.99.0.1 : xmt/rcv/%loss = 0/0/0% */
242 _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), 273 losstr = strstr(buf, "=");
243 state_text (status), server_name, loss, rta, 274 xmtstr = 1 + losstr;
244 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), 275 xmt = strtod(xmtstr, NULL);
245 fperfdata ("rta", rta/1.0e3, "s", wrta_p, wrta/1.0e3, crta_p, crta/1.0e3, true, 0, false, 0)); 276 if (xmt == 0) {
246 277 die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name);
247 } 278 }
248 else if(strstr (buf, "xmt/rcv/%loss") ) { 279 losstr = 1 + strstr(losstr, "/");
249 /* no min/max/avg if host was unreachable in fping v2.2.b1 */ 280 losstr = 1 + strstr(losstr, "/");
250 /* in v2.4b2: 10.99.0.1 : xmt/rcv/%loss = 0/0/0% */ 281 loss = strtod(losstr, NULL);
251 losstr = strstr (buf, "="); 282 if (atoi(losstr) == 100) {
252 xmtstr = 1 + losstr; 283 status = STATE_CRITICAL;
253 xmt = strtod (xmtstr, NULL); 284 } else if (cpl_p && loss > cpl) {
254 if(xmt == 0) 285 status = STATE_CRITICAL;
255 die (STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name); 286 } else if (wpl_p && loss > wpl) {
256 losstr = 1 + strstr (losstr, "/"); 287 status = STATE_WARNING;
257 losstr = 1 + strstr (losstr, "/"); 288 } else {
258 loss = strtod (losstr, NULL); 289 status = STATE_OK;
259 if (atoi(losstr) == 100) 290 }
260 status = STATE_CRITICAL; 291 /* loss=%.0f%%;%d;%d;0;100 */
261 else if (cpl_p && loss > cpl) 292 die(status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), state_text(status), server_name, loss,
262 status = STATE_CRITICAL; 293 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0));
263 else if (wpl_p && loss > wpl) 294
264 status = STATE_WARNING; 295 } else {
265 else 296 status = max_state(status, STATE_WARNING);
266 status = STATE_OK; 297 }
267 /* loss=%.0f%%;%d;%d;0;100 */ 298
268 die (status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), 299 return status;
269 state_text (status), server_name, loss ,
270 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100));
271
272 }
273 else {
274 status = max_state (status, STATE_WARNING);
275 }
276
277 return status;
278} 300}
279 301
280
281
282/* process command-line arguments */ 302/* process command-line arguments */
283int 303check_fping_config_wrapper process_arguments(int argc, char **argv) {
284process_arguments (int argc, char **argv) 304 enum {
285{ 305 FWMARK_OPT = CHAR_MAX + 1,
286 int c; 306 ICMP_TIMESTAMP_OPT,
287 char *rv[2]; 307 CHECK_SOURCE_OPT,
288 308 };
289 int option = 0; 309 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
290 static struct option longopts[] = { 310 {"sourceip", required_argument, 0, 'S'},
291 {"hostname", required_argument, 0, 'H'}, 311 {"sourceif", required_argument, 0, 'I'},
292 {"sourceip", required_argument, 0, 'S'}, 312 {"critical", required_argument, 0, 'c'},
293 {"sourceif", required_argument, 0, 'I'}, 313 {"warning", required_argument, 0, 'w'},
294 {"critical", required_argument, 0, 'c'}, 314 {"alive", no_argument, 0, 'a'},
295 {"warning", required_argument, 0, 'w'}, 315 {"bytes", required_argument, 0, 'b'},
296 {"alive", no_argument, 0, 'a'}, 316 {"number", required_argument, 0, 'n'},
297 {"bytes", required_argument, 0, 'b'}, 317 {"target-timeout", required_argument, 0, 'T'},
298 {"number", required_argument, 0, 'n'}, 318 {"interval", required_argument, 0, 'i'},
299 {"target-timeout", required_argument, 0, 'T'}, 319 {"verbose", no_argument, 0, 'v'},
300 {"interval", required_argument, 0, 'i'}, 320 {"version", no_argument, 0, 'V'},
301 {"verbose", no_argument, 0, 'v'}, 321 {"help", no_argument, 0, 'h'},
302 {"version", no_argument, 0, 'V'}, 322 {"use-ipv4", no_argument, 0, '4'},
303 {"help", no_argument, 0, 'h'}, 323 {"use-ipv6", no_argument, 0, '6'},
304 {"use-ipv4", no_argument, 0, '4'}, 324 {"dontfrag", no_argument, 0, 'M'},
305 {"use-ipv6", no_argument, 0, '6'}, 325 {"random", no_argument, 0, 'R'},
306 {0, 0, 0, 0} 326#ifdef FPING_VERSION_5_2_OR_HIGHER
307 }; 327 // only available with fping version >= 5.2
308 328 {"fwmark", required_argument, NULL, FWMARK_OPT},
309 rv[PL] = NULL; 329# ifdef FPING_VERSION_5_3_OR_HIGHER
310 rv[RTA] = NULL; 330 // only available with fping version >= 5.3
311 331 {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT},
312 if (argc < 2) 332 {"check-source", no_argument, NULL, CHECK_SOURCE_OPT},
313 return ERROR; 333# endif
314
315 if (!is_option (argv[1])) {
316 server_name = argv[1];
317 argv[1] = argv[0];
318 argv = &argv[1];
319 argc--;
320 }
321
322 while (1) {
323 c = getopt_long (argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:46", longopts, &option);
324
325 if (c == -1 || c == EOF || c == 1)
326 break;
327
328 switch (c) {
329 case '?': /* print short usage statement if args not parsable */
330 usage5 ();
331 case 'a': /* host alive mode */
332 alive_p = true;
333 break;
334 case 'h': /* help */
335 print_help ();
336 exit (STATE_UNKNOWN);
337 case 'V': /* version */
338 print_revision (progname, NP_VERSION);
339 exit (STATE_UNKNOWN);
340 case 'v': /* verbose mode */
341 verbose = true;
342 break;
343 case 'H': /* hostname */
344 if (is_host (optarg) == false) {
345 usage2 (_("Invalid hostname/address"), optarg);
346 }
347 server_name = strscpy (server_name, optarg);
348 break;
349 case 'S': /* sourceip */
350 if (is_host (optarg) == false) {
351 usage2 (_("Invalid hostname/address"), optarg);
352 }
353 sourceip = strscpy (sourceip, optarg);
354 break;
355 case 'I': /* sourceip */
356 sourceif = strscpy (sourceif, optarg);
357 break;
358 case '4': /* IPv4 only */
359 address_family = AF_INET;
360 break;
361 case '6': /* IPv6 only */
362#ifdef USE_IPV6
363 address_family = AF_INET6;
364#else
365 usage (_("IPv6 support not available\n"));
366#endif 334#endif
367 break; 335 {0, 0, 0, 0}};
368 case 'c':
369 get_threshold (optarg, rv);
370 if (rv[RTA]) {
371 crta = strtod (rv[RTA], NULL);
372 crta_p = true;
373 rv[RTA] = NULL;
374 }
375 if (rv[PL]) {
376 cpl = atoi (rv[PL]);
377 cpl_p = true;
378 rv[PL] = NULL;
379 }
380 break;
381 case 'w':
382 get_threshold (optarg, rv);
383 if (rv[RTA]) {
384 wrta = strtod (rv[RTA], NULL);
385 wrta_p = true;
386 rv[RTA] = NULL;
387 }
388 if (rv[PL]) {
389 wpl = atoi (rv[PL]);
390 wpl_p = true;
391 rv[PL] = NULL;
392 }
393 break;
394 case 'b': /* bytes per packet */
395 if (is_intpos (optarg))
396 packet_size = atoi (optarg);
397 else
398 usage (_("Packet size must be a positive integer"));
399 break;
400 case 'n': /* number of packets */
401 if (is_intpos (optarg))
402 packet_count = atoi (optarg);
403 else
404 usage (_("Packet count must be a positive integer"));
405 break;
406 case 'T': /* timeout in msec */
407 if (is_intpos (optarg))
408 target_timeout = atoi (optarg);
409 else
410 usage (_("Target timeout must be a positive integer"));
411 break;
412 case 'i': /* interval in msec */
413 if (is_intpos (optarg))
414 packet_interval = atoi (optarg);
415 else
416 usage (_("Interval must be a positive integer"));
417 break;
418 }
419 }
420
421 if (server_name == NULL)
422 usage4 (_("Hostname was not supplied"));
423
424 return OK;
425}
426
427 336
428int 337 char *rv[2];
429get_threshold (char *arg, char *rv[2]) 338 rv[PL] = NULL;
430{ 339 rv[RTA] = NULL;
431 char *arg1 = NULL;
432 char *arg2 = NULL;
433
434 arg1 = strscpy (arg1, arg);
435 if (strpbrk (arg1, ",:"))
436 arg2 = 1 + strpbrk (arg1, ",:");
437
438 if (arg2) {
439 arg1[strcspn (arg1, ",:")] = 0;
440 if (strstr (arg1, "%") && strstr (arg2, "%"))
441 die (STATE_UNKNOWN,
442 _("%s: Only one threshold may be packet loss (%s)\n"), progname,
443 arg);
444 if (!strstr (arg1, "%") && !strstr (arg2, "%"))
445 die (STATE_UNKNOWN,
446 _("%s: Only one threshold must be packet loss (%s)\n"),
447 progname, arg);
448 }
449
450 if (arg2 && strstr (arg2, "%")) {
451 rv[PL] = arg2;
452 rv[RTA] = arg1;
453 }
454 else if (arg2) {
455 rv[PL] = arg1;
456 rv[RTA] = arg2;
457 }
458 else if (strstr (arg1, "%")) {
459 rv[PL] = arg1;
460 }
461 else {
462 rv[RTA] = arg1;
463 }
464
465 return OK;
466}
467 340
341 int option = 0;
468 342
469void print_help (void) { 343 check_fping_config_wrapper result = {
344 .errorcode = OK,
345 .config = check_fping_config_init(),
346 };
470 347
471 print_revision (progname, NP_VERSION); 348 if (argc < 2) {
349 result.errorcode = ERROR;
350 return result;
351 }
472 352
473 printf ("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n"); 353 if (!is_option(argv[1])) {
474 printf (COPYRIGHT, copyright, email); 354 result.config.server_name = argv[1];
355 argv[1] = argv[0];
356 argv = &argv[1];
357 argc--;
358 }
475 359
476 printf ("%s\n", _("This plugin will use the fping command to ping the specified host for a fast check")); 360 while (true) {
361 int option_index =
362 getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option);
477 363
478 printf ("%s\n", _("Note that it is necessary to set the suid flag on fping.")); 364 if (option_index == -1 || option_index == EOF || option_index == 1) {
365 break;
366 }
479 367
480 printf ("\n\n"); 368 switch (option_index) {
369 case '?': /* print short usage statement if args not parsable */
370 usage5();
371 case 'a': /* host alive mode */
372 result.config.alive_p = true;
373 break;
374 case 'h': /* help */
375 print_help();
376 exit(STATE_UNKNOWN);
377 case 'V': /* version */
378 print_revision(progname, NP_VERSION);
379 exit(STATE_UNKNOWN);
380 case 'v': /* verbose mode */
381 verbose = true;
382 break;
383 case 'H': /* hostname */
384 if (!is_host(optarg)) {
385 usage2(_("Invalid hostname/address"), optarg);
386 }
387 result.config.server_name = optarg;
388 break;
389 case 'S': /* sourceip */
390 if (!is_host(optarg)) {
391 usage2(_("Invalid hostname/address"), optarg);
392 }
393 result.config.sourceip = optarg;
394 break;
395 case 'I': /* sourceip */
396 result.config.sourceif = optarg;
397 break;
398 case '4': /* IPv4 only */
399 address_family = AF_INET;
400 break;
401 case '6': /* IPv6 only */
402 address_family = AF_INET6;
403 break;
404 case 'c':
405 get_threshold(optarg, rv);
406 if (rv[RTA]) {
407 result.config.crta = strtod(rv[RTA], NULL);
408 result.config.crta_p = true;
409 rv[RTA] = NULL;
410 }
411 if (rv[PL]) {
412 result.config.cpl = atoi(rv[PL]);
413 result.config.cpl_p = true;
414 rv[PL] = NULL;
415 }
416 break;
417 case 'w':
418 get_threshold(optarg, rv);
419 if (rv[RTA]) {
420 result.config.wrta = strtod(rv[RTA], NULL);
421 result.config.wrta_p = true;
422 rv[RTA] = NULL;
423 }
424 if (rv[PL]) {
425 result.config.wpl = atoi(rv[PL]);
426 result.config.wpl_p = true;
427 rv[PL] = NULL;
428 }
429 break;
430 case 'b': /* bytes per packet */
431 if (is_intpos(optarg)) {
432 result.config.packet_size = atoi(optarg);
433 } else {
434 usage(_("Packet size must be a positive integer"));
435 }
436 break;
437 case 'n': /* number of packets */
438 if (is_intpos(optarg)) {
439 result.config.packet_count = atoi(optarg);
440 } else {
441 usage(_("Packet count must be a positive integer"));
442 }
443 break;
444 case 'T': /* timeout in msec */
445 if (is_intpos(optarg)) {
446 result.config.target_timeout = atoi(optarg);
447 } else {
448 usage(_("Target timeout must be a positive integer"));
449 }
450 break;
451 case 'i': /* interval in msec */
452 if (is_intpos(optarg)) {
453 result.config.packet_interval = atoi(optarg);
454 } else {
455 usage(_("Interval must be a positive integer"));
456 }
457 break;
458 case 'R':
459 result.config.randomize_packet_data = true;
460 break;
461 case 'M':
462 result.config.dontfrag = true;
463 break;
464 case FWMARK_OPT:
465 if (is_intpos(optarg)) {
466 result.config.fwmark = (unsigned int)atol(optarg);
467 result.config.fwmark_set = true;
468 } else {
469 usage(_("fwmark must be a positive integer"));
470 }
471 break;
472 case ICMP_TIMESTAMP_OPT:
473 result.config.icmp_timestamp = true;
474 break;
475 case CHECK_SOURCE_OPT:
476 result.config.check_source = true;
477 break;
478 }
479 }
481 480
482 print_usage (); 481 if (result.config.server_name == NULL) {
482 usage4(_("Hostname was not supplied"));
483 }
483 484
484 printf (UT_HELP_VRSN); 485 return result;
485 printf (UT_EXTRA_OPTS); 486}
486 487
487 printf (UT_IPv46); 488int get_threshold(char *arg, char *rv[2]) {
489 char *arg2 = NULL;
490
491 char *arg1 = strdup(arg);
492 if (strpbrk(arg1, ",:")) {
493 arg2 = 1 + strpbrk(arg1, ",:");
494 }
495
496 if (arg2) {
497 arg1[strcspn(arg1, ",:")] = 0;
498 if (strstr(arg1, "%") && strstr(arg2, "%")) {
499 die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname,
500 arg);
501 }
502 if (!strstr(arg1, "%") && !strstr(arg2, "%")) {
503 die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname,
504 arg);
505 }
506 }
507
508 if (arg2 && strstr(arg2, "%")) {
509 rv[PL] = arg2;
510 rv[RTA] = arg1;
511 } else if (arg2) {
512 rv[PL] = arg1;
513 rv[RTA] = arg2;
514 } else if (strstr(arg1, "%")) {
515 rv[PL] = arg1;
516 } else {
517 rv[RTA] = arg1;
518 }
519
520 return OK;
521}
488 522
489 printf (" %s\n", "-H, --hostname=HOST"); 523void print_help(void) {
490 printf (" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, reducing system load)")); 524
491 printf (" %s\n", "-w, --warning=THRESHOLD"); 525 print_revision(progname, NP_VERSION);
492 printf (" %s\n", _("warning threshold pair")); 526
493 printf (" %s\n", "-c, --critical=THRESHOLD"); 527 printf("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n");
494 printf (" %s\n", _("critical threshold pair")); 528 printf(COPYRIGHT, copyright, email);
495 printf (" %s\n", "-a, --alive"); 529
496 printf (" %s\n", _("Return OK after first successful reply")); 530 printf("%s\n",
497 printf (" %s\n", "-b, --bytes=INTEGER"); 531 _("This plugin will use the fping command to ping the specified host for a fast check"));
498 printf (" %s (default: %d)\n", _("size of ICMP packet"),PACKET_SIZE); 532
499 printf (" %s\n", "-n, --number=INTEGER"); 533 printf("%s\n", _("Note that it is necessary to set the suid flag on fping."));
500 printf (" %s (default: %d)\n", _("number of ICMP packets to send"),PACKET_COUNT); 534
501 printf (" %s\n", "-T, --target-timeout=INTEGER"); 535 printf("\n\n");
502 printf (" %s (default: fping's default for -t)\n", _("Target timeout (ms)")); 536
503 printf (" %s\n", "-i, --interval=INTEGER"); 537 print_usage();
504 printf (" %s (default: fping's default for -p)\n", _("Interval (ms) between sending packets")); 538
505 printf (" %s\n", "-S, --sourceip=HOST"); 539 printf(UT_HELP_VRSN);
506 printf (" %s\n", _("name or IP Address of sourceip")); 540 printf(UT_EXTRA_OPTS);
507 printf (" %s\n", "-I, --sourceif=IF"); 541
508 printf (" %s\n", _("source interface name")); 542 printf(UT_IPv46);
509 printf (UT_VERBOSE); 543
510 printf ("\n"); 544 printf(" %s\n", "-H, --hostname=HOST");
511 printf (" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)")); 545 printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, "
512 printf (" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of")); 546 "reducing system load)"));
513 printf (" %s\n", _("packet loss to trigger an alarm state.")); 547 printf(" %s\n", "-w, --warning=THRESHOLD");
548 printf(" %s\n", _("warning threshold pair"));
549 printf(" %s\n", "-c, --critical=THRESHOLD");
550 printf(" %s\n", _("critical threshold pair"));
551 printf(" %s\n", "-a, --alive");
552 printf(" %s\n", _("Return OK after first successful reply"));
553 printf(" %s\n", "-b, --bytes=INTEGER");
554 printf(" %s (default: %d)\n", _("size of ICMP packet"), PACKET_SIZE);
555 printf(" %s\n", "-n, --number=INTEGER");
556 printf(" %s (default: %d)\n", _("number of ICMP packets to send"), PACKET_COUNT);
557 printf(" %s\n", "-T, --target-timeout=INTEGER");
558 printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)"));
559 printf(" %s\n", "-i, --interval=INTEGER");
560 printf(" %s (default: fping's default for -p)\n",
561 _("Interval (ms) between sending packets"));
562 printf(" %s\n", "-S, --sourceip=HOST");
563 printf(" %s\n", _("name or IP Address of sourceip"));
564 printf(" %s\n", "-I, --sourceif=IF");
565 printf(" %s\n", _("source interface name"));
566 printf(" %s\n", "-M, --dontfrag");
567 printf(" %s\n", _("set the Don't Fragment flag"));
568 printf(" %s\n", "-R, --random");
569 printf(" %s\n", _("random packet data (to foil link data compression)"));
570#ifdef FPING_VERSION_5_2_OR_HIGHER
571 printf(" %s\n", "--fwmark=INTEGER");
572 printf(" %s\n", _("set the routing mark to INTEGER (fping option)"));
573# ifdef FPING_VERSION_5_3_OR_HIGHER
574 printf(" %s\n", "--icmp-timestamp");
575 printf(" %s\n", _("use ICMP Timestamp instead of ICMP Echo (fping option)"));
576 printf(" %s\n", "--check-source");
577 printf(" %s\n", _("discard replies not from target address (fping option)"));
578# endif
579#endif
580 printf(UT_VERBOSE);
581 printf("\n");
582 printf(" %s\n",
583 _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)"));
584 printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of"));
585 printf(" %s\n", _("packet loss to trigger an alarm state."));
514 586
515 printf ("\n"); 587 printf("\n");
516 printf (" %s\n", _("IPv4 is used by default. Specify -6 to use IPv6.")); 588 printf(" %s\n", _("IPv4 is used by default. Specify -6 to use IPv6."));
517 589
518 printf (UT_SUPPORT); 590 printf(UT_SUPPORT);
519} 591}
520 592
521 593void print_usage(void) {
522void 594 printf("%s\n", _("Usage:"));
523print_usage (void) 595 printf(" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n",
524{ 596 progname);
525 printf ("%s\n", _("Usage:"));
526 printf (" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", progname);
527} 597}
diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h
new file mode 100644
index 00000000..d3e50565
--- /dev/null
+++ b/plugins/check_fping.d/config.h
@@ -0,0 +1,81 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PACKET_SIZE = 56,
8 PACKET_COUNT = 1,
9};
10
11typedef struct {
12 char *server_name;
13 char *sourceip;
14 char *sourceif;
15 int packet_size;
16 int packet_count;
17 int target_timeout;
18 int packet_interval;
19 bool randomize_packet_data;
20 bool dontfrag;
21 bool alive_p;
22
23 double crta;
24 bool crta_p;
25 double wrta;
26 bool wrta_p;
27
28 int cpl;
29 bool cpl_p;
30 int wpl;
31 bool wpl_p;
32
33 // only available with fping version >= 5.2
34 // for a given uint _fwmark_ fping sets _fwmark_ as a firewall mark
35 // in the packets
36 unsigned int fwmark;
37 bool fwmark_set;
38
39 // only available with fping version >= 5.3
40 // Setting icmp_timestamp tells fping to use ICMP Timestamp (ICMP type 13) instead
41 // of ICMP Echo
42 bool icmp_timestamp;
43
44 // Setting check_source lets fping discard replies which are not from the target address
45 bool check_source;
46} check_fping_config;
47
48check_fping_config check_fping_config_init() {
49 check_fping_config tmp = {
50 .server_name = NULL,
51 .sourceip = NULL,
52 .sourceif = NULL,
53 .packet_size = PACKET_SIZE,
54 .packet_count = PACKET_COUNT,
55 .target_timeout = 0,
56 .packet_interval = 0,
57 .randomize_packet_data = false,
58 .dontfrag = false,
59 .alive_p = false,
60
61 .crta = 0,
62 .crta_p = false,
63 .wrta = 0,
64 .wrta_p = false,
65
66 .cpl = 0,
67 .cpl_p = false,
68 .wpl = 0,
69 .wpl_p = false,
70
71 // only available with fping version >= 5.2
72 .fwmark = 0,
73 .fwmark_set = false, // just to be deterministic
74
75 // only available with fping version >= 5.3
76 .icmp_timestamp = false,
77 .check_source = false,
78
79 };
80 return tmp;
81}
diff --git a/plugins/check_game.c b/plugins/check_game.c
index ca126973..974a7253 100644
--- a/plugins/check_game.c
+++ b/plugins/check_game.c
@@ -1,335 +1,329 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_game plugin 3 * Monitoring check_game plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2002-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_game plugin 10 * This file contains the check_game plugin
11* 11 *
12* This plugin tests game server connections with the specified host. 12 * This plugin tests game server connections with the specified host.
13* using the qstat program 13 * using the qstat program
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_game"; 32const char *progname = "check_game";
33const char *copyright = "2002-2007"; 33const char *copyright = "2002-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "utils.h" 37#include "utils.h"
38#include "runcmd.h" 38#include "runcmd.h"
39#include "check_game.d/config.h"
40#include "../lib/monitoringplug.h"
39 41
40int process_arguments (int, char **); 42typedef struct {
41int validate_arguments (void); 43 int errorcode;
42void print_help (void); 44 check_game_config config;
43void print_usage (void); 45} check_game_config_wrapper;
44 46
45#define QSTAT_DATA_DELIMITER "," 47static check_game_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
48static void print_help(void);
49void print_usage(void);
46 50
47#define QSTAT_HOST_ERROR "ERROR" 51#define QSTAT_DATA_DELIMITER ","
48#define QSTAT_HOST_DOWN "DOWN"
49#define QSTAT_HOST_TIMEOUT "TIMEOUT"
50#define QSTAT_MAX_RETURN_ARGS 12
51
52char *server_ip;
53char *game_type;
54int port = 0;
55
56bool verbose = false;
57
58int qstat_game_players_max = -1;
59int qstat_game_players = -1;
60int qstat_game_field = -1;
61int qstat_map_field = -1;
62int qstat_ping_field = -1;
63
64
65int
66main (int argc, char **argv)
67{
68 char *command_line;
69 int result = STATE_UNKNOWN;
70 char *p, *ret[QSTAT_MAX_RETURN_ARGS];
71 size_t i = 0;
72 output chld_out;
73
74 setlocale (LC_ALL, "");
75 bindtextdomain (PACKAGE, LOCALEDIR);
76 textdomain (PACKAGE);
77
78 /* Parse extra opts if any */
79 argv=np_extra_opts (&argc, argv, progname);
80
81 if (process_arguments (argc, argv) == ERROR)
82 usage_va(_("Could not parse arguments"));
83
84 result = STATE_OK;
85
86 /* create the command line to execute */
87 xasprintf (&command_line, "%s -raw %s -%s %s",
88 PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, game_type, server_ip);
89
90 if (port)
91 xasprintf (&command_line, "%s:%-d", command_line, port);
92
93 if (verbose)
94 printf ("%s\n", command_line);
95
96 /* run the command. historically, this plugin ignores output on stderr,
97 * as well as return status of the qstat program */
98 (void)np_runcmd(command_line, &chld_out, NULL, 0);
99
100 /* sanity check */
101 /* was thinking about running qstat without any options, capturing the
102 -default line, parsing it & making an array of all know server types
103 but thought this would be too much hassle considering this is a tool
104 for intelligent sysadmins (ha). Could put a static array of known
105 server types in a header file but then we'd be limiting ourselves
106
107 In the end, I figured I'd simply let an error occur & then trap it
108 */
109
110 if (!strncmp (chld_out.line[0], "unknown option", 14)) {
111 printf (_("CRITICAL - Host type parameter incorrect!\n"));
112 result = STATE_CRITICAL;
113 return result;
114 }
115
116 p = (char *) strtok (chld_out.line[0], QSTAT_DATA_DELIMITER);
117 while (p != NULL) {
118 ret[i] = p;
119 p = (char *) strtok (NULL, QSTAT_DATA_DELIMITER);
120 i++;
121 if (i >= QSTAT_MAX_RETURN_ARGS)
122 break;
123 }
124
125 if (strstr (ret[2], QSTAT_HOST_ERROR)) {
126 printf (_("CRITICAL - Host not found\n"));
127 result = STATE_CRITICAL;
128 }
129 else if (strstr (ret[2], QSTAT_HOST_DOWN)) {
130 printf (_("CRITICAL - Game server down or unavailable\n"));
131 result = STATE_CRITICAL;
132 }
133 else if (strstr (ret[2], QSTAT_HOST_TIMEOUT)) {
134 printf (_("CRITICAL - Game server timeout\n"));
135 result = STATE_CRITICAL;
136 }
137 else {
138 printf ("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n",
139 ret[qstat_game_players],
140 ret[qstat_game_players_max],
141 ret[qstat_game_field],
142 ret[qstat_map_field],
143 ret[qstat_ping_field],
144 perfdata ("players", atol(ret[qstat_game_players]), "",
145 false, 0, false, 0,
146 true, 0, true, atol(ret[qstat_game_players_max])),
147 fperfdata ("ping", strtod(ret[qstat_ping_field], NULL), "",
148 false, 0, false, 0,
149 true, 0, false, 0));
150 }
151
152 return result;
153}
154 52
53#define QSTAT_HOST_ERROR "ERROR"
54#define QSTAT_HOST_DOWN "DOWN"
55#define QSTAT_HOST_TIMEOUT "TIMEOUT"
56#define QSTAT_MAX_RETURN_ARGS 12
155 57
156int 58static bool verbose = false;
157process_arguments (int argc, char **argv) 59
158{ 60int main(int argc, char **argv) {
159 int c; 61 setlocale(LC_ALL, "");
160 62 bindtextdomain(PACKAGE, LOCALEDIR);
161 int opt_index = 0; 63 textdomain(PACKAGE);
162 static struct option long_opts[] = { 64
163 {"help", no_argument, 0, 'h'}, 65 /* Parse extra opts if any */
164 {"version", no_argument, 0, 'V'}, 66 argv = np_extra_opts(&argc, argv, progname);
165 {"verbose", no_argument, 0, 'v'}, 67
166 {"timeout", required_argument, 0, 't'}, 68 check_game_config_wrapper tmp = process_arguments(argc, argv);
167 {"hostname", required_argument, 0, 'H'}, 69
168 {"port", required_argument, 0, 'P'}, 70 if (tmp.errorcode == ERROR) {
169 {"game-type", required_argument, 0, 'G'}, 71 usage_va(_("Could not parse arguments"));
170 {"map-field", required_argument, 0, 'm'}, 72 }
171 {"ping-field", required_argument, 0, 'p'}, 73
172 {"game-field", required_argument, 0, 'g'}, 74 check_game_config config = tmp.config;
173 {"players-field", required_argument, 0, 129}, 75
174 {"max-players-field", required_argument, 0, 130}, 76 mp_state_enum result = STATE_OK;
175 {0, 0, 0, 0} 77
176 }; 78 /* create the command line to execute */
177 79 char *command_line = NULL;
178 if (argc < 2) 80 xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER,
179 return ERROR; 81 config.game_type, config.server_ip);
180 82
181 for (c = 1; c < argc; c++) { 83 if (config.port) {
182 if (strcmp ("-mf", argv[c]) == 0) 84 xasprintf(&command_line, "%s:%-d", command_line, config.port);
183 strcpy (argv[c], "-m"); 85 }
184 else if (strcmp ("-pf", argv[c]) == 0) 86
185 strcpy (argv[c], "-p"); 87 if (verbose) {
186 else if (strcmp ("-gf", argv[c]) == 0) 88 printf("%s\n", command_line);
187 strcpy (argv[c], "-g"); 89 }
188 } 90
189 91 /* run the command. historically, this plugin ignores output on stderr,
190 while (1) { 92 * as well as return status of the qstat program */
191 c = getopt_long (argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index); 93 output chld_out = {};
192 94 (void)np_runcmd(command_line, &chld_out, NULL, 0);
193 if (c == -1 || c == EOF) 95
194 break; 96 /* sanity check */
195 97 /* was thinking about running qstat without any options, capturing the
196 switch (c) { 98 -default line, parsing it & making an array of all know server types
197 case 'h': /* help */ 99 but thought this would be too much hassle considering this is a tool
198 print_help (); 100 for intelligent sysadmins (ha). Could put a static array of known
199 exit (STATE_UNKNOWN); 101 server types in a header file but then we'd be limiting ourselves
200 case 'V': /* version */ 102
201 print_revision (progname, NP_VERSION); 103 In the end, I figured I'd simply let an error occur & then trap it
202 exit (STATE_UNKNOWN); 104 */
203 case 'v': /* version */ 105
204 verbose = true; 106 if (!strncmp(chld_out.line[0], "unknown option", strlen("unknown option"))) {
205 break; 107 printf(_("CRITICAL - Host type parameter incorrect!\n"));
206 case 't': /* timeout period */ 108 result = STATE_CRITICAL;
207 timeout_interval = atoi (optarg); 109 exit(result);
208 break; 110 }
209 case 'H': /* hostname */ 111
210 if (strlen (optarg) >= MAX_HOST_ADDRESS_LENGTH) 112 char *ret[QSTAT_MAX_RETURN_ARGS];
211 die (STATE_UNKNOWN, _("Input buffer overflow\n")); 113 size_t i = 0;
212 server_ip = optarg; 114 char *sequence = strtok(chld_out.line[0], QSTAT_DATA_DELIMITER);
213 break; 115 while (sequence != NULL) {
214 case 'P': /* port */ 116 ret[i] = sequence;
215 port = atoi (optarg); 117 sequence = strtok(NULL, QSTAT_DATA_DELIMITER);
216 break; 118 i++;
217 case 'G': /* hostname */ 119 if (i >= QSTAT_MAX_RETURN_ARGS) {
218 if (strlen (optarg) >= MAX_INPUT_BUFFER) 120 break;
219 die (STATE_UNKNOWN, _("Input buffer overflow\n")); 121 }
220 game_type = optarg; 122 }
221 break; 123
222 case 'p': /* index of ping field */ 124 if (strstr(ret[2], QSTAT_HOST_ERROR)) {
223 qstat_ping_field = atoi (optarg); 125 printf(_("CRITICAL - Host not found\n"));
224 if (qstat_ping_field < 0 || qstat_ping_field > QSTAT_MAX_RETURN_ARGS) 126 result = STATE_CRITICAL;
225 return ERROR; 127 } else if (strstr(ret[2], QSTAT_HOST_DOWN)) {
226 break; 128 printf(_("CRITICAL - Game server down or unavailable\n"));
227 case 'm': /* index on map field */ 129 result = STATE_CRITICAL;
228 qstat_map_field = atoi (optarg); 130 } else if (strstr(ret[2], QSTAT_HOST_TIMEOUT)) {
229 if (qstat_map_field < 0 || qstat_map_field > QSTAT_MAX_RETURN_ARGS) 131 printf(_("CRITICAL - Game server timeout\n"));
230 return ERROR; 132 result = STATE_CRITICAL;
231 break; 133 } else {
232 case 'g': /* index of game field */ 134 printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[config.qstat_game_players],
233 qstat_game_field = atoi (optarg); 135 ret[config.qstat_game_players_max], ret[config.qstat_game_field],
234 if (qstat_game_field < 0 || qstat_game_field > QSTAT_MAX_RETURN_ARGS) 136 ret[config.qstat_map_field], ret[config.qstat_ping_field],
235 return ERROR; 137 perfdata("players", atol(ret[config.qstat_game_players]), "", false, 0, false, 0,
236 break; 138 true, 0, true, atol(ret[config.qstat_game_players_max])),
237 case 129: /* index of player count field */ 139 fperfdata("ping", strtod(ret[config.qstat_ping_field], NULL), "", false, 0, false, 0,
238 qstat_game_players = atoi (optarg); 140 true, 0, false, 0));
239 if (qstat_game_players_max == 0) 141 }
240 qstat_game_players_max = qstat_game_players - 1; 142
241 if (qstat_game_players < 0 || qstat_game_players > QSTAT_MAX_RETURN_ARGS) 143 exit(result);
242 return ERROR;
243 break;
244 case 130: /* index of max players field */
245 qstat_game_players_max = atoi (optarg);
246 if (qstat_game_players_max < 0 || qstat_game_players_max > QSTAT_MAX_RETURN_ARGS)
247 return ERROR;
248 break;
249 default: /* args not parsable */
250 usage5();
251 }
252 }
253
254 c = optind;
255 /* first option is the game type */
256 if (!game_type && c<argc)
257 game_type = strdup (argv[c++]);
258
259 /* Second option is the server name */
260 if (!server_ip && c<argc)
261 server_ip = strdup (argv[c++]);
262
263 return validate_arguments ();
264} 144}
265 145
266 146#define players_field_index 129
267int 147#define max_players_field_index 130
268validate_arguments (void) 148
269{ 149check_game_config_wrapper process_arguments(int argc, char **argv) {
270 if (qstat_game_players_max < 0) 150 static struct option long_opts[] = {
271 qstat_game_players_max = 4; 151 {"help", no_argument, 0, 'h'},
272 152 {"version", no_argument, 0, 'V'},
273 if (qstat_game_players < 0) 153 {"verbose", no_argument, 0, 'v'},
274 qstat_game_players = 5; 154 {"timeout", required_argument, 0, 't'},
275 155 {"hostname", required_argument, 0, 'H'},
276 if (qstat_game_field < 0) 156 {"port", required_argument, 0, 'P'},
277 qstat_game_field = 2; 157 {"game-type", required_argument, 0, 'G'},
278 158 {"map-field", required_argument, 0, 'm'},
279 if (qstat_map_field < 0) 159 {"ping-field", required_argument, 0, 'p'},
280 qstat_map_field = 3; 160 {"game-field", required_argument, 0, 'g'},
281 161 {"players-field", required_argument, 0, players_field_index},
282 if (qstat_ping_field < 0) 162 {"max-players-field", required_argument, 0, max_players_field_index},
283 qstat_ping_field = 5; 163 {0, 0, 0, 0}};
284 164
285 return OK; 165 check_game_config_wrapper result = {
166 .config = check_game_config_init(),
167 .errorcode = OK,
168 };
169
170 if (argc < 2) {
171 result.errorcode = ERROR;
172 return result;
173 }
174
175 for (int option_counter = 1; option_counter < argc; option_counter++) {
176 if (strcmp("-mf", argv[option_counter]) == 0) {
177 strcpy(argv[option_counter], "-m");
178 } else if (strcmp("-pf", argv[option_counter]) == 0) {
179 strcpy(argv[option_counter], "-p");
180 } else if (strcmp("-gf", argv[option_counter]) == 0) {
181 strcpy(argv[option_counter], "-g");
182 }
183 }
184
185 int opt_index = 0;
186 while (true) {
187 int option_index = getopt_long(argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index);
188
189 if (option_index == -1 || option_index == EOF) {
190 break;
191 }
192
193 switch (option_index) {
194 case 'h': /* help */
195 print_help();
196 exit(STATE_UNKNOWN);
197 case 'V': /* version */
198 print_revision(progname, NP_VERSION);
199 exit(STATE_UNKNOWN);
200 case 'v': /* version */
201 verbose = true;
202 break;
203 case 't': /* timeout period */
204 timeout_interval = atoi(optarg);
205 break;
206 case 'H': /* hostname */
207 if (strlen(optarg) >= MAX_HOST_ADDRESS_LENGTH) {
208 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
209 }
210 result.config.server_ip = optarg;
211 break;
212 case 'P': /* port */
213 result.config.port = atoi(optarg);
214 break;
215 case 'G': /* hostname */
216 if (strlen(optarg) >= MAX_INPUT_BUFFER) {
217 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
218 }
219 result.config.game_type = optarg;
220 break;
221 case 'p': /* index of ping field */
222 result.config.qstat_ping_field = atoi(optarg);
223 if (result.config.qstat_ping_field < 0 ||
224 result.config.qstat_ping_field > QSTAT_MAX_RETURN_ARGS) {
225 result.errorcode = ERROR;
226 return result;
227 }
228 break;
229 case 'm': /* index on map field */
230 result.config.qstat_map_field = atoi(optarg);
231 if (result.config.qstat_map_field < 0 ||
232 result.config.qstat_map_field > QSTAT_MAX_RETURN_ARGS) {
233 result.errorcode = ERROR;
234 return result;
235 }
236 break;
237 case 'g': /* index of game field */
238 result.config.qstat_game_field = atoi(optarg);
239 if (result.config.qstat_game_field < 0 ||
240 result.config.qstat_game_field > QSTAT_MAX_RETURN_ARGS) {
241 result.errorcode = ERROR;
242 return result;
243 }
244 break;
245 case players_field_index: /* index of player count field */
246 result.config.qstat_game_players = atoi(optarg);
247 if (result.config.qstat_game_players_max == 0) {
248 result.config.qstat_game_players_max = result.config.qstat_game_players - 1;
249 }
250 if (result.config.qstat_game_players < 0 ||
251 result.config.qstat_game_players > QSTAT_MAX_RETURN_ARGS) {
252 result.errorcode = ERROR;
253 return result;
254 }
255 break;
256 case max_players_field_index: /* index of max players field */
257 result.config.qstat_game_players_max = atoi(optarg);
258 if (result.config.qstat_game_players_max < 0 ||
259 result.config.qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) {
260 result.errorcode = ERROR;
261 return result;
262 }
263 break;
264 default: /* args not parsable */
265 usage5();
266 }
267 }
268
269 int option_counter = optind;
270 /* first option is the game type */
271 if (!result.config.game_type && option_counter < argc) {
272 result.config.game_type = strdup(argv[option_counter++]);
273 }
274
275 /* Second option is the server name */
276 if (!result.config.server_ip && option_counter < argc) {
277 result.config.server_ip = strdup(argv[option_counter++]);
278 }
279
280 return result;
286} 281}
287 282
283void print_help(void) {
284 print_revision(progname, NP_VERSION);
288 285
289void 286 printf("Copyright (c) 1999 Ian Cass, Knowledge Matters Limited\n");
290print_help (void) 287 printf(COPYRIGHT, copyright, email);
291{
292 print_revision (progname, NP_VERSION);
293
294 printf ("Copyright (c) 1999 Ian Cass, Knowledge Matters Limited\n");
295 printf (COPYRIGHT, copyright, email);
296 288
297 printf (_("This plugin tests game server connections with the specified host.")); 289 printf(_("This plugin tests game server connections with the specified host."));
298 290
299 printf ("\n\n"); 291 printf("\n\n");
300 292
301 print_usage (); 293 print_usage();
302 294
303 printf (UT_HELP_VRSN); 295 printf(UT_HELP_VRSN);
304 printf (UT_EXTRA_OPTS); 296 printf(UT_EXTRA_OPTS);
297 printf(" -H, --hostname=ADDRESS\n"
298 " Host name, IP Address, or unix socket (must be an absolute path)\n");
299 printf(" %s\n", "-P");
300 printf(" %s\n", _("Optional port to connect to"));
301 printf(" %s\n", "-g");
302 printf(" %s\n", _("Field number in raw qstat output that contains game name"));
303 printf(" %s\n", "-m");
304 printf(" %s\n", _("Field number in raw qstat output that contains map name"));
305 printf(" %s\n", "-p");
306 printf(" %s\n", _("Field number in raw qstat output that contains ping time"));
305 307
306 printf (" %s\n", "-p"); 308 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
307 printf (" %s\n", _("Optional port of which to connect"));
308 printf (" %s\n", "gf");
309 printf (" %s\n", _("Field number in raw qstat output that contains game name"));
310 printf (" %s\n", "-mf");
311 printf (" %s\n", _("Field number in raw qstat output that contains map name"));
312 printf (" %s\n", "-pf");
313 printf (" %s\n", _("Field number in raw qstat output that contains ping time"));
314 309
315 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 310 printf("\n");
311 printf("%s\n", _("Notes:"));
312 printf(" %s\n",
313 _("This plugin uses the 'qstat' command, the popular game server status query tool."));
314 printf(" %s\n",
315 _("If you don't have the package installed, you will need to download it from"));
316 printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin."));
316 317
317 printf ("\n"); 318 printf(UT_SUPPORT);
318 printf ("%s\n", _("Notes:"));
319 printf (" %s\n", _("This plugin uses the 'qstat' command, the popular game server status query tool."));
320 printf (" %s\n", _("If you don't have the package installed, you will need to download it from"));
321 printf (" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin."));
322
323 printf (UT_SUPPORT);
324} 319}
325 320
326 321void print_usage(void) {
327 322 printf("%s\n", _("Usage:"));
328void 323 printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G "
329print_usage (void) 324 "game-time] [-H hostname] <game> "
330{ 325 "<ip_address>\n",
331 printf ("%s\n", _("Usage:")); 326 progname);
332 printf (" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G game-time] [-H hostname] <game> <ip_address>\n", progname);
333} 327}
334 328
335/****************************************************************************** 329/******************************************************************************
diff --git a/plugins/check_game.d/config.h b/plugins/check_game.d/config.h
new file mode 100644
index 00000000..c95a1ced
--- /dev/null
+++ b/plugins/check_game.d/config.h
@@ -0,0 +1,30 @@
1#pragma once
2#include "../../config.h"
3#include <stddef.h>
4
5typedef struct {
6 char *server_ip;
7 char *game_type;
8 int port;
9
10 int qstat_game_players_max;
11 int qstat_game_players;
12 int qstat_game_field;
13 int qstat_map_field;
14 int qstat_ping_field;
15} check_game_config;
16
17check_game_config check_game_config_init() {
18 check_game_config tmp = {
19 .server_ip = NULL,
20 .game_type = NULL,
21 .port = 0,
22
23 .qstat_game_players_max = 4,
24 .qstat_game_players = 5,
25 .qstat_map_field = 3,
26 .qstat_game_field = 2,
27 .qstat_ping_field = 5,
28 };
29 return tmp;
30}
diff --git a/plugins/check_hpjd.c b/plugins/check_hpjd.c
index c34bb082..9907abc5 100644
--- a/plugins/check_hpjd.c
+++ b/plugins/check_hpjd.c
@@ -1,47 +1,46 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_hpjd plugin 3 * Monitoring check_hpjd plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_hpjd plugin 10 * This file contains the check_hpjd plugin
11* 11 *
12* This plugin tests the STATUS of an HP printer with a JetDirect card. 12 * This plugin tests the STATUS of an HP printer with a JetDirect card.
13* Net-SNMP must be installed on the computer running the plugin. 13 * Net-SNMP must be installed on the computer running the plugin.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_hpjd"; 32const char *progname = "check_hpjd";
33const char *copyright = "2000-2007"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "popen.h" 37#include "popen.h"
38#include "utils.h" 38#include "utils.h"
39#include "netutils.h" 39#include "netutils.h"
40#include "states.h"
41#include "check_hpjd.d/config.h"
40 42
41#define DEFAULT_COMMUNITY "public" 43#define DEFAULT_COMMUNITY "public"
42#define DEFAULT_PORT "161"
43
44const char *option_summary = "-H host [-C community]\n";
45 44
46#define HPJD_LINE_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.1" 45#define HPJD_LINE_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.1"
47#define HPJD_PAPER_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.2" 46#define HPJD_PAPER_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.2"
@@ -56,185 +55,165 @@ const char *option_summary = "-H host [-C community]\n";
56#define HPJD_GD_PAPER_OUTPUT ".1.3.6.1.4.1.11.2.3.9.1.1.2.19" 55#define HPJD_GD_PAPER_OUTPUT ".1.3.6.1.4.1.11.2.3.9.1.1.2.19"
57#define HPJD_GD_STATUS_DISPLAY ".1.3.6.1.4.1.11.2.3.9.1.1.3" 56#define HPJD_GD_STATUS_DISPLAY ".1.3.6.1.4.1.11.2.3.9.1.1.3"
58 57
59#define ONLINE 0 58#define ONLINE 0
60#define OFFLINE 1 59#define OFFLINE 1
61
62int process_arguments (int, char **);
63int validate_arguments (void);
64void print_help (void);
65void print_usage (void);
66 60
67char *community = NULL; 61typedef struct {
68char *address = NULL; 62 int errorcode;
69unsigned int port = 0; 63 check_hpjd_config config;
70int check_paper_out = 1; 64} check_hpjd_config_wrapper;
65static check_hpjd_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
66static void print_help(void);
67void print_usage(void);
71 68
72int 69int main(int argc, char **argv) {
73main (int argc, char **argv) 70 setlocale(LC_ALL, "");
74{ 71 bindtextdomain(PACKAGE, LOCALEDIR);
75 char command_line[1024]; 72 textdomain(PACKAGE);
76 int result = STATE_UNKNOWN;
77 int line;
78 char input_buffer[MAX_INPUT_BUFFER];
79 char query_string[512];
80 char *errmsg;
81 char *temp_buffer;
82 int line_status = ONLINE;
83 int paper_status = 0;
84 int intervention_required = 0;
85 int peripheral_error = 0;
86 int paper_jam = 0;
87 int paper_out = 0;
88 int toner_low = 0;
89 int page_punt = 0;
90 int memory_out = 0;
91 int door_open = 0;
92 int paper_output = 0;
93 char display_message[MAX_INPUT_BUFFER];
94 73
95 errmsg = malloc(MAX_INPUT_BUFFER); 74 /* Parse extra opts if any */
75 argv = np_extra_opts(&argc, argv, progname);
96 76
97 setlocale (LC_ALL, ""); 77 check_hpjd_config_wrapper tmp_config = process_arguments(argc, argv);
98 bindtextdomain (PACKAGE, LOCALEDIR);
99 textdomain (PACKAGE);
100 78
101 /* Parse extra opts if any */ 79 if (tmp_config.errorcode == ERROR) {
102 argv=np_extra_opts (&argc, argv, progname); 80 usage4(_("Could not parse arguments"));
81 }
103 82
104 if (process_arguments (argc, argv) == ERROR) 83 const check_hpjd_config config = tmp_config.config;
105 usage4 (_("Could not parse arguments"));
106 84
85 char query_string[512];
107 /* removed ' 2>1' at end of command 10/27/1999 - EG */ 86 /* removed ' 2>1' at end of command 10/27/1999 - EG */
108 /* create the query string */ 87 /* create the query string */
109 sprintf 88 sprintf(query_string, "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0",
110 (query_string, 89 HPJD_LINE_STATUS, HPJD_PAPER_STATUS, HPJD_INTERVENTION_REQUIRED,
111 "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0", 90 HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW,
112 HPJD_LINE_STATUS, 91 HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT,
113 HPJD_PAPER_STATUS, 92 HPJD_GD_STATUS_DISPLAY);
114 HPJD_INTERVENTION_REQUIRED,
115 HPJD_GD_PERIPHERAL_ERROR,
116 HPJD_GD_PAPER_JAM,
117 HPJD_GD_PAPER_OUT,
118 HPJD_GD_TONER_LOW,
119 HPJD_GD_PAGE_PUNT,
120 HPJD_GD_MEMORY_OUT,
121 HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT, HPJD_GD_STATUS_DISPLAY);
122 93
123 /* get the command to run */ 94 /* get the command to run */
124 sprintf (command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", 95 char command_line[1024];
125 PATH_TO_SNMPGET, 96 sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, config.community,
126 community, 97 config.address, config.port, query_string);
127 address,
128 port,
129 query_string);
130 98
131 /* run the command */ 99 /* run the command */
132 child_process = spopen (command_line); 100 child_process = spopen(command_line);
133 if (child_process == NULL) { 101 if (child_process == NULL) {
134 printf (_("Could not open pipe: %s\n"), command_line); 102 printf(_("Could not open pipe: %s\n"), command_line);
135 return STATE_UNKNOWN; 103 return STATE_UNKNOWN;
136 } 104 }
137 105
138 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 106 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
139 if (child_stderr == NULL) { 107 if (child_stderr == NULL) {
140 printf (_("Could not open stderr for %s\n"), command_line); 108 printf(_("Could not open stderr for %s\n"), command_line);
141 } 109 }
142 110
143 result = STATE_OK; 111 mp_state_enum result = STATE_OK;
144 112
145 line = 0; 113 int line_status = ONLINE;
146 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 114 int paper_status = 0;
115 int intervention_required = 0;
116 int peripheral_error = 0;
117 int paper_jam = 0;
118 int paper_out = 0;
119 int toner_low = 0;
120 int page_punt = 0;
121 int memory_out = 0;
122 int door_open = 0;
123 int paper_output = 0;
124 char display_message[MAX_INPUT_BUFFER];
125
126 char input_buffer[MAX_INPUT_BUFFER];
127 char *errmsg = malloc(MAX_INPUT_BUFFER);
128 int line = 0;
147 129
130 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
148 /* strip the newline character from the end of the input */ 131 /* strip the newline character from the end of the input */
149 if (input_buffer[strlen (input_buffer) - 1] == '\n') 132 if (input_buffer[strlen(input_buffer) - 1] == '\n') {
150 input_buffer[strlen (input_buffer) - 1] = 0; 133 input_buffer[strlen(input_buffer) - 1] = 0;
134 }
151 135
152 line++; 136 line++;
153 137
154 temp_buffer = strtok (input_buffer, "="); 138 char *temp_buffer = strtok(input_buffer, "=");
155 temp_buffer = strtok (NULL, "="); 139 temp_buffer = strtok(NULL, "=");
156 140
157 if (temp_buffer == NULL && line < 13) { 141 if (temp_buffer == NULL && line < 13) {
158 142 result = STATE_UNKNOWN;
159 result = STATE_UNKNOWN; 143 strcpy(errmsg, input_buffer);
160 strcpy (errmsg, input_buffer);
161
162 } else { 144 } else {
163
164 switch (line) { 145 switch (line) {
165 146 case 1: /* 1st line should contain the line status */
166 case 1: /* 1st line should contain the line status */ 147 line_status = atoi(temp_buffer);
167 line_status = atoi (temp_buffer);
168 break; 148 break;
169 case 2: /* 2nd line should contain the paper status */ 149 case 2: /* 2nd line should contain the paper status */
170 paper_status = atoi (temp_buffer); 150 paper_status = atoi(temp_buffer);
171 break; 151 break;
172 case 3: /* 3rd line should be intervention required */ 152 case 3: /* 3rd line should be intervention required */
173 intervention_required = atoi (temp_buffer); 153 intervention_required = atoi(temp_buffer);
174 break; 154 break;
175 case 4: /* 4th line should be peripheral error */ 155 case 4: /* 4th line should be peripheral error */
176 peripheral_error = atoi (temp_buffer); 156 peripheral_error = atoi(temp_buffer);
177 break; 157 break;
178 case 5: /* 5th line should contain the paper jam status */ 158 case 5: /* 5th line should contain the paper jam status */
179 paper_jam = atoi (temp_buffer); 159 paper_jam = atoi(temp_buffer);
180 break; 160 break;
181 case 6: /* 6th line should contain the paper out status */ 161 case 6: /* 6th line should contain the paper out status */
182 paper_out = atoi (temp_buffer); 162 paper_out = atoi(temp_buffer);
183 break; 163 break;
184 case 7: /* 7th line should contain the toner low status */ 164 case 7: /* 7th line should contain the toner low status */
185 toner_low = atoi (temp_buffer); 165 toner_low = atoi(temp_buffer);
186 break; 166 break;
187 case 8: /* did data come too slow for engine */ 167 case 8: /* did data come too slow for engine */
188 page_punt = atoi (temp_buffer); 168 page_punt = atoi(temp_buffer);
189 break; 169 break;
190 case 9: /* did we run out of memory */ 170 case 9: /* did we run out of memory */
191 memory_out = atoi (temp_buffer); 171 memory_out = atoi(temp_buffer);
192 break; 172 break;
193 case 10: /* is there a door open */ 173 case 10: /* is there a door open */
194 door_open = atoi (temp_buffer); 174 door_open = atoi(temp_buffer);
195 break; 175 break;
196 case 11: /* is output tray full */ 176 case 11: /* is output tray full */
197 paper_output = atoi (temp_buffer); 177 paper_output = atoi(temp_buffer);
198 break; 178 break;
199 case 12: /* display panel message */ 179 case 12: /* display panel message */
200 strcpy (display_message, temp_buffer + 1); 180 strcpy(display_message, temp_buffer + 1);
201 break; 181 break;
202 default: /* fold multiline message */ 182 default: /* fold multiline message */
203 strncat (display_message, input_buffer, 183 strncat(display_message, input_buffer,
204 sizeof (display_message) - strlen (display_message) - 1); 184 sizeof(display_message) - strlen(display_message) - 1);
205 } 185 }
206
207 } 186 }
208 187
209 /* break out of the read loop if we encounter an error */ 188 /* break out of the read loop if we encounter an error */
210 if (result != STATE_OK) 189 if (result != STATE_OK) {
211 break; 190 break;
191 }
212 } 192 }
213 193
214 /* WARNING if output found on stderr */ 194 /* WARNING if output found on stderr */
215 if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { 195 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
216 result = max_state (result, STATE_WARNING); 196 result = max_state(result, STATE_WARNING);
217 /* remove CRLF */ 197 /* remove CRLF */
218 if (input_buffer[strlen (input_buffer) - 1] == '\n') 198 if (input_buffer[strlen(input_buffer) - 1] == '\n') {
219 input_buffer[strlen (input_buffer) - 1] = 0; 199 input_buffer[strlen(input_buffer) - 1] = 0;
220 sprintf (errmsg, "%s", input_buffer ); 200 }
221 201 sprintf(errmsg, "%s", input_buffer);
222 } 202 }
223 203
224 /* close stderr */ 204 /* close stderr */
225 (void) fclose (child_stderr); 205 (void)fclose(child_stderr);
226 206
227 /* close the pipe */ 207 /* close the pipe */
228 if (spclose (child_process)) 208 if (spclose(child_process)) {
229 result = max_state (result, STATE_WARNING); 209 result = max_state(result, STATE_WARNING);
210 }
230 211
231 /* if there wasn't any output, display an error */ 212 /* if there wasn't any output, display an error */
232 if (line == 0) { 213 if (line == 0) {
233
234 /* might not be the problem, but most likely is. */ 214 /* might not be the problem, but most likely is. */
235 result = STATE_UNKNOWN ; 215 result = STATE_UNKNOWN;
236 xasprintf (&errmsg, "%s : Timeout from host %s\n", errmsg, address ); 216 xasprintf(&errmsg, "%s : Timeout from host %s\n", errmsg, config.address);
237
238 } 217 }
239 218
240 /* if we had no read errors, check the printer status results... */ 219 /* if we had no read errors, check the printer status results... */
@@ -242,201 +221,171 @@ main (int argc, char **argv)
242 221
243 if (paper_jam) { 222 if (paper_jam) {
244 result = STATE_WARNING; 223 result = STATE_WARNING;
245 strcpy (errmsg, _("Paper Jam")); 224 strcpy(errmsg, _("Paper Jam"));
246 } 225 } else if (paper_out) {
247 else if (paper_out) { 226 if (config.check_paper_out) {
248 if (check_paper_out)
249 result = STATE_WARNING; 227 result = STATE_WARNING;
250 strcpy (errmsg, _("Out of Paper")); 228 }
251 } 229 strcpy(errmsg, _("Out of Paper"));
252 else if (line_status == OFFLINE) { 230 } else if (line_status == OFFLINE) {
253 if (strcmp (errmsg, "POWERSAVE ON") != 0) { 231 if (strcmp(errmsg, "POWERSAVE ON") != 0) {
254 result = STATE_WARNING; 232 result = STATE_WARNING;
255 strcpy (errmsg, _("Printer Offline")); 233 strcpy(errmsg, _("Printer Offline"));
256 } 234 }
257 } 235 } else if (peripheral_error) {
258 else if (peripheral_error) {
259 result = STATE_WARNING; 236 result = STATE_WARNING;
260 strcpy (errmsg, _("Peripheral Error")); 237 strcpy(errmsg, _("Peripheral Error"));
261 } 238 } else if (intervention_required) {
262 else if (intervention_required) {
263 result = STATE_WARNING; 239 result = STATE_WARNING;
264 strcpy (errmsg, _("Intervention Required")); 240 strcpy(errmsg, _("Intervention Required"));
265 } 241 } else if (toner_low) {
266 else if (toner_low) {
267 result = STATE_WARNING; 242 result = STATE_WARNING;
268 strcpy (errmsg, _("Toner Low")); 243 strcpy(errmsg, _("Toner Low"));
269 } 244 } else if (memory_out) {
270 else if (memory_out) {
271 result = STATE_WARNING; 245 result = STATE_WARNING;
272 strcpy (errmsg, _("Insufficient Memory")); 246 strcpy(errmsg, _("Insufficient Memory"));
273 } 247 } else if (door_open) {
274 else if (door_open) {
275 result = STATE_WARNING; 248 result = STATE_WARNING;
276 strcpy (errmsg, _("A Door is Open")); 249 strcpy(errmsg, _("A Door is Open"));
277 } 250 } else if (paper_output) {
278 else if (paper_output) {
279 result = STATE_WARNING; 251 result = STATE_WARNING;
280 strcpy (errmsg, _("Output Tray is Full")); 252 strcpy(errmsg, _("Output Tray is Full"));
281 } 253 } else if (page_punt) {
282 else if (page_punt) {
283 result = STATE_WARNING; 254 result = STATE_WARNING;
284 strcpy (errmsg, _("Data too Slow for Engine")); 255 strcpy(errmsg, _("Data too Slow for Engine"));
285 } 256 } else if (paper_status) {
286 else if (paper_status) {
287 result = STATE_WARNING; 257 result = STATE_WARNING;
288 strcpy (errmsg, _("Unknown Paper Error")); 258 strcpy(errmsg, _("Unknown Paper Error"));
289 } 259 }
290 } 260 }
291 261
292 if (result == STATE_OK) 262 if (result == STATE_OK) {
293 printf (_("Printer ok - (%s)\n"), display_message); 263 printf(_("Printer ok - (%s)\n"), display_message);
294 264 } else if (result == STATE_UNKNOWN) {
295 else if (result == STATE_UNKNOWN) { 265 printf("%s\n", errmsg);
296
297 printf ("%s\n", errmsg);
298
299 /* if printer could not be reached, escalate to critical */ 266 /* if printer could not be reached, escalate to critical */
300 if (strstr (errmsg, "Timeout")) 267 if (strstr(errmsg, "Timeout")) {
301 result = STATE_CRITICAL; 268 result = STATE_CRITICAL;
269 }
270 } else if (result == STATE_WARNING) {
271 printf("%s (%s)\n", errmsg, display_message);
302 } 272 }
303 273
304 else if (result == STATE_WARNING) 274 exit(result);
305 printf ("%s (%s)\n", errmsg, display_message);
306
307 return result;
308} 275}
309 276
310
311/* process command-line arguments */ 277/* process command-line arguments */
312int 278check_hpjd_config_wrapper process_arguments(int argc, char **argv) {
313process_arguments (int argc, char **argv) 279 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
314{ 280 {"community", required_argument, 0, 'C'},
315 int c; 281 /* {"critical", required_argument,0,'c'}, */
316 282 /* {"warning", required_argument,0,'w'}, */
317 int option = 0; 283 {"port", required_argument, 0, 'p'},
318 static struct option longopts[] = { 284 {"version", no_argument, 0, 'V'},
319 {"hostname", required_argument, 0, 'H'}, 285 {"help", no_argument, 0, 'h'},
320 {"community", required_argument, 0, 'C'}, 286 {0, 0, 0, 0}};
321/* {"critical", required_argument,0,'c'}, */ 287
322/* {"warning", required_argument,0,'w'}, */ 288 check_hpjd_config_wrapper result = {
323 {"port", required_argument,0,'p'}, 289 .errorcode = OK,
324 {"version", no_argument, 0, 'V'}, 290 .config = check_hpjd_config_init(),
325 {"help", no_argument, 0, 'h'},
326 {0, 0, 0, 0}
327 }; 291 };
328 292
329 if (argc < 2) 293 if (argc < 2) {
330 return ERROR; 294 result.errorcode = ERROR;
331 295 return result;
296 }
332 297
333 while (1) { 298 int option = 0;
334 c = getopt_long (argc, argv, "+hVH:C:p:D", longopts, &option); 299 while (true) {
300 int option_index = getopt_long(argc, argv, "+hVH:C:p:D", longopts, &option);
335 301
336 if (c == -1 || c == EOF || c == 1) 302 if (option_index == -1 || option_index == EOF || option_index == 1) {
337 break; 303 break;
304 }
338 305
339 switch (c) { 306 switch (option_index) {
340 case 'H': /* hostname */ 307 case 'H': /* hostname */
341 if (is_host (optarg)) { 308 if (is_host(optarg)) {
342 address = strscpy(address, optarg) ; 309 result.config.address = strscpy(result.config.address, optarg);
343 } 310 } else {
344 else { 311 usage2(_("Invalid hostname/address"), optarg);
345 usage2 (_("Invalid hostname/address"), optarg);
346 } 312 }
347 break; 313 break;
348 case 'C': /* community */ 314 case 'C': /* community */
349 community = strscpy (community, optarg); 315 result.config.community = strscpy(result.config.community, optarg);
350 break; 316 break;
351 case 'p': 317 case 'p':
352 if (!is_intpos(optarg)) 318 if (!is_intpos(optarg)) {
353 usage2 (_("Port must be a positive short integer"), optarg); 319 usage2(_("Port must be a positive short integer"), optarg);
354 else 320 } else {
355 port = atoi(optarg); 321 result.config.port = atoi(optarg);
322 }
356 break; 323 break;
357 case 'D': /* disable paper out check*/ 324 case 'D': /* disable paper out check*/
358 check_paper_out = 0; 325 result.config.check_paper_out = false;
359 break; 326 break;
360 case 'V': /* version */ 327 case 'V': /* version */
361 print_revision (progname, NP_VERSION); 328 print_revision(progname, NP_VERSION);
362 exit (STATE_UNKNOWN); 329 exit(STATE_UNKNOWN);
363 case 'h': /* help */ 330 case 'h': /* help */
364 print_help (); 331 print_help();
365 exit (STATE_UNKNOWN); 332 exit(STATE_UNKNOWN);
366 case '?': /* help */ 333 case '?': /* help */
367 usage5 (); 334 usage5();
368 } 335 }
369 } 336 }
370 337
371 c = optind; 338 int c = optind;
372 if (address == NULL) { 339 if (result.config.address == NULL) {
373 if (is_host (argv[c])) { 340 if (is_host(argv[c])) {
374 address = argv[c++]; 341 result.config.address = argv[c++];
375 } 342 } else {
376 else { 343 usage2(_("Invalid hostname/address"), argv[c]);
377 usage2 (_("Invalid hostname/address"), argv[c]);
378 } 344 }
379 } 345 }
380 346
381 if (community == NULL) { 347 if (result.config.community == NULL) {
382 if (argv[c] != NULL ) 348 if (argv[c] != NULL) {
383 community = argv[c]; 349 result.config.community = argv[c];
384 else 350 } else {
385 community = strdup (DEFAULT_COMMUNITY); 351 result.config.community = strdup(DEFAULT_COMMUNITY);
386 } 352 }
387
388 if (port == 0) {
389 port = atoi(DEFAULT_PORT);
390 } 353 }
391 354
392 return validate_arguments (); 355 return result;
393}
394
395
396int
397validate_arguments (void)
398{
399 return OK;
400} 356}
401 357
358void print_help(void) {
359 print_revision(progname, NP_VERSION);
402 360
403void 361 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
404print_help (void) 362 printf(COPYRIGHT, copyright, email);
405{
406 print_revision (progname, NP_VERSION);
407
408 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
409 printf (COPYRIGHT, copyright, email);
410 363
411 printf ("%s\n", _("This plugin tests the STATUS of an HP printer with a JetDirect card.")); 364 printf("%s\n", _("This plugin tests the STATUS of an HP printer with a JetDirect card."));
412 printf ("%s\n", _("Net-snmp must be installed on the computer running the plugin.")); 365 printf("%s\n", _("Net-snmp must be installed on the computer running the plugin."));
413 366
414 printf ("\n\n"); 367 printf("\n\n");
415 368
416 print_usage (); 369 print_usage();
417 370
418 printf (UT_HELP_VRSN); 371 printf(UT_HELP_VRSN);
419 printf (UT_EXTRA_OPTS); 372 printf(UT_EXTRA_OPTS);
420 373
421 printf (" %s\n", "-C, --community=STRING"); 374 printf(" %s\n", "-C, --community=STRING");
422 printf (" %s", _("The SNMP community name ")); 375 printf(" %s", _("The SNMP community name "));
423 printf (_("(default=%s)"), DEFAULT_COMMUNITY); 376 printf(_("(default=%s)"), DEFAULT_COMMUNITY);
424 printf ("\n"); 377 printf("\n");
425 printf (" %s\n", "-p, --port=STRING"); 378 printf(" %s\n", "-p, --port=STRING");
426 printf (" %s", _("Specify the port to check ")); 379 printf(" %s", _("Specify the port to check "));
427 printf (_("(default=%s)"), DEFAULT_PORT); 380 printf(_("(default=%s)"), DEFAULT_PORT);
428 printf ("\n"); 381 printf("\n");
429 printf (" %s\n", "-D"); 382 printf(" %s\n", "-D");
430 printf (" %s", _("Disable paper check ")); 383 printf(" %s", _("Disable paper check "));
431 384
432 printf (UT_SUPPORT); 385 printf(UT_SUPPORT);
433} 386}
434 387
435 388void print_usage(void) {
436 389 printf("%s\n", _("Usage:"));
437void 390 printf("%s -H host [-C community] [-p port] [-D]\n", progname);
438print_usage (void)
439{
440 printf ("%s\n", _("Usage:"));
441 printf ("%s -H host [-C community] [-p port] [-D]\n", progname);
442} 391}
diff --git a/plugins/check_hpjd.d/config.h b/plugins/check_hpjd.d/config.h
new file mode 100644
index 00000000..e36b7972
--- /dev/null
+++ b/plugins/check_hpjd.d/config.h
@@ -0,0 +1,25 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7#define DEFAULT_PORT "161"
8
9typedef struct {
10 char *address;
11 char *community;
12 unsigned int port;
13 bool check_paper_out;
14
15} check_hpjd_config;
16
17check_hpjd_config check_hpjd_config_init() {
18 check_hpjd_config tmp = {
19 .address = NULL,
20 .community = NULL,
21 .port = (unsigned int)atoi(DEFAULT_PORT),
22 .check_paper_out = true,
23 };
24 return tmp;
25}
diff --git a/plugins/check_http.c b/plugins/check_http.c
index cdf768c9..d264b95d 100644
--- a/plugins/check_http.c
+++ b/plugins/check_http.c
@@ -1,38 +1,38 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_http plugin 3 * Monitoring check_http plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2013 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_http plugin 10 * This file contains the check_http plugin
11* 11 *
12* This plugin tests the HTTP service on the specified host. It can test 12 * This plugin tests the HTTP service on the specified host. It can test
13* normal (http) and secure (https) servers, follow redirects, search for 13 * normal (http) and secure (https) servers, follow redirects, search for
14* strings and regular expressions, check connection times, and report on 14 * strings and regular expressions, check connection times, and report on
15* certificate expiration times. 15 * certificate expiration times.
16* 16 *
17* 17 *
18* This program is free software: you can redistribute it and/or modify 18 * This program is free software: you can redistribute it and/or modify
19* it under the terms of the GNU General Public License as published by 19 * it under the terms of the GNU General Public License as published by
20* the Free Software Foundation, either version 3 of the License, or 20 * the Free Software Foundation, either version 3 of the License, or
21* (at your option) any later version. 21 * (at your option) any later version.
22* 22 *
23* This program is distributed in the hope that it will be useful, 23 * This program is distributed in the hope that it will be useful,
24* but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26* GNU General Public License for more details. 26 * GNU General Public License for more details.
27* 27 *
28* You should have received a copy of the GNU General Public License 28 * You should have received a copy of the GNU General Public License
29* along with this program. If not, see <http://www.gnu.org/licenses/>. 29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30* 30 *
31* 31 *
32*****************************************************************************/ 32 *****************************************************************************/
33 33
34const char *progname = "check_http"; 34const char *progname = "check_http";
35const char *copyright = "1999-2022"; 35const char *copyright = "1999-2024";
36const char *email = "devel@monitoring-plugins.org"; 36const char *email = "devel@monitoring-plugins.org";
37 37
38// Do NOT sort those headers, it will break the build 38// Do NOT sort those headers, it will break the build
@@ -41,7 +41,6 @@ const char *email = "devel@monitoring-plugins.org";
41#include "base64.h" 41#include "base64.h"
42#include "netutils.h" 42#include "netutils.h"
43#include "utils.h" 43#include "utils.h"
44#include "base64.h"
45#include <ctype.h> 44#include <ctype.h>
46 45
47#define STICKY_NONE 0 46#define STICKY_NONE 0
@@ -50,1346 +49,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", "--regex-state=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 3872e341..16fe3d01 100644
--- a/plugins/check_ide_smart.c
+++ b/plugins/check_ide_smart.c
@@ -1,240 +1,211 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ide_smart plugin 3 * Monitoring check_ide_smart plugin
4* ide-smart 1.3 - IDE S.M.A.R.T. checking tool 4 * ide-smart 1.3 - IDE S.M.A.R.T. checking tool
5* 5 *
6* License: GPL 6 * License: GPL
7* Copyright (C) 1998-1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org> 7 * Copyright (C) 1998-1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org>
8* 1998 Gadi Oxman <gadio@netvision.net.il> 8 * 1998 Gadi Oxman <gadio@netvision.net.il>
9* Copyright (c) 2000 Robert Dale <rdale@digital-mission.com> 9 * Copyright (c) 2000 Robert Dale <rdale@digital-mission.com>
10* Copyright (c) 2000-2007 Monitoring Plugins Development Team 10 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
11* 11 *
12* Description: 12 * Description:
13* 13 *
14* This file contains the check_ide_smart plugin 14 * This file contains the check_ide_smart plugin
15* 15 *
16* This plugin checks a local hard drive with the (Linux specific) SMART 16 * This plugin checks a local hard drive with the (Linux specific) SMART
17* interface 17 * interface
18* 18 *
19* 19 *
20* This program is free software: you can redistribute it and/or modify 20 * This program is free software: you can redistribute it and/or modify
21* it under the terms of the GNU General Public License as published by 21 * it under the terms of the GNU General Public License as published by
22* the Free Software Foundation, either version 3 of the License, or 22 * the Free Software Foundation, either version 3 of the License, or
23* (at your option) any later version. 23 * (at your option) any later version.
24* 24 *
25* This program is distributed in the hope that it will be useful, 25 * This program is distributed in the hope that it will be useful,
26* but WITHOUT ANY WARRANTY; without even the implied warranty of 26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28* GNU General Public License for more details. 28 * GNU General Public License for more details.
29* 29 *
30* You should have received a copy of the GNU General Public License 30 * You should have received a copy of the GNU General Public License
31* along with this program. If not, see <http://www.gnu.org/licenses/>. 31 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32* 32 *
33* 33 *
34*****************************************************************************/ 34 *****************************************************************************/
35 35
36const char *progname = "check_ide_smart"; 36const char *progname = "check_ide_smart";
37const char *copyright = "1998-2007"; 37const char *copyright = "1998-2024";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "common.h" 40#include "common.h"
41#include "utils.h" 41#include "utils.h"
42 42
43void print_help (void); 43static void print_help(void);
44void print_usage (void); 44void print_usage(void);
45 45
46#include <sys/stat.h> 46#include <sys/stat.h>
47#include <sys/ioctl.h> 47#include <sys/ioctl.h>
48#include <fcntl.h> 48#include <fcntl.h>
49#ifdef __linux__ 49#ifdef __linux__
50#include <linux/hdreg.h> 50# include <linux/hdreg.h>
51#include <linux/types.h> 51# include <linux/types.h>
52 52
53#define OPEN_MODE O_RDONLY 53# define OPEN_MODE O_RDONLY
54#endif /* __linux__ */ 54#endif /* __linux__ */
55#ifdef __NetBSD__ 55#ifdef __NetBSD__
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 */ 59# include <sys/scsiio.h>
60#include <sys/scsiio.h> 60# include <sys/ataio.h>
61#include <sys/ataio.h> 61# include <dev/ata/atareg.h>
62#include <dev/ata/atareg.h> 62# include <dev/ic/wdcreg.h>
63#include <dev/ic/wdcreg.h> 63
64 64# define SMART_ENABLE WDSM_ENABLE_OPS
65#define SMART_ENABLE WDSM_ENABLE_OPS 65# define SMART_DISABLE WDSM_DISABLE_OPS
66#define SMART_DISABLE WDSM_DISABLE_OPS 66# define SMART_IMMEDIATE_OFFLINE WDSM_EXEC_OFFL_IMM
67#define SMART_IMMEDIATE_OFFLINE WDSM_EXEC_OFFL_IMM 67# define SMART_AUTO_OFFLINE 0xdb /* undefined in NetBSD headers */
68#define SMART_AUTO_OFFLINE 0xdb /* undefined in NetBSD headers */ 68
69 69# define OPEN_MODE O_RDWR
70#define OPEN_MODE O_RDWR
71#endif /* __NetBSD__ */ 70#endif /* __NetBSD__ */
72#include <errno.h> 71#include <errno.h>
73 72
74#define NR_ATTRIBUTES 30 73#define NR_ATTRIBUTES 30
75 74
76#define PREFAILURE 2 75#define PREFAILURE 2
77#define ADVISORY 1 76#define ADVISORY 1
78#define OPERATIONAL 0 77#define OPERATIONAL 0
79#define UNKNOWN -1 78#define UNKNOWN -1
80 79
81typedef struct threshold_s 80typedef struct threshold_s {
82{ 81 uint8_t id;
83 __u8 id; 82 uint8_t threshold;
84 __u8 threshold; 83 uint8_t reserved[10];
85 __u8 reserved[10]; 84} __attribute__((packed)) threshold_t;
86}
87__attribute__ ((packed)) threshold_t;
88 85
89typedef struct thresholds_s 86typedef struct thresholds_s {
90{ 87 uint16_t revision;
91 __u16 revision;
92 threshold_t thresholds[NR_ATTRIBUTES]; 88 threshold_t thresholds[NR_ATTRIBUTES];
93 __u8 reserved[18]; 89 uint8_t reserved[18];
94 __u8 vendor[131]; 90 uint8_t vendor[131];
95 __u8 checksum; 91 uint8_t checksum;
96} 92} __attribute__((packed)) thresholds_t;
97__attribute__ ((packed)) thresholds_t; 93
98 94typedef struct value_s {
99typedef struct value_s 95 uint8_t id;
100{ 96 uint16_t status;
101 __u8 id; 97 uint8_t value;
102 __u16 status; 98 uint8_t vendor[8];
103 __u8 value; 99} __attribute__((packed)) value_t;
104 __u8 vendor[8]; 100
105} 101typedef struct values_s {
106__attribute__ ((packed)) value_t; 102 uint16_t revision;
107
108typedef struct values_s
109{
110 __u16 revision;
111 value_t values[NR_ATTRIBUTES]; 103 value_t values[NR_ATTRIBUTES];
112 __u8 offline_status; 104 uint8_t offline_status;
113 __u8 vendor1; 105 uint8_t vendor1;
114 __u16 offline_timeout; 106 uint16_t offline_timeout;
115 __u8 vendor2; 107 uint8_t vendor2;
116 __u8 offline_capability; 108 uint8_t offline_capability;
117 __u16 smart_capability; 109 uint16_t smart_capability;
118 __u8 reserved[16]; 110 uint8_t reserved[16];
119 __u8 vendor[125]; 111 uint8_t vendor[125];
120 __u8 checksum; 112 uint8_t checksum;
121} 113} __attribute__((packed)) values_t;
122__attribute__ ((packed)) values_t; 114
123 115static struct {
124struct 116 uint8_t value;
125{
126 __u8 value;
127 char *text; 117 char *text;
128} 118} offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
129 119
130offline_status_text[] = 120static struct {
131 { 121 uint8_t value;
132 {0x00, "NeverStarted"},
133 {0x02, "Completed"},
134 {0x04, "Suspended"},
135 {0x05, "Aborted"},
136 {0x06, "Failed"},
137 {0, 0}
138 };
139
140struct
141{
142 __u8 value;
143 char *text; 122 char *text;
144} 123} smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"},
145 124 {SMART_DISABLE, "SMART_DISABLE"},
146smart_command[] = 125 {SMART_IMMEDIATE_OFFLINE, "SMART_IMMEDIATE_OFFLINE"},
147 { 126 {SMART_AUTO_OFFLINE, "SMART_AUTO_OFFLINE"}};
148 {SMART_ENABLE, "SMART_ENABLE"}, 127
149 {SMART_DISABLE, "SMART_DISABLE"}, 128/* Index to smart_command table, keep in order */
150 {SMART_IMMEDIATE_OFFLINE, "SMART_IMMEDIATE_OFFLINE"}, 129enum SmartCommand {
151 {SMART_AUTO_OFFLINE, "SMART_AUTO_OFFLINE"} 130 SMART_CMD_ENABLE,
152 }; 131 SMART_CMD_DISABLE,
153 132 SMART_CMD_IMMEDIATE_OFFLINE,
154 133 SMART_CMD_AUTO_OFFLINE
155/* Index to smart_command table, keep in order */ 134};
156enum SmartCommand 135
157 { SMART_CMD_ENABLE, 136static char *get_offline_text(int);
158 SMART_CMD_DISABLE, 137static int smart_read_values(int, values_t *);
159 SMART_CMD_IMMEDIATE_OFFLINE, 138static int nagios(values_t *, thresholds_t *);
160 SMART_CMD_AUTO_OFFLINE 139static void print_value(value_t *, threshold_t *);
161 }; 140static void print_values(values_t *, thresholds_t *);
162 141static int smart_cmd_simple(int, enum SmartCommand, uint8_t, bool);
163char *get_offline_text (int); 142static int smart_read_thresholds(int, thresholds_t *);
164int smart_read_values (int, values_t *); 143static bool verbose = false;
165int nagios (values_t *, thresholds_t *); 144
166void print_value (value_t *, threshold_t *); 145int main(int argc, char *argv[]) {
167void print_values (values_t *, thresholds_t *);
168int smart_cmd_simple (int, enum SmartCommand, __u8, bool);
169int smart_read_thresholds (int, thresholds_t *);
170bool verbose = false;
171
172int
173main (int argc, char *argv[])
174{
175 char *device = NULL; 146 char *device = NULL;
176 int o, longindex; 147 int o;
148 int longindex;
177 int retval = 0; 149 int retval = 0;
178 150
179 thresholds_t thresholds; 151 thresholds_t thresholds;
180 values_t values; 152 values_t values;
181 int fd; 153 int fd;
182 154
183 static struct option longopts[] = { 155 static struct option longopts[] = {{"device", required_argument, 0, 'd'},
184 {"device", required_argument, 0, 'd'}, 156 {"immediate", no_argument, 0, 'i'},
185 {"immediate", no_argument, 0, 'i'}, 157 {"quiet-check", no_argument, 0, 'q'},
186 {"quiet-check", no_argument, 0, 'q'}, 158 {"auto-on", no_argument, 0, '1'},
187 {"auto-on", no_argument, 0, '1'}, 159 {"auto-off", no_argument, 0, '0'},
188 {"auto-off", no_argument, 0, '0'}, 160 {"nagios", no_argument, 0, 'n'}, /* DEPRECATED, but we still accept it */
189 {"nagios", no_argument, 0, 'n'}, /* DEPRECATED, but we still accept it */ 161 {"help", no_argument, 0, 'h'},
190 {"help", no_argument, 0, 'h'}, 162 {"version", no_argument, 0, 'V'},
191 {"version", no_argument, 0, 'V'}, 163 {0, 0, 0, 0}};
192 {0, 0, 0, 0}
193 };
194 164
195 /* Parse extra opts if any */ 165 /* Parse extra opts if any */
196 argv=np_extra_opts (&argc, argv, progname); 166 argv = np_extra_opts(&argc, argv, progname);
197 167
198 setlocale (LC_ALL, ""); 168 setlocale(LC_ALL, "");
199 bindtextdomain (PACKAGE, LOCALEDIR); 169 bindtextdomain(PACKAGE, LOCALEDIR);
200 textdomain (PACKAGE); 170 textdomain(PACKAGE);
201 171
202 while (true) { 172 while (true) {
203
204 o = getopt_long (argc, argv, "+d:iq10nhVv", longopts, &longindex);
205 173
206 if (o == -1 || o == EOF || o == 1) 174 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex);
175
176 if (o == -1 || o == EOF || o == 1) {
207 break; 177 break;
178 }
208 179
209 switch (o) { 180 switch (o) {
210 case 'd': 181 case 'd':
211 device = optarg; 182 device = optarg;
212 break; 183 break;
213 case 'q': 184 case 'q':
214 fprintf (stderr, "%s\n", _("DEPRECATION WARNING: the -q switch (quiet output) is no longer \"quiet\".")); 185 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -q switch (quiet output) is no longer \"quiet\"."));
215 fprintf (stderr, "%s\n", _("Nagios-compatible output is now always returned.")); 186 fprintf(stderr, "%s\n", _("Nagios-compatible output is now always returned."));
216 break; 187 break;
217 case 'i': 188 case 'i':
218 case '1': 189 case '1':
219 case '0': 190 case '0':
220 printf ("%s\n", _("SMART commands are broken and have been disabled (See Notes in --help).")); 191 printf("%s\n", _("SMART commands are broken and have been disabled (See Notes in --help)."));
221 return STATE_CRITICAL; 192 return STATE_CRITICAL;
222 break; 193 break;
223 case 'n': 194 case 'n':
224 fprintf (stderr, "%s\n", _("DEPRECATION WARNING: the -n switch (Nagios-compatible output) is now the")); 195 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -n switch (Nagios-compatible output) is now the"));
225 fprintf (stderr, "%s\n", _("default and will be removed from future releases.")); 196 fprintf(stderr, "%s\n", _("default and will be removed from future releases."));
226 break; 197 break;
227 case 'v': /* verbose */ 198 case 'v': /* verbose */
228 verbose = true; 199 verbose = true;
229 break; 200 break;
230 case 'h': 201 case 'h':
231 print_help (); 202 print_help();
232 return STATE_UNKNOWN; 203 return STATE_UNKNOWN;
233 case 'V': 204 case 'V':
234 print_revision (progname, NP_VERSION); 205 print_revision(progname, NP_VERSION);
235 return STATE_UNKNOWN; 206 return STATE_UNKNOWN;
236 default: 207 default:
237 usage5 (); 208 usage5();
238 } 209 }
239 } 210 }
240 211
@@ -243,36 +214,34 @@ main (int argc, char *argv[])
243 } 214 }
244 215
245 if (!device) { 216 if (!device) {
246 print_help (); 217 print_help();
247 return STATE_UNKNOWN; 218 return STATE_UNKNOWN;
248 } 219 }
249 220
250 fd = open (device, OPEN_MODE); 221 fd = open(device, OPEN_MODE);
251 222
252 if (fd < 0) { 223 if (fd < 0) {
253 printf (_("CRITICAL - Couldn't open device %s: %s\n"), device, strerror (errno)); 224 printf(_("CRITICAL - Couldn't open device %s: %s\n"), device, strerror(errno));
254 return STATE_CRITICAL; 225 return STATE_CRITICAL;
255 } 226 }
256 227
257 if (smart_cmd_simple (fd, SMART_CMD_ENABLE, 0, false)) { 228 if (smart_cmd_simple(fd, SMART_CMD_ENABLE, 0, false)) {
258 printf (_("CRITICAL - SMART_CMD_ENABLE\n")); 229 printf(_("CRITICAL - SMART_CMD_ENABLE\n"));
259 return STATE_CRITICAL; 230 return STATE_CRITICAL;
260 } 231 }
261 232
262 smart_read_values (fd, &values); 233 smart_read_values(fd, &values);
263 smart_read_thresholds (fd, &thresholds); 234 smart_read_thresholds(fd, &thresholds);
264 retval = nagios (&values, &thresholds); 235 retval = nagios(&values, &thresholds);
265 if (verbose) print_values (&values, &thresholds); 236 if (verbose) {
237 print_values(&values, &thresholds);
238 }
266 239
267 close (fd); 240 close(fd);
268 return retval; 241 return retval;
269} 242}
270 243
271 244char *get_offline_text(int status) {
272
273char *
274get_offline_text (int status)
275{
276 int i; 245 int i;
277 for (i = 0; offline_status_text[i].text; i++) { 246 for (i = 0; offline_status_text[i].text; i++) {
278 if (offline_status_text[i].value == status) { 247 if (offline_status_text[i].value == status) {
@@ -282,24 +251,20 @@ get_offline_text (int status)
282 return "UNKNOWN"; 251 return "UNKNOWN";
283} 252}
284 253
285 254int smart_read_values(int fd, values_t *values) {
286
287int
288smart_read_values (int fd, values_t * values)
289{
290#ifdef __linux__ 255#ifdef __linux__
291 int e; 256 int e;
292 __u8 args[4 + 512]; 257 uint8_t args[4 + 512];
293 args[0] = WIN_SMART; 258 args[0] = WIN_SMART;
294 args[1] = 0; 259 args[1] = 0;
295 args[2] = SMART_READ_VALUES; 260 args[2] = SMART_READ_VALUES;
296 args[3] = 1; 261 args[3] = 1;
297 if (ioctl (fd, HDIO_DRIVE_CMD, &args)) { 262 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
298 e = errno; 263 e = errno;
299 printf (_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror (errno)); 264 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno));
300 return e; 265 return e;
301 } 266 }
302 memcpy (values, args + 4, 512); 267 memcpy(values, args + 4, 512);
303#endif /* __linux__ */ 268#endif /* __linux__ */
304#ifdef __NetBSD__ 269#ifdef __NetBSD__
305 struct atareq req; 270 struct atareq req;
@@ -317,13 +282,14 @@ smart_read_values (int fd, values_t * values)
317 req.cylinder = WDSMART_CYL; 282 req.cylinder = WDSMART_CYL;
318 283
319 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
320 if (req.retsts != ATACMD_OK) 285 if (req.retsts != ATACMD_OK) {
321 errno = ENODEV; 286 errno = ENODEV;
287 }
322 } 288 }
323 289
324 if (errno != 0) { 290 if (errno != 0) {
325 int e = errno; 291 int e = errno;
326 printf (_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror (errno)); 292 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno));
327 return e; 293 return e;
328 } 294 }
329 295
@@ -332,13 +298,9 @@ smart_read_values (int fd, values_t * values)
332 return 0; 298 return 0;
333} 299}
334 300
335 301int nagios(values_t *p, thresholds_t *t) {
336 302 value_t *value = p->values;
337int 303 threshold_t *threshold = t->thresholds;
338nagios (values_t * p, thresholds_t * t)
339{
340 value_t * value = p->values;
341 threshold_t * threshold = t->thresholds;
342 int status = OPERATIONAL; 304 int status = OPERATIONAL;
343 int prefailure = 0; 305 int prefailure = 0;
344 int advisory = 0; 306 int advisory = 0;
@@ -353,13 +315,11 @@ nagios (values_t * p, thresholds_t * t)
353 if (value->status & 1) { 315 if (value->status & 1) {
354 status = PREFAILURE; 316 status = PREFAILURE;
355 ++prefailure; 317 ++prefailure;
356 } 318 } else {
357 else {
358 status = ADVISORY; 319 status = ADVISORY;
359 ++advisory; 320 ++advisory;
360 } 321 }
361 } 322 } else {
362 else {
363 ++passed; 323 ++passed;
364 } 324 }
365 ++total; 325 ++total;
@@ -369,96 +329,66 @@ nagios (values_t * p, thresholds_t * t)
369 } 329 }
370 switch (status) { 330 switch (status) {
371 case PREFAILURE: 331 case PREFAILURE:
372 printf (_("CRITICAL - %d Harddrive PreFailure%cDetected! %d/%d tests failed.\n"), 332 printf(_("CRITICAL - %d Harddrive PreFailure%cDetected! %d/%d tests failed.\n"), prefailure, prefailure > 1 ? 's' : ' ', failed,
373 prefailure, 333 total);
374 prefailure > 1 ? 's' : ' ', 334 status = STATE_CRITICAL;
375 failed,
376 total);
377 status=STATE_CRITICAL;
378 break; 335 break;
379 case ADVISORY: 336 case ADVISORY:
380 printf (_("WARNING - %d Harddrive Advisor%s Detected. %d/%d tests failed.\n"), 337 printf(_("WARNING - %d Harddrive Advisor%s Detected. %d/%d tests failed.\n"), advisory, advisory > 1 ? "ies" : "y", failed, total);
381 advisory, 338 status = STATE_WARNING;
382 advisory > 1 ? "ies" : "y",
383 failed,
384 total);
385 status=STATE_WARNING;
386 break; 339 break;
387 case OPERATIONAL: 340 case OPERATIONAL:
388 printf (_("OK - Operational (%d/%d tests passed)\n"), passed, total); 341 printf(_("OK - Operational (%d/%d tests passed)\n"), passed, total);
389 status=STATE_OK; 342 status = STATE_OK;
390 break; 343 break;
391 default: 344 default:
392 printf (_("ERROR - Status '%d' unknown. %d/%d tests passed\n"), status, 345 printf(_("ERROR - Status '%d' unknown. %d/%d tests passed\n"), status, passed, total);
393 passed, total);
394 status = STATE_UNKNOWN; 346 status = STATE_UNKNOWN;
395 break; 347 break;
396 } 348 }
397 return status; 349 return status;
398} 350}
399 351
400 352void print_value(value_t *p, threshold_t *t) {
401 353 printf("Id=%3d, Status=%2d {%s , %s}, Value=%3d, Threshold=%3d, %s\n", p->id, p->status, p->status & 1 ? "PreFailure" : "Advisory ",
402void 354 p->status & 2 ? "OnLine " : "OffLine", p->value, t->threshold, p->value >= t->threshold ? "Passed" : "Failed");
403print_value (value_t * p, threshold_t * t)
404{
405 printf ("Id=%3d, Status=%2d {%s , %s}, Value=%3d, Threshold=%3d, %s\n",
406 p->id, p->status, p->status & 1 ? "PreFailure" : "Advisory ",
407 p->status & 2 ? "OnLine " : "OffLine", p->value, t->threshold,
408 p->value >= t->threshold ? "Passed" : "Failed");
409} 355}
410 356
411 357void print_values(values_t *p, thresholds_t *t) {
412 358 value_t *value = p->values;
413void 359 threshold_t *threshold = t->thresholds;
414print_values (values_t * p, thresholds_t * t)
415{
416 value_t * value = p->values;
417 threshold_t * threshold = t->thresholds;
418 int i; 360 int i;
419 for (i = 0; i < NR_ATTRIBUTES; i++) { 361 for (i = 0; i < NR_ATTRIBUTES; i++) {
420 if (value->id && threshold->id && value->id == threshold->id) { 362 if (value->id && threshold->id && value->id == threshold->id) {
421 print_value (value++, threshold++); 363 print_value(value++, threshold++);
422 } 364 }
423 } 365 }
424 printf 366 printf(_("OffLineStatus=%d {%s}, AutoOffLine=%s, OffLineTimeout=%d minutes\n"), p->offline_status,
425 (_("OffLineStatus=%d {%s}, AutoOffLine=%s, OffLineTimeout=%d minutes\n"), 367 get_offline_text(p->offline_status & 0x7f), (p->offline_status & 0x80 ? "Yes" : "No"), p->offline_timeout / 60);
426 p->offline_status, 368 printf(_("OffLineCapability=%d {%s %s %s}\n"), p->offline_capability, p->offline_capability & 1 ? "Immediate" : "",
427 get_offline_text (p->offline_status & 0x7f), 369 p->offline_capability & 2 ? "Auto" : "", p->offline_capability & 4 ? "AbortOnCmd" : "SuspendOnCmd");
428 (p->offline_status & 0x80 ? "Yes" : "No"), 370 printf(_("SmartRevision=%d, CheckSum=%d, SmartCapability=%d {%s %s}\n"), p->revision, p->checksum, p->smart_capability,
429 p->offline_timeout / 60); 371 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : "");
430 printf
431 (_("OffLineCapability=%d {%s %s %s}\n"),
432 p->offline_capability,
433 p->offline_capability & 1 ? "Immediate" : "",
434 p->offline_capability & 2 ? "Auto" : "",
435 p->offline_capability & 4 ? "AbortOnCmd" : "SuspendOnCmd");
436 printf
437 (_("SmartRevision=%d, CheckSum=%d, SmartCapability=%d {%s %s}\n"),
438 p->revision,
439 p->checksum,
440 p->smart_capability,
441 p->smart_capability & 1 ? "SaveOnStandBy" : "",
442 p->smart_capability & 2 ? "AutoSave" : "");
443} 372}
444 373
445 374int smart_cmd_simple(int fd, enum SmartCommand command, uint8_t val0, bool show_error) {
446int smart_cmd_simple (int fd, enum SmartCommand command, __u8 val0, bool show_error) {
447 int e = STATE_UNKNOWN; 375 int e = STATE_UNKNOWN;
448#ifdef __linux__ 376#ifdef __linux__
449 __u8 args[4]; 377 uint8_t args[4];
450 args[0] = WIN_SMART; 378 args[0] = WIN_SMART;
451 args[1] = val0; 379 args[1] = val0;
452 args[2] = smart_command[command].value; 380 args[2] = smart_command[command].value;
453 args[3] = 0; 381 args[3] = 0;
454 if (ioctl (fd, HDIO_DRIVE_CMD, &args)) { 382 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
455 e = STATE_CRITICAL; 383 e = STATE_CRITICAL;
456 if (show_error) 384 if (show_error) {
457 printf (_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror (errno)); 385 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
386 }
458 } else { 387 } else {
459 e = STATE_OK; 388 e = STATE_OK;
460 if (show_error) 389 if (show_error) {
461 printf (_("OK - Command sent (%s)\n"), smart_command[command].text); 390 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
391 }
462 } 392 }
463 393
464#endif /* __linux__ */ 394#endif /* __linux__ */
@@ -474,44 +404,44 @@ int smart_cmd_simple (int fd, enum SmartCommand command, __u8 val0, bool show_er
474 req.sec_count = val0; 404 req.sec_count = val0;
475 405
476 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 406 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
477 if (req.retsts != ATACMD_OK) 407 if (req.retsts != ATACMD_OK) {
478 errno = ENODEV; 408 errno = ENODEV;
479 if (req.cylinder != WDSMART_CYL) 409 }
410 if (req.cylinder != WDSMART_CYL) {
480 errno = ENODEV; 411 errno = ENODEV;
412 }
481 } 413 }
482 414
483 if (errno != 0) { 415 if (errno != 0) {
484 e = STATE_CRITICAL; 416 e = STATE_CRITICAL;
485 if (show_error) 417 if (show_error) {
486 printf (_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror (errno)); 418 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
419 }
487 } else { 420 } else {
488 e = STATE_OK; 421 e = STATE_OK;
489 if (show_error) 422 if (show_error) {
490 printf (_("OK - Command sent (%s)\n"), smart_command[command].text); 423 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
424 }
491 } 425 }
492 426
493#endif /* __NetBSD__ */ 427#endif /* __NetBSD__ */
494 return e; 428 return e;
495} 429}
496 430
497 431int smart_read_thresholds(int fd, thresholds_t *thresholds) {
498
499int
500smart_read_thresholds (int fd, thresholds_t * thresholds)
501{
502#ifdef __linux__ 432#ifdef __linux__
503 int e; 433 int e;
504 __u8 args[4 + 512]; 434 uint8_t args[4 + 512];
505 args[0] = WIN_SMART; 435 args[0] = WIN_SMART;
506 args[1] = 0; 436 args[1] = 0;
507 args[2] = SMART_READ_THRESHOLDS; 437 args[2] = SMART_READ_THRESHOLDS;
508 args[3] = 1; 438 args[3] = 1;
509 if (ioctl (fd, HDIO_DRIVE_CMD, &args)) { 439 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
510 e = errno; 440 e = errno;
511 printf (_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror (errno)); 441 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno));
512 return e; 442 return e;
513 } 443 }
514 memcpy (thresholds, args + 4, 512); 444 memcpy(thresholds, args + 4, 512);
515#endif /* __linux__ */ 445#endif /* __linux__ */
516#ifdef __NetBSD__ 446#ifdef __NetBSD__
517 struct atareq req; 447 struct atareq req;
@@ -529,13 +459,14 @@ smart_read_thresholds (int fd, thresholds_t * thresholds)
529 req.cylinder = WDSMART_CYL; 459 req.cylinder = WDSMART_CYL;
530 460
531 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 461 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
532 if (req.retsts != ATACMD_OK) 462 if (req.retsts != ATACMD_OK) {
533 errno = ENODEV; 463 errno = ENODEV;
464 }
534 } 465 }
535 466
536 if (errno != 0) { 467 if (errno != 0) {
537 int e = errno; 468 int e = errno;
538 printf (_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror (errno)); 469 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno));
539 return e; 470 return e;
540 } 471 }
541 472
@@ -544,45 +475,43 @@ smart_read_thresholds (int fd, thresholds_t * thresholds)
544 return 0; 475 return 0;
545} 476}
546 477
478void print_help(void) {
479 print_revision(progname, NP_VERSION);
547 480
548void 481 printf("(C) 1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org>\n");
549print_help (void) 482 printf("Plugin implementation - 1999 Robert Dale <rdale@digital-mission.com>\n");
550{ 483 printf(COPYRIGHT, copyright, email);
551 print_revision (progname, NP_VERSION);
552
553 printf ("(C) 1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org>\n");
554 printf ("Plugin implementation - 1999 Robert Dale <rdale@digital-mission.com>\n");
555 printf (COPYRIGHT, copyright, email);
556 484
557 printf (_("This plugin checks a local hard drive with the (Linux specific) SMART interface [http://smartlinux.sourceforge.net/smart/index.php].")); 485 printf(_("This plugin checks a local hard drive with the (Linux specific) SMART interface "
486 "[http://smartlinux.sourceforge.net/smart/index.php]."));
558 487
559 printf ("\n\n"); 488 printf("\n\n");
560 489
561 print_usage (); 490 print_usage();
562 491
563 printf (UT_HELP_VRSN); 492 printf(UT_HELP_VRSN);
564 printf (UT_EXTRA_OPTS); 493 printf(UT_EXTRA_OPTS);
565 494
566 printf (" %s\n", "-d, --device=DEVICE"); 495 printf(" %s\n", "-d, --device=DEVICE");
567 printf (" %s\n", _("Select device DEVICE")); 496 printf(" %s\n", _("Select device DEVICE"));
568 printf (" %s\n", _("Note: if the device is specified without this option, any further option will")); 497 printf(" %s\n", _("Note: if the device is specified without this option, any further option will"));
569 printf (" %s\n", _("be ignored.")); 498 printf(" %s\n", _("be ignored."));
570 499
571 printf (UT_VERBOSE); 500 printf(UT_VERBOSE);
572 501
573 printf ("\n"); 502 printf("\n");
574 printf ("%s\n", _("Notes:")); 503 printf("%s\n", _("Notes:"));
575 printf (" %s\n", _("The SMART command modes (-i/--immediate, -0/--auto-off and -1/--auto-on) were")); 504 printf(" %s\n", _("The SMART command modes (-i/--immediate, -0/--auto-off and -1/--auto-on) were"));
576 printf (" %s\n", _("broken in an underhand manner and have been disabled. You can use smartctl")); 505 printf(" %s\n", _("broken in an underhand manner and have been disabled. You can use smartctl"));
577 printf (" %s\n", _("instead:")); 506 printf(" %s\n", _("instead:"));
578 printf (" %s\n", _("-0/--auto-off: use \"smartctl --offlineauto=off\"")); 507 printf(" %s\n", _("-0/--auto-off: use \"smartctl --offlineauto=off\""));
579 printf (" %s\n", _("-1/--auto-on: use \"smartctl --offlineauto=on\"")); 508 printf(" %s\n", _("-1/--auto-on: use \"smartctl --offlineauto=on\""));
580 printf (" %s\n", _("-i/--immediate: use \"smartctl --test=offline\"")); 509 printf(" %s\n", _("-i/--immediate: use \"smartctl --test=offline\""));
581 510
582 printf (UT_SUPPORT); 511 printf(UT_SUPPORT);
583} 512}
584 513
585 /* todo : add to the long nanual as example 514/* todo : add to the long nanual as example
586 * 515 *
587 * Run with: check_ide-smart --nagios [-d] <DRIVE> 516 * Run with: check_ide-smart --nagios [-d] <DRIVE>
588 * Where DRIVE is an IDE drive, ie. /dev/hda, /dev/hdb, /dev/hdc 517 * Where DRIVE is an IDE drive, ie. /dev/hda, /dev/hdb, /dev/hdc
@@ -593,10 +522,7 @@ print_help (void)
593 * - Returns -1 not too often 522 * - Returns -1 not too often
594 */ 523 */
595 524
596 525void print_usage(void) {
597void 526 printf("%s\n", _("Usage:"));
598print_usage (void) 527 printf("%s [-d <device>] [-v]", progname);
599{
600 printf ("%s\n", _("Usage:"));
601 printf ("%s [-d <device>] [-v]", progname);
602} 528}
diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c
index 868ffc1e..77a33304 100644
--- a/plugins/check_ldap.c
+++ b/plugins/check_ldap.c
@@ -1,242 +1,216 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ldap plugin 3 * Monitoring check_ldap plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_ldap plugin 10 * This file contains the check_ldap plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 13 * This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14 * it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 23 * You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29/* progname may be check_ldaps */ 29/* progname may be check_ldaps */
30char *progname = "check_ldap"; 30char *progname = "check_ldap";
31const char *copyright = "2000-2008"; 31const char *copyright = "2000-2024";
32const char *email = "devel@monitoring-plugins.org"; 32const char *email = "devel@monitoring-plugins.org";
33 33
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
50int process_arguments (int, char **); 48typedef struct {
51int validate_arguments (void); 49 int errorcode;
52void 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*/);
55char ld_defattr[] = "(objectclass=*)"; 53static check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper /*config_wrapper*/);
56char *ld_attr = ld_defattr;
57char *ld_host = NULL;
58char *ld_base = NULL;
59char *ld_passwd = NULL;
60char *ld_binddn = NULL;
61int ld_port = -1;
62#ifdef HAVE_LDAP_SET_OPTION
63int ld_protocol = DEFAULT_PROTOCOL;
64#endif
65#ifndef LDAP_OPT_SUCCESS
66# define LDAP_OPT_SUCCESS LDAP_SUCCESS
67#endif
68double warn_time = UNDEFINED;
69double crit_time = UNDEFINED;
70thresholds *entries_thresholds = NULL;
71struct timeval tv;
72char* warn_entries = NULL;
73char* crit_entries = NULL;
74bool starttls = false;
75bool ssl_on_connect = false;
76bool verbose = false;
77
78/* for ldap tls */
79
80char *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 826b77e9..4a17049a 100644
--- a/plugins/check_mrtg.c
+++ b/plugins/check_mrtg.c
@@ -1,385 +1,387 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_mrtg plugin 3 * Monitoring check_mrtg plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_mrtg plugin 10 * This file contains the check_mrtg plugin
11* 11 *
12* This plugin will check either the average or maximum value of one of the 12 * This plugin will check either the average or maximum value of one of the
13* two variables recorded in an MRTG log file. 13 * two variables recorded in an MRTG log file.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_mrtg"; 32const char *progname = "check_mrtg";
33const char *copyright = "1999-2007"; 33const char *copyright = "1999-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "utils.h" 37#include "utils.h"
38#include "check_mrtg.d/config.h"
38 39
39int process_arguments (int, char **); 40typedef struct {
40int validate_arguments (void); 41 int errorcode;
41void print_help (void); 42 check_mrtg_config config;
42void print_usage (void); 43} check_mrtg_config_wrapper;
43 44static check_mrtg_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
44char *log_file = NULL; 45static check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper /*config_wrapper*/);
45int expire_minutes = 0; 46
46bool use_average = true; 47static void print_help(void);
47int variable_number = -1; 48void print_usage(void);
48unsigned long value_warning_threshold = 0L;
49unsigned long value_critical_threshold = 0L;
50char *label;
51char *units;
52
53int
54main (int argc, char **argv)
55{
56 int result = STATE_OK;
57 FILE *fp;
58 int line;
59 char input_buffer[MAX_INPUT_BUFFER];
60 char *temp_buffer;
61 time_t current_time;
62 time_t timestamp = 0L;
63 unsigned long average_value_rate = 0L;
64 unsigned long maximum_value_rate = 0L;
65 unsigned long rate = 0L;
66 49
67 setlocale (LC_ALL, ""); 50int main(int argc, char **argv) {
68 bindtextdomain (PACKAGE, LOCALEDIR); 51 setlocale(LC_ALL, "");
69 textdomain (PACKAGE); 52 bindtextdomain(PACKAGE, LOCALEDIR);
53 textdomain(PACKAGE);
70 54
71 /* Parse extra opts if any */ 55 /* Parse extra opts if any */
72 argv=np_extra_opts (&argc, argv, progname); 56 argv = np_extra_opts(&argc, argv, progname);
73 57
74 if (process_arguments (argc, argv) == ERROR) 58 check_mrtg_config_wrapper tmp_config = process_arguments(argc, argv);
75 usage4 (_("Could not parse arguments\n")); 59 if (tmp_config.errorcode == ERROR) {
60 usage4(_("Could not parse arguments\n"));
61 }
62
63 const check_mrtg_config config = tmp_config.config;
76 64
77 /* open the MRTG log file for reading */ 65 /* open the MRTG log file for reading */
78 fp = fopen (log_file, "r"); 66 FILE *mtrg_log_file = fopen(config.log_file, "r");
79 if (fp == NULL) { 67 if (mtrg_log_file == NULL) {
80 printf (_("Unable to open MRTG log file\n")); 68 printf(_("Unable to open MRTG log file\n"));
81 return STATE_UNKNOWN; 69 return STATE_UNKNOWN;
82 } 70 }
83 71
84 line = 0; 72 time_t timestamp = 0;
85 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) { 73 unsigned long average_value_rate = 0;
86 74 unsigned long maximum_value_rate = 0;
75 char input_buffer[MAX_INPUT_BUFFER];
76 int line = 0;
77 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mtrg_log_file)) {
87 line++; 78 line++;
88 79
89 /* skip the first line of the log file */ 80 /* skip the first line of the log file */
90 if (line == 1) 81 if (line == 1) {
91 continue; 82 continue;
83 }
92 84
93 /* 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 */
94 if (line > 2) 86 if (line > 2) {
95 break; 87 break;
88 }
96 89
97 /* grab the timestamp */ 90 /* grab the timestamp */
98 temp_buffer = strtok (input_buffer, " "); 91 char *temp_buffer = strtok(input_buffer, " ");
99 timestamp = strtoul (temp_buffer, NULL, 10); 92 timestamp = strtoul(temp_buffer, NULL, 10);
100 93
101 /* grab the average value 1 rate */ 94 /* grab the average value 1 rate */
102 temp_buffer = strtok (NULL, " "); 95 temp_buffer = strtok(NULL, " ");
103 if (variable_number == 1) 96 if (config.variable_number == 1) {
104 average_value_rate = strtoul (temp_buffer, NULL, 10); 97 average_value_rate = strtoul(temp_buffer, NULL, 10);
98 }
105 99
106 /* grab the average value 2 rate */ 100 /* grab the average value 2 rate */
107 temp_buffer = strtok (NULL, " "); 101 temp_buffer = strtok(NULL, " ");
108 if (variable_number == 2) 102 if (config.variable_number == 2) {
109 average_value_rate = strtoul (temp_buffer, NULL, 10); 103 average_value_rate = strtoul(temp_buffer, NULL, 10);
104 }
110 105
111 /* grab the maximum value 1 rate */ 106 /* grab the maximum value 1 rate */
112 temp_buffer = strtok (NULL, " "); 107 temp_buffer = strtok(NULL, " ");
113 if (variable_number == 1) 108 if (config.variable_number == 1) {
114 maximum_value_rate = strtoul (temp_buffer, NULL, 10); 109 maximum_value_rate = strtoul(temp_buffer, NULL, 10);
110 }
115 111
116 /* grab the maximum value 2 rate */ 112 /* grab the maximum value 2 rate */
117 temp_buffer = strtok (NULL, " "); 113 temp_buffer = strtok(NULL, " ");
118 if (variable_number == 2) 114 if (config.variable_number == 2) {
119 maximum_value_rate = strtoul (temp_buffer, NULL, 10); 115 maximum_value_rate = strtoul(temp_buffer, NULL, 10);
116 }
120 } 117 }
121 118
122 /* close the log file */ 119 /* close the log file */
123 fclose (fp); 120 fclose(mtrg_log_file);
124 121
125 /* if we couldn't read enough data, return an unknown error */ 122 /* if we couldn't read enough data, return an unknown error */
126 if (line <= 2) { 123 if (line <= 2) {
127 printf (_("Unable to process MRTG log file\n")); 124 printf(_("Unable to process MRTG log file\n"));
128 return STATE_UNKNOWN; 125 return STATE_UNKNOWN;
129 } 126 }
130 127
131 /* make sure the MRTG data isn't too old */ 128 /* make sure the MRTG data isn't too old */
132 time (&current_time); 129 time_t current_time;
133 if (expire_minutes > 0 130 time(&current_time);
134 && (current_time - timestamp) > (expire_minutes * 60)) { 131 if (config.expire_minutes > 0 && (current_time - timestamp) > (config.expire_minutes * 60)) {
135 printf (_("MRTG data has expired (%d minutes old)\n"), 132 printf(_("MRTG data has expired (%d minutes old)\n"),
136 (int) ((current_time - timestamp) / 60)); 133 (int)((current_time - timestamp) / 60));
137 return STATE_WARNING; 134 return STATE_WARNING;
138 } 135 }
139 136
137 unsigned long rate = 0L;
140 /* else check the incoming/outgoing rates */ 138 /* else check the incoming/outgoing rates */
141 if (use_average) 139 if (config.use_average) {
142 rate = average_value_rate; 140 rate = average_value_rate;
143 else 141 } else {
144 rate = maximum_value_rate; 142 rate = maximum_value_rate;
143 }
145 144
146 if (rate > value_critical_threshold) 145 int result = STATE_OK;
146 if (config.value_critical_threshold_set && rate > config.value_critical_threshold) {
147 result = STATE_CRITICAL; 147 result = STATE_CRITICAL;
148 else if (rate > value_warning_threshold) 148 } else if (config.value_warning_threshold_set && rate > config.value_warning_threshold) {
149 result = STATE_WARNING; 149 result = STATE_WARNING;
150 }
150 151
151 printf("%s. %s = %lu %s|%s\n", 152 printf("%s. %s = %lu %s|%s\n", (config.use_average) ? _("Avg") : _("Max"), config.label, rate,
152 (use_average) ? _("Avg") : _("Max"), 153 config.units,
153 label, rate, units, 154 perfdata(config.label, (long)rate, config.units, config.value_warning_threshold_set,
154 perfdata(label, (long) rate, units, 155 (long)config.value_warning_threshold, config.value_critical_threshold_set,
155 (int) value_warning_threshold, (long) value_warning_threshold, 156 (long)config.value_critical_threshold, 0, 0, 0, 0));
156 (int) value_critical_threshold, (long) value_critical_threshold,
157 0, 0, 0, 0));
158 157
159 return result; 158 return result;
160} 159}
161 160
162
163
164/* process command-line arguments */ 161/* process command-line arguments */
165int 162check_mrtg_config_wrapper process_arguments(int argc, char **argv) {
166process_arguments (int argc, char **argv) 163 static struct option longopts[] = {{"logfile", required_argument, 0, 'F'},
167{ 164 {"expires", required_argument, 0, 'e'},
168 int c; 165 {"aggregation", required_argument, 0, 'a'},
169 166 {"variable", required_argument, 0, 'v'},
170 int option = 0; 167 {"critical", required_argument, 0, 'c'},
171 static struct option longopts[] = { 168 {"warning", required_argument, 0, 'w'},
172 {"logfile", required_argument, 0, 'F'}, 169 {"label", required_argument, 0, 'l'},
173 {"expires", required_argument, 0, 'e'}, 170 {"units", required_argument, 0, 'u'},
174 {"aggregation", required_argument, 0, 'a'}, 171 {"variable", required_argument, 0, 'v'},
175 {"variable", required_argument, 0, 'v'}, 172 {"version", no_argument, 0, 'V'},
176 {"critical", required_argument, 0, 'c'}, 173 {"help", no_argument, 0, 'h'},
177 {"warning", required_argument, 0, 'w'}, 174 {0, 0, 0, 0}};
178 {"label", required_argument, 0, 'l'}, 175
179 {"units", required_argument, 0, 'u'}, 176 check_mrtg_config_wrapper result = {
180 {"variable", required_argument, 0, 'v'}, 177 .errorcode = OK,
181 {"version", no_argument, 0, 'V'}, 178 .config = check_mrtg_config_init(),
182 {"help", no_argument, 0, 'h'},
183 {0, 0, 0, 0}
184 }; 179 };
185 180
186 if (argc < 2) 181 if (argc < 2) {
187 return ERROR; 182 result.errorcode = ERROR;
183 return result;
184 }
188 185
189 for (c = 1; c < argc; c++) { 186 for (int i = 1; i < argc; i++) {
190 if (strcmp ("-to", argv[c]) == 0) 187 if (strcmp("-to", argv[i]) == 0) {
191 strcpy (argv[c], "-t"); 188 strcpy(argv[i], "-t");
192 else if (strcmp ("-wt", argv[c]) == 0) 189 } else if (strcmp("-wt", argv[i]) == 0) {
193 strcpy (argv[c], "-w"); 190 strcpy(argv[i], "-w");
194 else if (strcmp ("-ct", argv[c]) == 0) 191 } else if (strcmp("-ct", argv[i]) == 0) {
195 strcpy (argv[c], "-c"); 192 strcpy(argv[i], "-c");
193 }
196 } 194 }
197 195
196 int option_char;
197 int option = 0;
198 while (1) { 198 while (1) {
199 c = getopt_long (argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, 199 option_char = getopt_long(argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, &option);
200 &option);
201 200
202 if (c == -1 || c == EOF) 201 if (option_char == -1 || option_char == EOF) {
203 break; 202 break;
203 }
204 204
205 switch (c) { 205 switch (option_char) {
206 case 'F': /* input file */ 206 case 'F': /* input file */
207 log_file = optarg; 207 result.config.log_file = optarg;
208 break; 208 break;
209 case 'e': /* ups name */ 209 case 'e': /* ups name */
210 expire_minutes = atoi (optarg); 210 result.config.expire_minutes = atoi(optarg);
211 break; 211 break;
212 case 'a': /* port */ 212 case 'a': /* port */
213 if (!strcmp (optarg, "MAX")) 213 result.config.use_average = (bool)(strcmp(optarg, "MAX"));
214 use_average = false;
215 else
216 use_average = true;
217 break; 214 break;
218 case 'v': 215 case 'v':
219 variable_number = atoi (optarg); 216 result.config.variable_number = atoi(optarg);
220 if (variable_number < 1 || variable_number > 2) 217 if (result.config.variable_number < 1 || result.config.variable_number > 2) {
221 usage4 (_("Invalid variable number")); 218 usage4(_("Invalid variable number"));
219 }
222 break; 220 break;
223 case 'w': /* critical time threshold */ 221 case 'w': /* critical time threshold */
224 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);
225 break; 224 break;
226 case 'c': /* warning time threshold */ 225 case 'c': /* warning time threshold */
227 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);
228 break; 228 break;
229 case 'l': /* label */ 229 case 'l': /* label */
230 label = optarg; 230 result.config.label = optarg;
231 break; 231 break;
232 case 'u': /* timeout */ 232 case 'u': /* timeout */
233 units = optarg; 233 result.config.units = optarg;
234 break; 234 break;
235 case 'V': /* version */ 235 case 'V': /* version */
236 print_revision (progname, NP_VERSION); 236 print_revision(progname, NP_VERSION);
237 exit (STATE_UNKNOWN); 237 exit(STATE_UNKNOWN);
238 case 'h': /* help */ 238 case 'h': /* help */
239 print_help (); 239 print_help();
240 exit (STATE_UNKNOWN); 240 exit(STATE_UNKNOWN);
241 case '?': /* help */ 241 case '?': /* help */
242 usage5 (); 242 usage5();
243 } 243 }
244 } 244 }
245 245
246 c = optind; 246 option_char = optind;
247 if (log_file == NULL && argc > c) { 247 if (result.config.log_file == NULL && argc > option_char) {
248 log_file = argv[c++]; 248 result.config.log_file = argv[option_char++];
249 } 249 }
250 250
251 if (expire_minutes <= 0 && argc > c) { 251 if (result.config.expire_minutes <= 0 && argc > option_char) {
252 if (is_intpos (argv[c])) 252 if (is_intpos(argv[option_char])) {
253 expire_minutes = atoi (argv[c++]); 253 result.config.expire_minutes = atoi(argv[option_char++]);
254 else 254 } else {
255 die (STATE_UNKNOWN, 255 die(STATE_UNKNOWN,
256 _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"), 256 _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"),
257 argv[c], progname); 257 argv[option_char], progname);
258 }
258 } 259 }
259 260
260 if (argc > c && strcmp (argv[c], "MAX") == 0) { 261 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) {
261 use_average = false; 262 result.config.use_average = false;
262 c++; 263 option_char++;
263 } 264 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) {
264 else if (argc > c && strcmp (argv[c], "AVG") == 0) { 265 result.config.use_average = true;
265 use_average = true; 266 option_char++;
266 c++;
267 } 267 }
268 268
269 if (argc > c && variable_number == -1) { 269 if (argc > option_char && result.config.variable_number == -1) {
270 variable_number = atoi (argv[c++]); 270 result.config.variable_number = atoi(argv[option_char++]);
271 if (variable_number < 1 || variable_number > 2) { 271 if (result.config.variable_number < 1 || result.config.variable_number > 2) {
272 printf ("%s :", argv[c]); 272 printf("%s :", argv[option_char]);
273 usage (_("Invalid variable number\n")); 273 usage(_("Invalid variable number\n"));
274 } 274 }
275 } 275 }
276 276
277 if (argc > c && value_warning_threshold == 0) { 277 if (argc > option_char && !result.config.value_warning_threshold_set) {
278 value_warning_threshold = strtoul (argv[c++], NULL, 10); 278 result.config.value_warning_threshold_set = true;
279 result.config.value_warning_threshold = strtoul(argv[option_char++], NULL, 10);
279 } 280 }
280 281
281 if (argc > c && value_critical_threshold == 0) { 282 if (argc > option_char && !result.config.value_critical_threshold_set) {
282 value_critical_threshold = strtoul (argv[c++], NULL, 10); 283 result.config.value_critical_threshold_set = true;
284 result.config.value_critical_threshold = strtoul(argv[option_char++], NULL, 10);
283 } 285 }
284 286
285 if (argc > c && strlen (label) == 0) { 287 if (argc > option_char && strlen(result.config.label) == 0) {
286 label = argv[c++]; 288 result.config.label = argv[option_char++];
287 } 289 }
288 290
289 if (argc > c && strlen (units) == 0) { 291 if (argc > option_char && strlen(result.config.units) == 0) {
290 units = argv[c++]; 292 result.config.units = argv[option_char++];
291 } 293 }
292 294
293 return validate_arguments (); 295 return validate_arguments(result);
294} 296}
295 297
296int 298check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper config_wrapper) {
297validate_arguments (void) 299 if (config_wrapper.config.variable_number == -1) {
298{ 300 usage4(_("You must supply the variable number"));
299 if (variable_number == -1) 301 }
300 usage4 (_("You must supply the variable number"));
301 302
302 if (label == NULL) 303 if (config_wrapper.config.label == NULL) {
303 label = strdup ("value"); 304 config_wrapper.config.label = strdup("value");
305 }
304 306
305 if (units == NULL) 307 if (config_wrapper.config.units == NULL) {
306 units = strdup (""); 308 config_wrapper.config.units = strdup("");
309 }
307 310
308 return OK; 311 return config_wrapper;
309} 312}
310 313
311 314void print_help(void) {
312 315 print_revision(progname, NP_VERSION);
313void 316
314print_help (void) 317 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
315{ 318 printf(COPYRIGHT, copyright, email);
316 print_revision (progname, NP_VERSION); 319
317 320 printf("%s\n", _("This plugin will check either the average or maximum value of one of the"));
318 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 321 printf("%s\n", _("two variables recorded in an MRTG log file."));
319 printf (COPYRIGHT, copyright, email); 322
320 323 printf("\n\n");
321 printf ("%s\n", _("This plugin will check either the average or maximum value of one of the")); 324
322 printf ("%s\n", _("two variables recorded in an MRTG log file.")); 325 print_usage();
323 326
324 printf ("\n\n"); 327 printf(UT_HELP_VRSN);
325 328 printf(UT_EXTRA_OPTS);
326 print_usage (); 329
327 330 printf(" %s\n", "-F, --logfile=FILE");
328 printf (UT_HELP_VRSN); 331 printf(" %s\n", _("The MRTG log file containing the data you want to monitor"));
329 printf (UT_EXTRA_OPTS); 332 printf(" %s\n", "-e, --expires=MINUTES");
330 333 printf(" %s\n", _("Minutes before MRTG data is considered to be too old"));
331 printf (" %s\n", "-F, --logfile=FILE"); 334 printf(" %s\n", "-a, --aggregation=AVG|MAX");
332 printf (" %s\n", _("The MRTG log file containing the data you want to monitor")); 335 printf(" %s\n", _("Should we check average or maximum values?"));
333 printf (" %s\n", "-e, --expires=MINUTES"); 336 printf(" %s\n", "-v, --variable=INTEGER");
334 printf (" %s\n", _("Minutes before MRTG data is considered to be too old")); 337 printf(" %s\n", _("Which variable set should we inspect? (1 or 2)"));
335 printf (" %s\n", "-a, --aggregation=AVG|MAX"); 338 printf(" %s\n", "-w, --warning=INTEGER");
336 printf (" %s\n", _("Should we check average or maximum values?")); 339 printf(" %s\n", _("Threshold value for data to result in WARNING status"));
337 printf (" %s\n", "-v, --variable=INTEGER"); 340 printf(" %s\n", "-c, --critical=INTEGER");
338 printf (" %s\n", _("Which variable set should we inspect? (1 or 2)")); 341 printf(" %s\n", _("Threshold value for data to result in CRITICAL status"));
339 printf (" %s\n", "-w, --warning=INTEGER"); 342 printf(" %s\n", "-l, --label=STRING");
340 printf (" %s\n", _("Threshold value for data to result in WARNING status")); 343 printf(" %s\n", _("Type label for data (Examples: Conns, \"Processor Load\", In, Out)"));
341 printf (" %s\n", "-c, --critical=INTEGER"); 344 printf(" %s\n", "-u, --units=STRING");
342 printf (" %s\n", _("Threshold value for data to result in CRITICAL status")); 345 printf(" %s\n", _("Option units label for data (Example: Packets/Sec, Errors/Sec,"));
343 printf (" %s\n", "-l, --label=STRING"); 346 printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")"));
344 printf (" %s\n", _("Type label for data (Examples: Conns, \"Processor Load\", In, Out)")); 347
345 printf (" %s\n", "-u, --units=STRING"); 348 printf("\n");
346 printf (" %s\n", _("Option units label for data (Example: Packets/Sec, Errors/Sec,")); 349 printf(" %s\n",
347 printf (" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")")); 350 _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If"));
348 351 printf(" %s\n", _("the value exceeds the <vcl> threshold, a CRITICAL status is returned. If"));
349 printf ("\n"); 352 printf(" %s\n", _("the data in the log file is older than <expire_minutes> old, a WARNING"));
350 printf (" %s\n", _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If")); 353 printf(" %s\n", _("status is returned and a warning message is printed."));
351 printf (" %s\n", _("the value exceeds the <vcl> threshold, a CRITICAL status is returned. If")); 354
352 printf (" %s\n", _("the data in the log file is older than <expire_minutes> old, a WARNING")); 355 printf("\n");
353 printf (" %s\n", _("status is returned and a warning message is printed.")); 356 printf(" %s\n",
354 357 _("This plugin is useful for monitoring MRTG data that does not correspond to"));
355 printf ("\n"); 358 printf(" %s\n",
356 printf (" %s\n", _("This plugin is useful for monitoring MRTG data that does not correspond to")); 359 _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth)."));
357 printf (" %s\n", _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth).")); 360 printf(" %s\n",
358 printf (" %s\n", _("It can be used to monitor any kind of data that MRTG is monitoring - errors,")); 361 _("It can be used to monitor any kind of data that MRTG is monitoring - errors,"));
359 printf (" %s\n", _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows")); 362 printf(" %s\n",
360 printf (" %s\n", _("me to track processor utilization, user connections, drive space, etc and")); 363 _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows"));
361 printf (" %s\n\n", _("this plugin works well for monitoring that kind of data as well.")); 364 printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and"));
362 365 printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well."));
363 printf ("%s\n", _("Notes:")); 366
364 printf (" %s\n", _("- This plugin only monitors one of the two variables stored in the MRTG log")); 367 printf("%s\n", _("Notes:"));
365 printf (" %s\n", _("file. If you want to monitor both values you will have to define two")); 368 printf(" %s\n",
366 printf (" %s\n", _("commands with different values for the <variable> argument. Of course,")); 369 _("- This plugin only monitors one of the two variables stored in the MRTG log"));
367 printf (" %s\n", _("you can always hack the code to make this plugin work for you...")); 370 printf(" %s\n", _("file. If you want to monitor both values you will have to define two"));
368 printf (" %s\n", _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from")); 371 printf(" %s\n", _("commands with different values for the <variable> argument. Of course,"));
369 printf (" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"); 372 printf(" %s\n", _("you can always hack the code to make this plugin work for you..."));
370 373 printf(" %s\n",
371 printf (UT_SUPPORT); 374 _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from"));
375 printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html");
376
377 printf(UT_SUPPORT);
372} 378}
373 379
374
375
376/* original command line: 380/* original command line:
377 <log_file> <expire_minutes> <AVG|MAX> <variable> <vwl> <vcl> <label> [units] */ 381 <log_file> <expire_minutes> <AVG|MAX> <variable> <vwl> <vcl> <label> [units] */
378 382
379void 383void print_usage(void) {
380print_usage (void) 384 printf("%s\n", _("Usage:"));
381{ 385 printf("%s -F log_file -a <AVG | MAX> -v variable -w warning -c critical\n", progname);
382 printf ("%s\n", _("Usage:")); 386 printf("[-l label] [-u units] [-e expire_minutes] [-t timeout] [-v]\n");
383 printf ("%s -F log_file -a <AVG | MAX> -v variable -w warning -c critical\n",progname);
384 printf ("[-l label] [-u units] [-e expire_minutes] [-t timeout] [-v]\n");
385} 387}
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 bd25d47d..10ce936f 100644
--- a/plugins/check_mrtgtraf.c
+++ b/plugins/check_mrtgtraf.c
@@ -1,381 +1,360 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_mrtgtraf plugin 3 * Monitoring check_mrtgtraf plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_mtrgtraf plugin 10 * This file contains the check_mtrgtraf plugin
11* 11 *
12* This plugin will check the incoming/outgoing transfer rates of a router 12 * This plugin will check the incoming/outgoing transfer rates of a router
13* switch, etc recorded in an MRTG log. 13 * switch, etc recorded in an MRTG log.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_mrtgtraf";
33const char *copyright = "1999-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "check_mrtgtraf.d/config.h"
32#include "common.h" 37#include "common.h"
33#include "utils.h" 38#include "utils.h"
34 39
35const char *progname = "check_mrtgtraf"; 40typedef struct {
36const char *copyright = "1999-2007"; 41 int errorcode;
37const char *email = "devel@monitoring-plugins.org"; 42 check_mrtgtraf_config config;
43} check_mrtgtraf_config_wrapper;
38 44
39int process_arguments (int, char **); 45static check_mrtgtraf_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
40int validate_arguments (void); 46static void print_help(void);
41void print_help(void);
42void print_usage(void); 47void print_usage(void);
43 48
44char *log_file = NULL; 49int main(int argc, char **argv) {
45int expire_minutes = -1; 50 setlocale(LC_ALL, "");
46bool use_average = true; 51 bindtextdomain(PACKAGE, LOCALEDIR);
47unsigned long incoming_warning_threshold = 0L; 52 textdomain(PACKAGE);
48unsigned long incoming_critical_threshold = 0L;
49unsigned long outgoing_warning_threshold = 0L;
50unsigned long outgoing_critical_threshold = 0L;
51 53
54 /* Parse extra opts if any */
55 argv = np_extra_opts(&argc, argv, progname);
56
57 check_mrtgtraf_config_wrapper tmp_config = process_arguments(argc, argv);
58 if (tmp_config.errorcode == ERROR) {
59 usage4(_("Could not parse arguments"));
60 }
61
62 const check_mrtgtraf_config config = tmp_config.config;
63
64 /* open the MRTG log file for reading */
65 FILE *mrtg_log_file_ptr = fopen(config.log_file, "r");
66 if (mrtg_log_file_ptr == NULL) {
67 usage4(_("Unable to open MRTG log file"));
68 }
52 69
53int
54main (int argc, char **argv)
55{
56 int result = STATE_OK;
57 FILE *fp;
58 int line;
59 char input_buffer[MAX_INPUT_BUFFER];
60 char *temp_buffer;
61 time_t current_time;
62 char *error_message;
63 time_t timestamp = 0L; 70 time_t timestamp = 0L;
71 char input_buffer[MAX_INPUT_BUFFER];
64 unsigned long average_incoming_rate = 0L; 72 unsigned long average_incoming_rate = 0L;
65 unsigned long average_outgoing_rate = 0L; 73 unsigned long average_outgoing_rate = 0L;
66 unsigned long maximum_incoming_rate = 0L; 74 unsigned long maximum_incoming_rate = 0L;
67 unsigned long maximum_outgoing_rate = 0L; 75 unsigned long maximum_outgoing_rate = 0L;
68 unsigned long incoming_rate = 0L; 76 int line = 0;
69 unsigned long outgoing_rate = 0L; 77 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mrtg_log_file_ptr)) {
70 double adjusted_incoming_rate = 0.0;
71 double adjusted_outgoing_rate = 0.0;
72 char incoming_speed_rating[8];
73 char outgoing_speed_rating[8];
74
75 setlocale (LC_ALL, "");
76 bindtextdomain (PACKAGE, LOCALEDIR);
77 textdomain (PACKAGE);
78
79 /* Parse extra opts if any */
80 argv=np_extra_opts (&argc, argv, progname);
81
82 if (process_arguments (argc, argv) == ERROR)
83 usage4 (_("Could not parse arguments"));
84
85 /* open the MRTG log file for reading */
86 fp = fopen (log_file, "r");
87 if (fp == NULL)
88 usage4 (_("Unable to open MRTG log file"));
89
90 line = 0;
91 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) {
92 78
93 line++; 79 line++;
94 80
95 /* skip the first line of the log file */ 81 /* skip the first line of the log file */
96 if (line == 1) 82 if (line == 1) {
97 continue; 83 continue;
84 }
98 85
99 /* break out of read loop */ 86 /* break out of read loop */
100 /* 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 */
101 if (line > 2) 88 if (line > 2) {
102 break; 89 break;
90 }
103 91
104 /* grab the timestamp */ 92 /* grab the timestamp */
105 temp_buffer = strtok (input_buffer, " "); 93 char *temp_buffer = strtok(input_buffer, " ");
106 timestamp = strtoul (temp_buffer, NULL, 10); 94 timestamp = strtoul(temp_buffer, NULL, 10);
107 95
108 /* grab the average incoming transfer rate */ 96 /* grab the average incoming transfer rate */
109 temp_buffer = strtok (NULL, " "); 97 temp_buffer = strtok(NULL, " ");
110 average_incoming_rate = strtoul (temp_buffer, NULL, 10); 98 average_incoming_rate = strtoul(temp_buffer, NULL, 10);
111 99
112 /* grab the average outgoing transfer rate */ 100 /* grab the average outgoing transfer rate */
113 temp_buffer = strtok (NULL, " "); 101 temp_buffer = strtok(NULL, " ");
114 average_outgoing_rate = strtoul (temp_buffer, NULL, 10); 102 average_outgoing_rate = strtoul(temp_buffer, NULL, 10);
115 103
116 /* grab the maximum incoming transfer rate */ 104 /* grab the maximum incoming transfer rate */
117 temp_buffer = strtok (NULL, " "); 105 temp_buffer = strtok(NULL, " ");
118 maximum_incoming_rate = strtoul (temp_buffer, NULL, 10); 106 maximum_incoming_rate = strtoul(temp_buffer, NULL, 10);
119 107
120 /* grab the maximum outgoing transfer rate */ 108 /* grab the maximum outgoing transfer rate */
121 temp_buffer = strtok (NULL, " "); 109 temp_buffer = strtok(NULL, " ");
122 maximum_outgoing_rate = strtoul (temp_buffer, NULL, 10); 110 maximum_outgoing_rate = strtoul(temp_buffer, NULL, 10);
123 } 111 }
124 112
125 /* close the log file */ 113 /* close the log file */
126 fclose (fp); 114 fclose(mrtg_log_file_ptr);
127 115
128 /* if we couldn't read enough data, return an unknown error */ 116 /* if we couldn't read enough data, return an unknown error */
129 if (line <= 2) 117 if (line <= 2) {
130 usage4 (_("Unable to process MRTG log file")); 118 usage4(_("Unable to process MRTG log file"));
119 }
131 120
132 /* make sure the MRTG data isn't too old */ 121 /* make sure the MRTG data isn't too old */
133 time (&current_time); 122 time_t current_time;
134 if ((expire_minutes > 0) && 123 time(&current_time);
135 (current_time - timestamp) > (expire_minutes * 60)) 124 if ((config.expire_minutes > 0) && (current_time - timestamp) > (config.expire_minutes * 60)) {
136 die (STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"), 125 die(STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"),
137 (int) ((current_time - timestamp) / 60)); 126 (int)((current_time - timestamp) / 60));
127 }
138 128
129 unsigned long incoming_rate = 0L;
130 unsigned long outgoing_rate = 0L;
139 /* else check the incoming/outgoing rates */ 131 /* else check the incoming/outgoing rates */
140 if (use_average) { 132 if (config.use_average) {
141 incoming_rate = average_incoming_rate; 133 incoming_rate = average_incoming_rate;
142 outgoing_rate = average_outgoing_rate; 134 outgoing_rate = average_outgoing_rate;
143 } 135 } else {
144 else {
145 incoming_rate = maximum_incoming_rate; 136 incoming_rate = maximum_incoming_rate;
146 outgoing_rate = maximum_outgoing_rate; 137 outgoing_rate = maximum_outgoing_rate;
147 } 138 }
148 139
140 double adjusted_incoming_rate = 0.0;
141 char incoming_speed_rating[8];
149 /* report incoming traffic in Bytes/sec */ 142 /* report incoming traffic in Bytes/sec */
150 if (incoming_rate < 1024) { 143 if (incoming_rate < 1024) {
151 strcpy (incoming_speed_rating, "B"); 144 strcpy(incoming_speed_rating, "B");
152 adjusted_incoming_rate = (double) incoming_rate; 145 adjusted_incoming_rate = (double)incoming_rate;
153 } 146 }
154 147
155 /* report incoming traffic in KBytes/sec */ 148 /* report incoming traffic in KBytes/sec */
156 else if (incoming_rate < (1024 * 1024)) { 149 else if (incoming_rate < (1024 * 1024)) {
157 strcpy (incoming_speed_rating, "KB"); 150 strcpy(incoming_speed_rating, "KB");
158 adjusted_incoming_rate = (double) (incoming_rate / 1024.0); 151 adjusted_incoming_rate = (double)(incoming_rate / 1024.0);
159 } 152 }
160 153
161 /* report incoming traffic in MBytes/sec */ 154 /* report incoming traffic in MBytes/sec */
162 else { 155 else {
163 strcpy (incoming_speed_rating, "MB"); 156 strcpy(incoming_speed_rating, "MB");
164 adjusted_incoming_rate = (double) (incoming_rate / 1024.0 / 1024.0); 157 adjusted_incoming_rate = (double)(incoming_rate / 1024.0 / 1024.0);
165 } 158 }
166 159
160 double adjusted_outgoing_rate = 0.0;
161 char outgoing_speed_rating[8];
167 /* report outgoing traffic in Bytes/sec */ 162 /* report outgoing traffic in Bytes/sec */
168 if (outgoing_rate < 1024) { 163 if (outgoing_rate < 1024) {
169 strcpy (outgoing_speed_rating, "B"); 164 strcpy(outgoing_speed_rating, "B");
170 adjusted_outgoing_rate = (double) outgoing_rate; 165 adjusted_outgoing_rate = (double)outgoing_rate;
171 } 166 }
172 167
173 /* report outgoing traffic in KBytes/sec */ 168 /* report outgoing traffic in KBytes/sec */
174 else if (outgoing_rate < (1024 * 1024)) { 169 else if (outgoing_rate < (1024 * 1024)) {
175 strcpy (outgoing_speed_rating, "KB"); 170 strcpy(outgoing_speed_rating, "KB");
176 adjusted_outgoing_rate = (double) (outgoing_rate / 1024.0); 171 adjusted_outgoing_rate = (double)(outgoing_rate / 1024.0);
177 } 172 }
178 173
179 /* report outgoing traffic in MBytes/sec */ 174 /* report outgoing traffic in MBytes/sec */
180 else { 175 else {
181 strcpy (outgoing_speed_rating, "MB"); 176 strcpy(outgoing_speed_rating, "MB");
182 adjusted_outgoing_rate = (double) (outgoing_rate / 1024.0 / 1024.0); 177 adjusted_outgoing_rate = (outgoing_rate / 1024.0 / 1024.0);
183 } 178 }
184 179
185 if (incoming_rate > incoming_critical_threshold 180 int result = STATE_OK;
186 || outgoing_rate > outgoing_critical_threshold) { 181 if (incoming_rate > config.incoming_critical_threshold ||
182 outgoing_rate > config.outgoing_critical_threshold) {
187 result = STATE_CRITICAL; 183 result = STATE_CRITICAL;
188 } 184 } else if (incoming_rate > config.incoming_warning_threshold ||
189 else if (incoming_rate > incoming_warning_threshold 185 outgoing_rate > config.outgoing_warning_threshold) {
190 || outgoing_rate > outgoing_warning_threshold) {
191 result = STATE_WARNING; 186 result = STATE_WARNING;
192 } 187 }
193 188
194 xasprintf (&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"), 189 char *error_message;
195 (use_average) ? _("Avg") : _("Max"), adjusted_incoming_rate, 190 xasprintf(&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"),
196 incoming_speed_rating, (use_average) ? _("Avg") : _("Max"), 191 (config.use_average) ? _("Avg") : _("Max"), adjusted_incoming_rate,
197 adjusted_outgoing_rate, outgoing_speed_rating, 192 incoming_speed_rating, (config.use_average) ? _("Avg") : _("Max"),
198 fperfdata("in", adjusted_incoming_rate, incoming_speed_rating, 193 adjusted_outgoing_rate, outgoing_speed_rating,
199 (int)incoming_warning_threshold, incoming_warning_threshold, 194 fperfdata("in", adjusted_incoming_rate, incoming_speed_rating,
200 (int)incoming_critical_threshold, incoming_critical_threshold, 195 (int)config.incoming_warning_threshold, config.incoming_warning_threshold,
201 true, 0, false, 0), 196 (int)config.incoming_critical_threshold, config.incoming_critical_threshold,
202 fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating, 197 true, 0, false, 0),
203 (int)outgoing_warning_threshold, outgoing_warning_threshold, 198 fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating,
204 (int)outgoing_critical_threshold, outgoing_critical_threshold, 199 (int)config.outgoing_warning_threshold, config.outgoing_warning_threshold,
205 true, 0, false, 0)); 200 (int)config.outgoing_critical_threshold, config.outgoing_critical_threshold,
206 201 true, 0, false, 0));
207 printf (_("Traffic %s - %s\n"), state_text(result), error_message); 202
203 printf(_("Traffic %s - %s\n"), state_text(result), error_message);
208 204
209 return result; 205 return result;
210} 206}
211 207
212
213
214/* process command-line arguments */ 208/* process command-line arguments */
215int 209check_mrtgtraf_config_wrapper process_arguments(int argc, char **argv) {
216process_arguments (int argc, char **argv) 210 static struct option longopts[] = {{"filename", required_argument, 0, 'F'},
217{ 211 {"expires", required_argument, 0, 'e'},
218 int c; 212 {"aggregation", required_argument, 0, 'a'},
219 213 {"critical", required_argument, 0, 'c'},
220 int option = 0; 214 {"warning", required_argument, 0, 'w'},
221 static struct option longopts[] = { 215 {"version", no_argument, 0, 'V'},
222 {"filename", required_argument, 0, 'F'}, 216 {"help", no_argument, 0, 'h'},
223 {"expires", required_argument, 0, 'e'}, 217 {0, 0, 0, 0}};
224 {"aggregation", required_argument, 0, 'a'}, 218
225 {"critical", required_argument, 0, 'c'}, 219 check_mrtgtraf_config_wrapper result = {
226 {"warning", required_argument, 0, 'w'}, 220 .errorcode = OK,
227 {"version", no_argument, 0, 'V'}, 221 .config = check_mrtgtraf_config_init(),
228 {"help", no_argument, 0, 'h'},
229 {0, 0, 0, 0}
230 }; 222 };
223 if (argc < 2) {
224 result.errorcode = ERROR;
225 return result;
226 }
231 227
232 if (argc < 2) 228 for (int i = 1; i < argc; i++) {
233 return ERROR; 229 if (strcmp("-to", argv[i]) == 0) {
234 230 strcpy(argv[i], "-t");
235 for (c = 1; c < argc; c++) { 231 } else if (strcmp("-wt", argv[i]) == 0) {
236 if (strcmp ("-to", argv[c]) == 0) 232 strcpy(argv[i], "-w");
237 strcpy (argv[c], "-t"); 233 } else if (strcmp("-ct", argv[i]) == 0) {
238 else if (strcmp ("-wt", argv[c]) == 0) 234 strcpy(argv[i], "-c");
239 strcpy (argv[c], "-w"); 235 }
240 else if (strcmp ("-ct", argv[c]) == 0)
241 strcpy (argv[c], "-c");
242 } 236 }
243 237
244 while (1) { 238 int option_char;
245 c = getopt_long (argc, argv, "hVF:e:a:c:w:", longopts, &option); 239 int option = 0;
240 while (true) {
241 option_char = getopt_long(argc, argv, "hVF:e:a:c:w:", longopts, &option);
246 242
247 if (c == -1 || c == EOF) 243 if (option_char == -1 || option_char == EOF) {
248 break; 244 break;
245 }
249 246
250 switch (c) { 247 switch (option_char) {
251 case 'F': /* input file */ 248 case 'F': /* input file */
252 log_file = optarg; 249 result.config.log_file = optarg;
253 break; 250 break;
254 case 'e': /* expiration time */ 251 case 'e': /* expiration time */
255 expire_minutes = atoi (optarg); 252 result.config.expire_minutes = atoi(optarg);
256 break; 253 break;
257 case 'a': /* aggregation (AVE or MAX) */ 254 case 'a': /* aggregation (AVE or MAX) */
258 if (!strcmp (optarg, "MAX")) 255 result.config.use_average = (bool)(strcmp(optarg, "MAX"));
259 use_average = false;
260 else
261 use_average = true;
262 break; 256 break;
263 case 'c': /* warning threshold */ 257 case 'c': /* warning threshold */
264 sscanf (optarg, "%lu,%lu", &incoming_critical_threshold, 258 sscanf(optarg, "%lu,%lu", &result.config.incoming_critical_threshold,
265 &outgoing_critical_threshold); 259 &result.config.outgoing_critical_threshold);
266 break; 260 break;
267 case 'w': /* critical threshold */ 261 case 'w': /* critical threshold */
268 sscanf (optarg, "%lu,%lu", &incoming_warning_threshold, 262 sscanf(optarg, "%lu,%lu", &result.config.incoming_warning_threshold,
269 &outgoing_warning_threshold); 263 &result.config.outgoing_warning_threshold);
270 break; 264 break;
271 case 'V': /* version */ 265 case 'V': /* version */
272 print_revision (progname, NP_VERSION); 266 print_revision(progname, NP_VERSION);
273 exit (STATE_UNKNOWN); 267 exit(STATE_UNKNOWN);
274 case 'h': /* help */ 268 case 'h': /* help */
275 print_help (); 269 print_help();
276 exit (STATE_UNKNOWN); 270 exit(STATE_UNKNOWN);
277 case '?': /* help */ 271 case '?': /* help */
278 usage5 (); 272 usage5();
279 } 273 }
280 } 274 }
281 275
282 c = optind; 276 option_char = optind;
283 if (argc > c && log_file == NULL) { 277 if (argc > option_char && result.config.log_file == NULL) {
284 log_file = argv[c++]; 278 result.config.log_file = argv[option_char++];
285 } 279 }
286 280
287 if (argc > c && expire_minutes == -1) { 281 if (argc > option_char && result.config.expire_minutes == -1) {
288 expire_minutes = atoi (argv[c++]); 282 result.config.expire_minutes = atoi(argv[option_char++]);
289 } 283 }
290 284
291 if (argc > c && strcmp (argv[c], "MAX") == 0) { 285 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) {
292 use_average = false; 286 result.config.use_average = false;
293 c++; 287 option_char++;
294 } 288 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) {
295 else if (argc > c && strcmp (argv[c], "AVG") == 0) { 289 result.config.use_average = true;
296 use_average = true; 290 option_char++;
297 c++;
298 } 291 }
299 292
300 if (argc > c && incoming_warning_threshold == 0) { 293 if (argc > option_char && result.config.incoming_warning_threshold == 0) {
301 incoming_warning_threshold = strtoul (argv[c++], NULL, 10); 294 result.config.incoming_warning_threshold = strtoul(argv[option_char++], NULL, 10);
302 } 295 }
303 296
304 if (argc > c && incoming_critical_threshold == 0) { 297 if (argc > option_char && result.config.incoming_critical_threshold == 0) {
305 incoming_critical_threshold = strtoul (argv[c++], NULL, 10); 298 result.config.incoming_critical_threshold = strtoul(argv[option_char++], NULL, 10);
306 } 299 }
307 300
308 if (argc > c && outgoing_warning_threshold == 0) { 301 if (argc > option_char && result.config.outgoing_warning_threshold == 0) {
309 outgoing_warning_threshold = strtoul (argv[c++], NULL, 10); 302 result.config.outgoing_warning_threshold = strtoul(argv[option_char++], NULL, 10);
310 } 303 }
311
312 if (argc > c && outgoing_critical_threshold == 0) {
313 outgoing_critical_threshold = strtoul (argv[c++], NULL, 10);
314 }
315
316 return validate_arguments ();
317}
318 304
305 if (argc > option_char && result.config.outgoing_critical_threshold == 0) {
306 result.config.outgoing_critical_threshold = strtoul(argv[option_char++], NULL, 10);
307 }
319 308
320int 309 return result;
321validate_arguments (void)
322{
323 return OK;
324} 310}
325 311
326 312void print_help(void) {
327void 313 print_revision(progname, NP_VERSION);
328print_help (void) 314
329{ 315 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
330 print_revision (progname, NP_VERSION); 316 printf(COPYRIGHT, copyright, email);
331 317
332 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 318 printf("%s\n", _("This plugin will check the incoming/outgoing transfer rates of a router,"));
333 printf (COPYRIGHT, copyright, email); 319 printf("%s\n", _("switch, etc recorded in an MRTG log. If the newest log entry is older"));
334 320 printf("%s\n", _("than <expire_minutes>, a WARNING status is returned. If either the"));
335 printf ("%s\n", _("This plugin will check the incoming/outgoing transfer rates of a router,")); 321 printf("%s\n", _("incoming or outgoing rates exceed the <icl> or <ocl> thresholds (in"));
336 printf ("%s\n", _("switch, etc recorded in an MRTG log. If the newest log entry is older")); 322 printf("%s\n", _("Bytes/sec), a CRITICAL status results. If either of the rates exceed"));
337 printf ("%s\n", _("than <expire_minutes>, a WARNING status is returned. If either the")); 323 printf("%s\n", _("the <iwl> or <owl> thresholds (in Bytes/sec), a WARNING status results."));
338 printf ("%s\n", _("incoming or outgoing rates exceed the <icl> or <ocl> thresholds (in")); 324
339 printf ("%s\n", _("Bytes/sec), a CRITICAL status results. If either of the rates exceed")); 325 printf("\n\n");
340 printf ("%s\n", _("the <iwl> or <owl> thresholds (in Bytes/sec), a WARNING status results.")); 326
341 327 print_usage();
342 printf ("\n\n"); 328
343 329 printf(UT_HELP_VRSN);
344 print_usage (); 330 printf(UT_EXTRA_OPTS);
345 331
346 printf (UT_HELP_VRSN); 332 printf(" %s\n", "-F, --filename=STRING");
347 printf (UT_EXTRA_OPTS); 333 printf(" %s\n", _("File to read log from"));
348 334 printf(" %s\n", "-e, --expires=INTEGER");
349 printf (" %s\n", "-F, --filename=STRING"); 335 printf(" %s\n", _("Minutes after which log expires"));
350 printf (" %s\n", _("File to read log from")); 336 printf(" %s\n", "-a, --aggregation=(AVG|MAX)");
351 printf (" %s\n", "-e, --expires=INTEGER"); 337 printf(" %s\n", _("Test average or maximum"));
352 printf (" %s\n", _("Minutes after which log expires")); 338 printf(" %s\n", "-w, --warning");
353 printf (" %s\n", "-a, --aggregation=(AVG|MAX)"); 339 printf(" %s\n", _("Warning threshold pair <incoming>,<outgoing>"));
354 printf (" %s\n", _("Test average or maximum")); 340 printf(" %s\n", "-c, --critical");
355 printf (" %s\n", "-w, --warning"); 341 printf(" %s\n", _("Critical threshold pair <incoming>,<outgoing>"));
356 printf (" %s\n", _("Warning threshold pair <incoming>,<outgoing>")); 342
357 printf (" %s\n", "-c, --critical"); 343 printf("\n");
358 printf (" %s\n", _("Critical threshold pair <incoming>,<outgoing>")); 344 printf("%s\n", _("Notes:"));
359 345 printf(" %s\n", _("- MRTG stands for Multi Router Traffic Grapher. It can be downloaded from"));
360 printf ("\n"); 346 printf(" %s\n", " http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html");
361 printf ("%s\n", _("Notes:")); 347 printf(" %s\n", _("- While MRTG can monitor things other than traffic rates, this"));
362 printf (" %s\n", _("- MRTG stands for Multi Router Traffic Grapher. It can be downloaded from")); 348 printf(" %s\n", _(" plugin probably won't work with much else without modification."));
363 printf (" %s\n", " http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"); 349 printf(" %s\n", _("- The calculated i/o rates are a little off from what MRTG actually"));
364 printf (" %s\n", _("- While MRTG can monitor things other than traffic rates, this")); 350 printf(" %s\n", _(" reports. I'm not sure why this is right now, but will look into it"));
365 printf (" %s\n", _(" plugin probably won't work with much else without modification.")); 351 printf(" %s\n", _(" for future enhancements of this plugin."));
366 printf (" %s\n", _("- The calculated i/o rates are a little off from what MRTG actually")); 352
367 printf (" %s\n", _(" reports. I'm not sure why this is right now, but will look into it")); 353 printf(UT_SUPPORT);
368 printf (" %s\n", _(" for future enhancements of this plugin."));
369
370 printf (UT_SUPPORT);
371} 354}
372 355
373 356void print_usage(void) {
374 357 printf(_("Usage"));
375void 358 printf(" %s -F <log_file> -a <AVG | MAX> -w <warning_pair>\n", progname);
376print_usage (void) 359 printf("-c <critical_pair> [-e expire_minutes]\n");
377{
378 printf (_("Usage"));
379 printf (" %s -F <log_file> -a <AVG | MAX> -w <warning_pair>\n",progname);
380 printf ("-c <critical_pair> [-e expire_minutes]\n");
381} 360}
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 6a7daf11..3d7ec4cd 100644
--- a/plugins/check_mysql.c
+++ b/plugins/check_mysql.c
@@ -1,267 +1,296 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_mysql plugin 3 * Monitoring check_mysql plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at) 6 * Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)
7* Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net) 7 * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net)
8* Copyright (c) 1999-2011 Monitoring Plugins Development Team 8 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
9* 9 *
10* Description: 10 * Description:
11* 11 *
12* This file contains the check_mysql plugin 12 * This file contains the check_mysql plugin
13* 13 *
14* This program tests connections to a mysql server 14 * This program tests connections to a mysql server
15* 15 *
16* 16 *
17* This program is free software: you can redistribute it and/or modify 17 * This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by 18 * it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or 19 * the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version. 20 * (at your option) any later version.
21* 21 *
22* This program is distributed in the hope that it will be useful, 22 * This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details. 25 * GNU General Public License for more details.
26* 26 *
27* You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>. 28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29* 29 *
30* 30 *
31*****************************************************************************/ 31 *****************************************************************************/
32 32
33const char *progname = "check_mysql"; 33const char *progname = "check_mysql";
34const char *copyright = "1999-2011"; 34const char *copyright = "1999-2024";
35const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
36 36
37#define SLAVERESULTSIZE 96 37#define REPLICA_RESULTSIZE 96
38 38
39#include "common.h" 39#include "common.h"
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
48char *db_user = NULL; 49static int verbose = 0;
49char *db_host = NULL;
50char *db_socket = NULL;
51char *db_pass = NULL;
52char *db = NULL;
53char *ca_cert = NULL;
54char *ca_dir = NULL;
55char *cert = NULL;
56char *key = NULL;
57char *ciphers = NULL;
58bool ssl = false;
59char *opt_file = NULL;
60char *opt_group = NULL;
61unsigned int db_port = MYSQL_PORT;
62int check_slave = 0, warn_sec = 0, crit_sec = 0;
63int ignore_auth = 0;
64int verbose = 0;
65
66static double warning_time = 0;
67static double critical_time = 0;
68 50
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", 53 "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache",
72 "Open_tables", 54 "Threads_connected", "Threads_running"};
73 "Qcache_free_memory",
74 "Qcache_queries_in_cache",
75 "Threads_connected",
76 "Threads_running"
77};
78 55
79#define LENGTH_METRIC_COUNTER 9 56#define LENGTH_METRIC_COUNTER 9
80static const char *metric_counter[LENGTH_METRIC_COUNTER] = { 57static const char *metric_counter[LENGTH_METRIC_COUNTER] = {"Connections",
81 "Connections", 58 "Qcache_hits",
82 "Qcache_hits", 59 "Qcache_inserts",
83 "Qcache_inserts", 60 "Qcache_lowmem_prunes",
84 "Qcache_lowmem_prunes", 61 "Qcache_not_cached",
85 "Qcache_not_cached", 62 "Queries",
86 "Queries", 63 "Questions",
87 "Questions", 64 "Table_locks_waited",
88 "Table_locks_waited", 65 "Uptime"};
89 "Uptime" 66
90}; 67#define MYSQLDUMP_THREADS_QUERY \
91 68 "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE " \
92#define MYSQLDUMP_THREADS_QUERY "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE 'SELECT /*!40001 SQL_NO_CACHE */%'" 69 "'SELECT /*!40001 SQL_NO_CACHE */%'"
93 70
94thresholds *my_threshold = NULL; 71typedef struct {
95 72 int errorcode;
96int process_arguments (int, char **); 73 check_mysql_config config;
97int validate_arguments (void); 74} check_mysql_config_wrapper;
98void print_help (void); 75static check_mysql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
99void print_usage (void); 76static check_mysql_config_wrapper validate_arguments(check_mysql_config_wrapper /*config_wrapper*/);
100 77static void print_help(void);
101int 78void print_usage(void);
102main (int argc, char **argv) 79
103{ 80int main(int argc, char **argv) {
81 setlocale(LC_ALL, "");
82 bindtextdomain(PACKAGE, LOCALEDIR);
83 textdomain(PACKAGE);
104 84
105 MYSQL mysql; 85 /* Parse extra opts if any */
106 MYSQL_RES *res; 86 argv = np_extra_opts(&argc, argv, progname);
107 MYSQL_ROW row;
108
109 /* should be status */
110 87
111 char *result = NULL; 88 check_mysql_config_wrapper tmp_config = process_arguments(argc, argv);
112 char *error = NULL; 89 if (tmp_config.errorcode == ERROR) {
113 char slaveresult[SLAVERESULTSIZE] = { 0 }; 90 usage4(_("Could not parse arguments"));
114 char* perf; 91 }
115 92
116 perf = strdup (""); 93 const check_mysql_config config = tmp_config.config;
117 94
118 setlocale (LC_ALL, ""); 95 MYSQL mysql;
119 bindtextdomain (PACKAGE, LOCALEDIR); 96 /* initialize mysql */
120 textdomain (PACKAGE); 97 mysql_init(&mysql);
121 98
122 /* Parse extra opts if any */ 99 if (config.opt_file != NULL) {
123 argv=np_extra_opts (&argc, argv, progname); 100 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file);
101 }
124 102
125 if (process_arguments (argc, argv) == ERROR) 103 if (config.opt_group != NULL) {
126 usage4 (_("Could not parse arguments")); 104 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group);
105 } else {
106 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client");
107 }
127 108
128 /* initialize mysql */ 109 if (config.ssl) {
129 mysql_init (&mysql); 110 mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir,
130 111 config.ciphers);
131 if (opt_file != NULL) 112 }
132 mysql_options(&mysql,MYSQL_READ_DEFAULT_FILE,opt_file);
133
134 if (opt_group != NULL)
135 mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,opt_group);
136 else
137 mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"client");
138
139 if (ssl)
140 mysql_ssl_set(&mysql,key,cert,ca_cert,ca_dir,ciphers);
141 /* establish a connection to the server and error checking */ 113 /* establish a connection to the server and error checking */
142 if (!mysql_real_connect(&mysql,db_host,db_user,db_pass,db,db_port,db_socket,0)) { 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)) {
143 /* Depending on internally-selected auth plugin MySQL might return */ 116 /* Depending on internally-selected auth plugin MySQL might return */
144 /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */ 117 /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */
145 /* Semantically these errors are the same. */ 118 /* Semantically these errors are the same. */
146 if (ignore_auth && (mysql_errno (&mysql) == ER_ACCESS_DENIED_ERROR || mysql_errno (&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) 119 if (config.ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR ||
147 { 120 mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) {
148 printf("MySQL OK - Version: %s (protocol %d)\n", 121 printf("MySQL OK - Version: %s (protocol %d)\n", mysql_get_server_info(&mysql),
149 mysql_get_server_info(&mysql), 122 mysql_get_proto_info(&mysql));
150 mysql_get_proto_info(&mysql) 123 mysql_close(&mysql);
151 );
152 mysql_close (&mysql);
153 return STATE_OK; 124 return STATE_OK;
154 } 125 }
155 else if (mysql_errno (&mysql) == CR_UNKNOWN_HOST) 126
156 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 127 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
157 else if (mysql_errno (&mysql) == CR_VERSION_ERROR) 128 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
158 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 129 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
159 else if (mysql_errno (&mysql) == CR_OUT_OF_MEMORY) 130 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
160 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 131 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
161 else if (mysql_errno (&mysql) == CR_IPSOCK_ERROR) 132 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
162 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 133 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
163 else if (mysql_errno (&mysql) == CR_SOCKET_CREATE_ERROR) 134 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
164 die (STATE_WARNING, "%s\n", mysql_error (&mysql)); 135 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
165 else 136 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
166 die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); 137 } else {
138 die(STATE_CRITICAL, "%s\n", mysql_error(&mysql));
139 }
167 } 140 }
168 141
169 /* get the server stats */ 142 /* get the server stats */
170 result = strdup (mysql_stat (&mysql)); 143 char *result = strdup(mysql_stat(&mysql));
171 144
172 /* error checking once more */ 145 /* error checking once more */
173 if (mysql_error (&mysql)) { 146 if (mysql_error(&mysql)) {
174 if (mysql_errno (&mysql) == CR_SERVER_GONE_ERROR) 147 if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) {
175 die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); 148 die(STATE_CRITICAL, "%s\n", mysql_error(&mysql));
176 else if (mysql_errno (&mysql) == CR_SERVER_LOST) 149 } else if (mysql_errno(&mysql) == CR_SERVER_LOST) {
177 die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); 150 die(STATE_CRITICAL, "%s\n", mysql_error(&mysql));
178 else if (mysql_errno (&mysql) == CR_UNKNOWN_ERROR) 151 } else if (mysql_errno(&mysql) == CR_UNKNOWN_ERROR) {
179 die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); 152 die(STATE_CRITICAL, "%s\n", mysql_error(&mysql));
153 }
180 } 154 }
181 155
156 char *perf = strdup("");
157 char *error = NULL;
158 MYSQL_RES *res;
159 MYSQL_ROW row;
182 /* try to fetch some perf data */ 160 /* try to fetch some perf data */
183 if (mysql_query (&mysql, "show global status") == 0) { 161 if (mysql_query(&mysql, "show global status") == 0) {
184 if ( (res = mysql_store_result (&mysql)) == NULL) { 162 if ((res = mysql_store_result(&mysql)) == NULL) {
185 error = strdup(mysql_error(&mysql)); 163 error = strdup(mysql_error(&mysql));
186 mysql_close (&mysql); 164 mysql_close(&mysql);
187 die (STATE_CRITICAL, _("status store_result error: %s\n"), error); 165 die(STATE_CRITICAL, _("status store_result error: %s\n"), error);
188 } 166 }
189 167
190 while ( (row = mysql_fetch_row (res)) != NULL) { 168 while ((row = mysql_fetch_row(res)) != NULL) {
191 int i; 169 for (int i = 0; i < LENGTH_METRIC_UNIT; i++) {
192
193 for(i = 0; i < LENGTH_METRIC_UNIT; i++) {
194 if (strcmp(row[0], metric_unit[i]) == 0) { 170 if (strcmp(row[0], metric_unit[i]) == 0) {
195 xasprintf(&perf, "%s%s ", perf, perfdata(metric_unit[i], 171 xasprintf(&perf, "%s%s ", perf,
196 atol(row[1]), "", false, 0, false, 0, false, 0, false, 0)); 172 perfdata(metric_unit[i], atol(row[1]), "", false, 0, false, 0, false,
173 0, false, 0));
197 continue; 174 continue;
198 } 175 }
199 } 176 }
200 for(i = 0; i < LENGTH_METRIC_COUNTER; i++) { 177 for (int i = 0; i < LENGTH_METRIC_COUNTER; i++) {
201 if (strcmp(row[0], metric_counter[i]) == 0) { 178 if (strcmp(row[0], metric_counter[i]) == 0) {
202 xasprintf(&perf, "%s%s ", perf, perfdata(metric_counter[i], 179 xasprintf(&perf, "%s%s ", perf,
203 atol(row[1]), "c", false, 0, false, 0, false, 0, false, 0)); 180 perfdata(metric_counter[i], atol(row[1]), "c", false, 0, false, 0,
181 false, 0, false, 0));
204 continue; 182 continue;
205 } 183 }
206 } 184 }
207 } 185 }
208 /* remove trailing space */ 186 /* remove trailing space */
209 if (strlen(perf) > 0) 187 if (strlen(perf) > 0) {
210 perf[strlen(perf) - 1] = '\0'; 188 perf[strlen(perf) - 1] = '\0';
189 }
211 } 190 }
212 191
213 if(check_slave) { 192 char replica_result[REPLICA_RESULTSIZE] = {0};
214 /* check the slave status */ 193 if (config.check_replica) {
215 if (mysql_query (&mysql, "show slave status") != 0) { 194 // Detect which version we are, on older version
195 // "show slave status" should work, on newer ones
196 // "show replica status"
197 // But first we have to find out whether this is
198 // MySQL or MariaDB since the version numbering scheme
199 // is different
200 bool use_deprecated_slave_status = false;
201 const char *server_version = mysql_get_server_info(&mysql);
202 unsigned long server_verion_int = mysql_get_server_version(&mysql);
203 unsigned long major_version = server_verion_int / 10000;
204 unsigned long minor_version = (server_verion_int % 10000) / 100;
205 unsigned long patch_version = (server_verion_int % 100);
206 if (verbose) {
207 printf("Found MariaDB: %s, main version: %lu, minor version: %lu, patch version: %lu\n",
208 server_version, major_version, minor_version, patch_version);
209 }
210
211 if (strstr(server_version, "MariaDB") != NULL) {
212 // Looks like MariaDB, new commands should be available after 10.5.1
213 if (major_version < 10) {
214 use_deprecated_slave_status = true;
215 } else if (major_version == 10) {
216 if (minor_version < 5) {
217 use_deprecated_slave_status = true;
218 } else if (minor_version == 5 && patch_version < 1) {
219 use_deprecated_slave_status = true;
220 }
221 }
222 } else if (strstr(server_version, "MySQL") != NULL) {
223 // Looks like MySQL
224 if (major_version < 8) {
225 use_deprecated_slave_status = true;
226 } else if (major_version == 10 && minor_version < 4) {
227 use_deprecated_slave_status = true;
228 }
229 } else {
230 printf("Not a known sever implementation: %s\n", server_version);
231 exit(STATE_UNKNOWN);
232 }
233
234 char *replica_query = NULL;
235 if (use_deprecated_slave_status) {
236 replica_query = "show slave status";
237 } else {
238 replica_query = "show replica status";
239 }
240
241 /* check the replica status */
242 if (mysql_query(&mysql, replica_query) != 0) {
216 error = strdup(mysql_error(&mysql)); 243 error = strdup(mysql_error(&mysql));
217 mysql_close (&mysql); 244 mysql_close(&mysql);
218 die (STATE_CRITICAL, _("slave query error: %s\n"), error); 245 die(STATE_CRITICAL, _("replica query error: %s\n"), error);
219 } 246 }
220 247
221 /* store the result */ 248 /* store the result */
222 if ( (res = mysql_store_result (&mysql)) == NULL) { 249 if ((res = mysql_store_result(&mysql)) == NULL) {
223 error = strdup(mysql_error(&mysql)); 250 error = strdup(mysql_error(&mysql));
224 mysql_close (&mysql); 251 mysql_close(&mysql);
225 die (STATE_CRITICAL, _("slave store_result error: %s\n"), error); 252 die(STATE_CRITICAL, _("replica store_result error: %s\n"), error);
226 } 253 }
227 254
228 /* Check there is some data */ 255 /* Check there is some data */
229 if (mysql_num_rows(res) == 0) { 256 if (mysql_num_rows(res) == 0) {
230 mysql_close(&mysql); 257 mysql_close(&mysql);
231 die (STATE_WARNING, "%s\n", _("No slaves defined")); 258 die(STATE_WARNING, "%s\n", _("No replicas defined"));
232 } 259 }
233 260
234 /* fetch the first row */ 261 /* fetch the first row */
235 if ( (row = mysql_fetch_row (res)) == NULL) { 262 if ((row = mysql_fetch_row(res)) == NULL) {
236 error = strdup(mysql_error(&mysql)); 263 error = strdup(mysql_error(&mysql));
237 mysql_free_result (res); 264 mysql_free_result(res);
238 mysql_close (&mysql); 265 mysql_close(&mysql);
239 die (STATE_CRITICAL, _("slave fetch row error: %s\n"), error); 266 die(STATE_CRITICAL, _("replica fetch row error: %s\n"), error);
240 } 267 }
241 268
242 if (mysql_field_count (&mysql) == 12) { 269 if (mysql_field_count(&mysql) == 12) {
243 /* mysql 3.23.x */ 270 /* mysql 3.23.x */
244 snprintf (slaveresult, SLAVERESULTSIZE, _("Slave running: %s"), row[6]); 271 snprintf(replica_result, REPLICA_RESULTSIZE, _("Replica running: %s"), row[6]);
245 if (strcmp (row[6], "Yes") != 0) { 272 if (strcmp(row[6], "Yes") != 0) {
246 mysql_free_result (res); 273 mysql_free_result(res);
247 mysql_close (&mysql); 274 mysql_close(&mysql);
248 die (STATE_CRITICAL, "%s\n", slaveresult); 275 die(STATE_CRITICAL, "%s\n", replica_result);
249 } 276 }
250 277
251 } else { 278 } else {
252 /* mysql 4.x.x and mysql 5.x.x */ 279 /* mysql 4.x.x and mysql 5.x.x */
253 int slave_io_field = -1 , slave_sql_field = -1, seconds_behind_field = -1, i, num_fields; 280 int replica_io_field = -1;
254 MYSQL_FIELD* fields; 281 int replica_sql_field = -1;
255 282 int seconds_behind_field = -1;
283 int num_fields;
284 MYSQL_FIELD *fields;
256 num_fields = mysql_num_fields(res); 285 num_fields = mysql_num_fields(res);
257 fields = mysql_fetch_fields(res); 286 fields = mysql_fetch_fields(res);
258 for(i = 0; i < num_fields; i++) { 287 for (int i = 0; i < num_fields; i++) {
259 if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { 288 if (strcmp(fields[i].name, "Slave_IO_Running") == 0) {
260 slave_io_field = i; 289 replica_io_field = i;
261 continue; 290 continue;
262 } 291 }
263 if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) { 292 if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) {
264 slave_sql_field = i; 293 replica_sql_field = i;
265 continue; 294 continue;
266 } 295 }
267 if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) { 296 if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) {
@@ -270,175 +299,185 @@ main (int argc, char **argv)
270 } 299 }
271 } 300 }
272 301
273 /* Check if slave status is available */ 302 /* Check if replica status is available */
274 if ((slave_io_field < 0) || (slave_sql_field < 0) || (num_fields == 0)) { 303 if ((replica_io_field < 0) || (replica_sql_field < 0) || (num_fields == 0)) {
275 mysql_free_result (res); 304 mysql_free_result(res);
276 mysql_close (&mysql); 305 mysql_close(&mysql);
277 die (STATE_CRITICAL, "Slave status unavailable\n"); 306 die(STATE_CRITICAL, "Replica status unavailable\n");
278 } 307 }
279 308
280 /* Save slave status in slaveresult */ 309 /* Save replica status in replica_result */
281 snprintf (slaveresult, SLAVERESULTSIZE, "Slave IO: %s Slave SQL: %s Seconds Behind Master: %s", row[slave_io_field], row[slave_sql_field], seconds_behind_field!=-1?row[seconds_behind_field]:"Unknown"); 310 snprintf(replica_result, REPLICA_RESULTSIZE,
311 "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s",
312 row[replica_io_field], row[replica_sql_field],
313 seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown");
282 314
283 /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no mysqldump threads running */ 315 /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no
284 if (strcmp (row[slave_io_field], "Yes") != 0 || strcmp (row[slave_sql_field], "Yes") != 0) { 316 * mysqldump threads running */
317 if (strcmp(row[replica_io_field], "Yes") != 0 ||
318 strcmp(row[replica_sql_field], "Yes") != 0) {
285 MYSQL_RES *res_mysqldump; 319 MYSQL_RES *res_mysqldump;
286 MYSQL_ROW row_mysqldump; 320 MYSQL_ROW row_mysqldump;
287 unsigned int mysqldump_threads = 0; 321 unsigned int mysqldump_threads = 0;
288 322
289 if (mysql_query (&mysql, MYSQLDUMP_THREADS_QUERY) == 0) { 323 if (mysql_query(&mysql, MYSQLDUMP_THREADS_QUERY) == 0) {
290 /* store the result */ 324 /* store the result */
291 if ( (res_mysqldump = mysql_store_result (&mysql)) != NULL) { 325 if ((res_mysqldump = mysql_store_result(&mysql)) != NULL) {
292 if (mysql_num_rows(res_mysqldump) == 1) { 326 if (mysql_num_rows(res_mysqldump) == 1) {
293 if ( (row_mysqldump = mysql_fetch_row (res_mysqldump)) != NULL) { 327 if ((row_mysqldump = mysql_fetch_row(res_mysqldump)) != NULL) {
294 mysqldump_threads = atoi(row_mysqldump[0]); 328 mysqldump_threads = atoi(row_mysqldump[0]);
295 } 329 }
296 } 330 }
297 /* free the result */ 331 /* free the result */
298 mysql_free_result (res_mysqldump); 332 mysql_free_result(res_mysqldump);
299 } 333 }
300 mysql_close (&mysql); 334 mysql_close(&mysql);
301 } 335 }
302 if (mysqldump_threads == 0) { 336 if (mysqldump_threads == 0) {
303 die (STATE_CRITICAL, "%s\n", slaveresult); 337 die(STATE_CRITICAL, "%s\n", replica_result);
304 } else { 338 } else {
305 strncat(slaveresult, " Mysqldump: in progress", SLAVERESULTSIZE-1); 339 strncat(replica_result, " Mysqldump: in progress", REPLICA_RESULTSIZE - 1);
306 } 340 }
307 } 341 }
308 342
309 if (verbose >=3) { 343 if (verbose >= 3) {
310 if (seconds_behind_field == -1) { 344 if (seconds_behind_field == -1) {
311 printf("seconds_behind_field not found\n"); 345 printf("seconds_behind_field not found\n");
312 } else { 346 } else {
313 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]);
314 } 349 }
315 } 350 }
316 351
317 /* Check Seconds Behind against threshold */ 352 /* Check Seconds Behind against threshold */
318 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)) {
319 double value = atof(row[seconds_behind_field]); 355 double value = atof(row[seconds_behind_field]);
320 int status; 356 int status;
321 357
322 status = get_status(value, my_threshold); 358 status = get_status(value, config.my_threshold);
323 359
324 xasprintf (&perf, "%s %s", perf, fperfdata ("seconds behind master", value, "s", 360 xasprintf(&perf, "%s %s", perf,
325 true, (double) warning_time, 361 fperfdata("seconds behind master", value, "s", true,
326 true, (double) critical_time, 362 (double)config.warning_time, true, (double)config.critical_time,
327 false, 0, 363 false, 0, false, 0));
328 false, 0));
329 364
330 if (status == STATE_WARNING) { 365 if (status == STATE_WARNING) {
331 printf("SLOW_SLAVE %s: %s|%s\n", _("WARNING"), slaveresult, perf); 366 printf("SLOW_REPLICA %s: %s|%s\n", _("WARNING"), replica_result, perf);
332 exit(STATE_WARNING); 367 exit(STATE_WARNING);
333 } else if (status == STATE_CRITICAL) { 368 } else if (status == STATE_CRITICAL) {
334 printf("SLOW_SLAVE %s: %s|%s\n", _("CRITICAL"), slaveresult, perf); 369 printf("SLOW_REPLICA %s: %s|%s\n", _("CRITICAL"), replica_result, perf);
335 exit(STATE_CRITICAL); 370 exit(STATE_CRITICAL);
336 } 371 }
337 } 372 }
338 } 373 }
339 374
340 /* free the result */ 375 /* free the result */
341 mysql_free_result (res); 376 mysql_free_result(res);
342 } 377 }
343 378
344 /* close the connection */ 379 /* close the connection */
345 mysql_close (&mysql); 380 mysql_close(&mysql);
346 381
347 /* print out the result of stats */ 382 /* print out the result of stats */
348 if (check_slave) { 383 if (config.check_replica) {
349 printf ("%s %s|%s\n", result, slaveresult, perf); 384 printf("%s %s|%s\n", result, replica_result, perf);
350 } else { 385 } else {
351 printf ("%s|%s\n", result, perf); 386 printf("%s|%s\n", result, perf);
352 } 387 }
353 388
354 return STATE_OK; 389 return STATE_OK;
355} 390}
356 391
392#define CHECK_REPLICA_OPT CHAR_MAX + 1
357 393
358/* process command-line arguments */ 394/* process command-line arguments */
359int 395check_mysql_config_wrapper process_arguments(int argc, char **argv) {
360process_arguments (int argc, char **argv) 396 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
361{ 397 {"socket", required_argument, 0, 's'},
362 int c; 398 {"database", required_argument, 0, 'd'},
399 {"username", required_argument, 0, 'u'},
400 {"password", required_argument, 0, 'p'},
401 {"file", required_argument, 0, 'f'},
402 {"group", required_argument, 0, 'g'},
403 {"port", required_argument, 0, 'P'},
404 {"critical", required_argument, 0, 'c'},
405 {"warning", required_argument, 0, 'w'},
406 {"check-slave", no_argument, 0, 'S'},
407 {"check-replica", no_argument, 0, CHECK_REPLICA_OPT},
408 {"ignore-auth", no_argument, 0, 'n'},
409 {"verbose", no_argument, 0, 'v'},
410 {"version", no_argument, 0, 'V'},
411 {"help", no_argument, 0, 'h'},
412 {"ssl", no_argument, 0, 'l'},
413 {"ca-cert", optional_argument, 0, 'C'},
414 {"key", required_argument, 0, 'k'},
415 {"cert", required_argument, 0, 'a'},
416 {"ca-dir", required_argument, 0, 'D'},
417 {"ciphers", required_argument, 0, 'L'},
418 {0, 0, 0, 0}};
419
420 check_mysql_config_wrapper result = {
421 .errorcode = OK,
422 .config = check_mysql_config_init(),
423 };
424
425 if (argc < 1) {
426 result.errorcode = ERROR;
427 return result;
428 }
429
363 char *warning = NULL; 430 char *warning = NULL;
364 char *critical = NULL; 431 char *critical = NULL;
365 432
366 int option = 0; 433 int option = 0;
367 static struct option longopts[] = { 434 while (true) {
368 {"hostname", required_argument, 0, 'H'}, 435 int option_index =
369 {"socket", required_argument, 0, 's'}, 436 getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option);
370 {"database", required_argument, 0, 'd'},
371 {"username", required_argument, 0, 'u'},
372 {"password", required_argument, 0, 'p'},
373 {"file", required_argument, 0, 'f'},
374 {"group", required_argument, 0, 'g'},
375 {"port", required_argument, 0, 'P'},
376 {"critical", required_argument, 0, 'c'},
377 {"warning", required_argument, 0, 'w'},
378 {"check-slave", no_argument, 0, 'S'},
379 {"ignore-auth", no_argument, 0, 'n'},
380 {"verbose", no_argument, 0, 'v'},
381 {"version", no_argument, 0, 'V'},
382 {"help", no_argument, 0, 'h'},
383 {"ssl", no_argument, 0, 'l'},
384 {"ca-cert", optional_argument, 0, 'C'},
385 {"key", required_argument,0,'k'},
386 {"cert", required_argument,0,'a'},
387 {"ca-dir", required_argument, 0, 'D'},
388 {"ciphers", required_argument, 0, 'L'},
389 {0, 0, 0, 0}
390 };
391 437
392 if (argc < 1) 438 if (option_index == -1 || option_index == EOF) {
393 return ERROR;
394
395 while (1) {
396 c = getopt_long (argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option);
397
398 if (c == -1 || c == EOF)
399 break; 439 break;
440 }
400 441
401 switch (c) { 442 switch (option_index) {
402 case 'H': /* hostname */ 443 case 'H': /* hostname */
403 if (is_host (optarg)) { 444 if (is_host(optarg)) {
404 db_host = optarg; 445 result.config.db_host = optarg;
405 } 446 } else if (*optarg == '/') {
406 else if (*optarg == '/') { 447 result.config.db_socket = optarg;
407 db_socket = optarg; 448 } else {
408 } 449 usage2(_("Invalid hostname/address"), optarg);
409 else {
410 usage2 (_("Invalid hostname/address"), optarg);
411 } 450 }
412 break; 451 break;
413 case 's': /* socket */ 452 case 's': /* socket */
414 db_socket = optarg; 453 result.config.db_socket = optarg;
415 break; 454 break;
416 case 'd': /* database */ 455 case 'd': /* database */
417 db = optarg; 456 result.config.db = optarg;
418 break; 457 break;
419 case 'l': 458 case 'l':
420 ssl = true; 459 result.config.ssl = true;
421 break; 460 break;
422 case 'C': 461 case 'C':
423 ca_cert = optarg; 462 result.config.ca_cert = optarg;
424 break; 463 break;
425 case 'a': 464 case 'a':
426 cert = optarg; 465 result.config.cert = optarg;
427 break; 466 break;
428 case 'k': 467 case 'k':
429 key = optarg; 468 result.config.key = optarg;
430 break; 469 break;
431 case 'D': 470 case 'D':
432 ca_dir = optarg; 471 result.config.ca_dir = optarg;
433 break; 472 break;
434 case 'L': 473 case 'L':
435 ciphers = optarg; 474 result.config.ciphers = optarg;
436 break; 475 break;
437 case 'u': /* username */ 476 case 'u': /* username */
438 db_user = optarg; 477 result.config.db_user = optarg;
439 break; 478 break;
440 case 'p': /* authentication information: password */ 479 case 'p': /* authentication information: password */
441 db_pass = strdup(optarg); 480 result.config.db_pass = strdup(optarg);
442 481
443 /* Delete the password from process list */ 482 /* Delete the password from process list */
444 while (*optarg != '\0') { 483 while (*optarg != '\0') {
@@ -446,167 +485,166 @@ process_arguments (int argc, char **argv)
446 optarg++; 485 optarg++;
447 } 486 }
448 break; 487 break;
449 case 'f': /* client options file */ 488 case 'f': /* client options file */
450 opt_file = optarg; 489 result.config.opt_file = optarg;
451 break; 490 break;
452 case 'g': /* client options group */ 491 case 'g': /* client options group */
453 opt_group = optarg; 492 result.config.opt_group = optarg;
454 break; 493 break;
455 case 'P': /* critical time threshold */ 494 case 'P': /* critical time threshold */
456 db_port = atoi (optarg); 495 result.config.db_port = atoi(optarg);
457 break; 496 break;
458 case 'S': 497 case 'S':
459 check_slave = 1; /* check-slave */ 498 case CHECK_REPLICA_OPT:
499 result.config.check_replica = true; /* check-slave */
460 break; 500 break;
461 case 'n': 501 case 'n':
462 ignore_auth = 1; /* ignore-auth */ 502 result.config.ignore_auth = true; /* ignore-auth */
463 break; 503 break;
464 case 'w': 504 case 'w':
465 warning = optarg; 505 warning = optarg;
466 warning_time = strtod (warning, NULL); 506 result.config.warning_time = strtod(warning, NULL);
467 break; 507 break;
468 case 'c': 508 case 'c':
469 critical = optarg; 509 critical = optarg;
470 critical_time = strtod (critical, NULL); 510 result.config.critical_time = strtod(critical, NULL);
471 break; 511 break;
472 case 'V': /* version */ 512 case 'V': /* version */
473 print_revision (progname, NP_VERSION); 513 print_revision(progname, NP_VERSION);
474 exit (STATE_UNKNOWN); 514 exit(STATE_UNKNOWN);
475 case 'h': /* help */ 515 case 'h': /* help */
476 print_help (); 516 print_help();
477 exit (STATE_UNKNOWN); 517 exit(STATE_UNKNOWN);
478 case 'v': 518 case 'v':
479 verbose++; 519 verbose++;
480 break; 520 break;
481 case '?': /* help */ 521 case '?': /* help */
482 usage5 (); 522 usage5();
483 } 523 }
484 } 524 }
485 525
486 c = optind; 526 int index = optind;
487
488 set_thresholds(&my_threshold, warning, critical);
489 527
490 while ( argc > c ) { 528 set_thresholds(&result.config.my_threshold, warning, critical);
491 529
492 if (db_host == NULL) 530 while (argc > index) {
493 if (is_host (argv[c])) { 531 if (result.config.db_host == NULL) {
494 db_host = argv[c++]; 532 if (is_host(argv[index])) {
533 result.config.db_host = argv[index++];
534 } else {
535 usage2(_("Invalid hostname/address"), argv[index]);
495 } 536 }
496 else { 537 } else if (result.config.db_user == NULL) {
497 usage2 (_("Invalid hostname/address"), argv[c]); 538 result.config.db_user = argv[index++];
498 } 539 } else if (result.config.db_pass == NULL) {
499 else if (db_user == NULL) 540 result.config.db_pass = argv[index++];
500 db_user = argv[c++]; 541 } else if (result.config.db == NULL) {
501 else if (db_pass == NULL) 542 result.config.db = argv[index++];
502 db_pass = argv[c++]; 543 } else if (is_intnonneg(argv[index])) {
503 else if (db == NULL) 544 result.config.db_port = atoi(argv[index++]);
504 db = argv[c++]; 545 } else {
505 else if (is_intnonneg (argv[c]))
506 db_port = atoi (argv[c++]);
507 else
508 break; 546 break;
547 }
509 } 548 }
510 549
511 return validate_arguments (); 550 return validate_arguments(result);
512} 551}
513 552
553check_mysql_config_wrapper validate_arguments(check_mysql_config_wrapper config_wrapper) {
554 if (config_wrapper.config.db_user == NULL) {
555 config_wrapper.config.db_user = strdup("");
556 }
514 557
515int 558 if (config_wrapper.config.db_host == NULL) {
516validate_arguments (void) 559 config_wrapper.config.db_host = strdup("");
517{ 560 }
518 if (db_user == NULL)
519 db_user = strdup("");
520
521 if (db_host == NULL)
522 db_host = strdup("");
523 561
524 if (db == NULL) 562 if (config_wrapper.config.db == NULL) {
525 db = strdup(""); 563 config_wrapper.config.db = strdup("");
564 }
526 565
527 return OK; 566 return config_wrapper;
528} 567}
529 568
530 569void print_help(void) {
531void
532print_help (void)
533{
534 char *myport; 570 char *myport;
535 xasprintf (&myport, "%d", MYSQL_PORT); 571 xasprintf(&myport, "%d", MYSQL_PORT);
536 572
537 print_revision (progname, NP_VERSION); 573 print_revision(progname, NP_VERSION);
538 574
539 printf (_(COPYRIGHT), copyright, email); 575 printf(_(COPYRIGHT), copyright, email);
540 576
541 printf ("%s\n", _("This program tests connections to a MySQL server")); 577 printf("%s\n", _("This program tests connections to a MySQL server"));
542 578
543 printf ("\n\n"); 579 printf("\n\n");
544 580
545 print_usage (); 581 print_usage();
546 582
547 printf (UT_HELP_VRSN); 583 printf(UT_HELP_VRSN);
548 printf (UT_EXTRA_OPTS); 584 printf(UT_EXTRA_OPTS);
549 585
550 printf (UT_HOST_PORT, 'P', myport); 586 printf(UT_HOST_PORT, 'P', myport);
551 printf (" %s\n", "-n, --ignore-auth"); 587 printf(" %s\n", "-n, --ignore-auth");
552 printf (" %s\n", _("Ignore authentication failure and check for mysql connectivity only")); 588 printf(" %s\n", _("Ignore authentication failure and check for mysql connectivity only"));
553 589
554 printf (" %s\n", "-s, --socket=STRING"); 590 printf(" %s\n", "-s, --socket=STRING");
555 printf (" %s\n", _("Use the specified socket (has no effect if -H is used)")); 591 printf(" %s\n", _("Use the specified socket (has no effect if -H is used)"));
556 592
557 printf (" %s\n", "-d, --database=STRING"); 593 printf(" %s\n", "-d, --database=STRING");
558 printf (" %s\n", _("Check database with indicated name")); 594 printf(" %s\n", _("Check database with indicated name"));
559 printf (" %s\n", "-f, --file=STRING"); 595 printf(" %s\n", "-f, --file=STRING");
560 printf (" %s\n", _("Read from the specified client options file")); 596 printf(" %s\n", _("Read from the specified client options file"));
561 printf (" %s\n", "-g, --group=STRING"); 597 printf(" %s\n", "-g, --group=STRING");
562 printf (" %s\n", _("Use a client options group")); 598 printf(" %s\n", _("Use a client options group"));
563 printf (" %s\n", "-u, --username=STRING"); 599 printf(" %s\n", "-u, --username=STRING");
564 printf (" %s\n", _("Connect using the indicated username")); 600 printf(" %s\n", _("Connect using the indicated username"));
565 printf (" %s\n", "-p, --password=STRING"); 601 printf(" %s\n", "-p, --password=STRING");
566 printf (" %s\n", _("Use the indicated password to authenticate the connection")); 602 printf(" %s\n", _("Use the indicated password to authenticate the connection"));
567 printf (" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); 603 printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!"));
568 printf (" %s\n", _("Your clear-text password could be visible as a process table entry")); 604 printf(" %s\n", _("Your clear-text password could be visible as a process table entry"));
569 printf (" %s\n", "-S, --check-slave"); 605 printf(" %s\n", "-S, --check-slave");
570 printf (" %s\n", _("Check if the slave thread is running properly.")); 606 printf(" %s\n", _("Check if the slave thread is running properly. This option is deprecated "
571 printf (" %s\n", "-w, --warning"); 607 "in favour of check-replica, which does the same"));
572 printf (" %s\n", _("Exit with WARNING status if slave server is more than INTEGER seconds")); 608 printf(" %s\n", "--check-replica");
573 printf (" %s\n", _("behind master")); 609 printf(" %s\n", _("Check if the replica thread is running properly."));
574 printf (" %s\n", "-c, --critical"); 610 printf(" %s\n", "-w, --warning");
575 printf (" %s\n", _("Exit with CRITICAL status if slave server is more then INTEGER seconds")); 611 printf(" %s\n",
576 printf (" %s\n", _("behind master")); 612 _("Exit with WARNING status if replica server is more than INTEGER seconds"));
577 printf (" %s\n", "-l, --ssl"); 613 printf(" %s\n", _("behind master"));
578 printf (" %s\n", _("Use ssl encryption")); 614 printf(" %s\n", "-c, --critical");
579 printf (" %s\n", "-C, --ca-cert=STRING"); 615 printf(" %s\n",
580 printf (" %s\n", _("Path to CA signing the cert")); 616 _("Exit with CRITICAL status if replica server is more then INTEGER seconds"));
581 printf (" %s\n", "-a, --cert=STRING"); 617 printf(" %s\n", _("behind master"));
582 printf (" %s\n", _("Path to SSL certificate")); 618 printf(" %s\n", "-l, --ssl");
583 printf (" %s\n", "-k, --key=STRING"); 619 printf(" %s\n", _("Use ssl encryption"));
584 printf (" %s\n", _("Path to private SSL key")); 620 printf(" %s\n", "-C, --ca-cert=STRING");
585 printf (" %s\n", "-D, --ca-dir=STRING"); 621 printf(" %s\n", _("Path to CA signing the cert"));
586 printf (" %s\n", _("Path to CA directory")); 622 printf(" %s\n", "-a, --cert=STRING");
587 printf (" %s\n", "-L, --ciphers=STRING"); 623 printf(" %s\n", _("Path to SSL certificate"));
588 printf (" %s\n", _("List of valid SSL ciphers")); 624 printf(" %s\n", "-k, --key=STRING");
589 625 printf(" %s\n", _("Path to private SSL key"));
590 626 printf(" %s\n", "-D, --ca-dir=STRING");
591 printf ("\n"); 627 printf(" %s\n", _("Path to CA directory"));
592 printf (" %s\n", _("There are no required arguments. By default, the local database is checked")); 628 printf(" %s\n", "-L, --ciphers=STRING");
593 printf (" %s\n", _("using the default unix socket. You can force TCP on localhost by using an")); 629 printf(" %s\n", _("List of valid SSL ciphers"));
594 printf (" %s\n", _("IP address or FQDN ('localhost' will use the socket as well).")); 630
595 631 printf("\n");
596 printf ("\n"); 632 printf(" %s\n",
597 printf ("%s\n", _("Notes:")); 633 _("There are no required arguments. By default, the local database is checked"));
598 printf (" %s\n", _("You must specify -p with an empty string to force an empty password,")); 634 printf(" %s\n", _("using the default unix socket. You can force TCP on localhost by using an"));
599 printf (" %s\n", _("overriding any my.cnf settings.")); 635 printf(" %s\n", _("IP address or FQDN ('localhost' will use the socket as well)."));
600 636
601 printf (UT_SUPPORT); 637 printf("\n");
638 printf("%s\n", _("Notes:"));
639 printf(" %s\n", _("You must specify -p with an empty string to force an empty password,"));
640 printf(" %s\n", _("overriding any my.cnf settings."));
641
642 printf(UT_SUPPORT);
602} 643}
603 644
604 645void print_usage(void) {
605void 646 printf("%s\n", _("Usage:"));
606print_usage (void) 647 printf(" %s [-d database] [-H host] [-P port] [-s socket]\n", progname);
607{ 648 printf(" [-u user] [-p password] [-S] [-l] [-a cert] [-k key]\n");
608 printf ("%s\n", _("Usage:")); 649 printf(" [-C ca-cert] [-D ca-dir] [-L ciphers] [-f optfile] [-g group]\n");
609 printf (" %s [-d database] [-H host] [-P port] [-s socket]\n",progname);
610 printf (" [-u user] [-p password] [-S] [-l] [-a cert] [-k key]\n");
611 printf (" [-C ca-cert] [-D ca-dir] [-L ciphers] [-f optfile] [-g group]\n");
612} 650}
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 842b7a2f..c7e84deb 100644
--- a/plugins/check_mysql_query.c
+++ b/plugins/check_mysql_query.c
@@ -1,157 +1,153 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_mysql_query plugin 3 * Monitoring check_mysql_query plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006-2009 Monitoring Plugins Development Team 6 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
7* Original code from check_mysql, copyright 1999 Didi Rieder 7 * Original code from check_mysql, copyright 1999 Didi Rieder
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_mysql_query plugin 11 * This file contains the check_mysql_query plugin
12* 12 *
13* This plugin is for running arbitrary SQL and checking the results 13 * This plugin is for running arbitrary SQL and checking the results
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_mysql_query"; 32const char *progname = "check_mysql_query";
33const char *copyright = "1999-2007"; 33const char *copyright = "1999-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "utils.h" 37#include "utils.h"
38#include "utils_base.h" 38#include "utils_base.h"
39#include "netutils.h" 39#include "netutils.h"
40#include "check_mysql_query.d/config.h"
40 41
41#include <mysql.h> 42#include <mysql.h>
42#include <errmsg.h> 43#include <errmsg.h>
43 44
44char *db_user = NULL; 45typedef struct {
45char *db_host = NULL; 46 int errorcode;
46char *db_socket = NULL; 47 check_mysql_query_config config;
47char *db_pass = NULL; 48} check_mysql_query_config_wrapper;
48char *db = NULL; 49static check_mysql_query_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
49char *opt_file = NULL; 50static check_mysql_query_config_wrapper
50char *opt_group = NULL; 51 validate_arguments(check_mysql_query_config_wrapper /*config_wrapper*/);
51unsigned int db_port = MYSQL_PORT; 52static void print_help(void);
53void print_usage(void);
52 54
53int process_arguments (int, char **); 55static int verbose = 0;
54int validate_arguments (void);
55void print_help (void);
56void print_usage (void);
57 56
58char *sql_query = NULL; 57int main(int argc, char **argv) {
59int verbose = 0; 58 setlocale(LC_ALL, "");
60thresholds *my_thresholds = NULL; 59 bindtextdomain(PACKAGE, LOCALEDIR);
61 60 textdomain(PACKAGE);
62
63int
64main (int argc, char **argv)
65{
66
67 MYSQL mysql;
68 MYSQL_RES *res;
69 MYSQL_ROW row;
70
71 double value;
72 char *error = NULL;
73 int status;
74
75 setlocale (LC_ALL, "");
76 bindtextdomain (PACKAGE, LOCALEDIR);
77 textdomain (PACKAGE);
78 61
79 /* Parse extra opts if any */ 62 /* Parse extra opts if any */
80 argv=np_extra_opts (&argc, argv, progname); 63 argv = np_extra_opts(&argc, argv, progname);
81 64
82 if (process_arguments (argc, argv) == ERROR) 65 check_mysql_query_config_wrapper tmp_config = process_arguments(argc, argv);
83 usage4 (_("Could not parse arguments")); 66 if (tmp_config.errorcode == ERROR) {
67 usage4(_("Could not parse arguments"));
68 }
84 69
70 const check_mysql_query_config config = tmp_config.config;
71
72 MYSQL mysql;
85 /* initialize mysql */ 73 /* initialize mysql */
86 mysql_init (&mysql); 74 mysql_init(&mysql);
87 75
88 if (opt_file != NULL) 76 if (config.opt_file != NULL) {
89 mysql_options(&mysql,MYSQL_READ_DEFAULT_FILE,opt_file); 77 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file);
78 }
90 79
91 if (opt_group != NULL) 80 if (config.opt_group != NULL) {
92 mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,opt_group); 81 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group);
93 else 82 } else {
94 mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"client"); 83 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client");
84 }
95 85
96 /* establish a connection to the server and error checking */ 86 /* establish a connection to the server and error checking */
97 if (!mysql_real_connect(&mysql,db_host,db_user,db_pass,db,db_port,db_socket,0)) { 87 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db,
98 if (mysql_errno (&mysql) == CR_UNKNOWN_HOST) 88 config.db_port, config.db_socket, 0)) {
99 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 89 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
100 else if (mysql_errno (&mysql) == CR_VERSION_ERROR) 90 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
101 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 91 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
102 else if (mysql_errno (&mysql) == CR_OUT_OF_MEMORY) 92 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
103 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 93 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
104 else if (mysql_errno (&mysql) == CR_IPSOCK_ERROR) 94 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
105 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 95 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
106 else if (mysql_errno (&mysql) == CR_SOCKET_CREATE_ERROR) 96 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
107 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error (&mysql)); 97 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
108 else 98 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
109 die (STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error (&mysql)); 99 } else {
100 die(STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error(&mysql));
101 }
110 } 102 }
111 103
112 if (mysql_query (&mysql, sql_query) != 0) { 104 char *error = NULL;
105 if (mysql_query(&mysql, config.sql_query) != 0) {
113 error = strdup(mysql_error(&mysql)); 106 error = strdup(mysql_error(&mysql));
114 mysql_close (&mysql); 107 mysql_close(&mysql);
115 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);
116 } 109 }
117 110
111 MYSQL_RES *res;
118 /* store the result */ 112 /* store the result */
119 if ( (res = mysql_store_result (&mysql)) == NULL) { 113 if ((res = mysql_store_result(&mysql)) == NULL) {
120 error = strdup(mysql_error(&mysql)); 114 error = strdup(mysql_error(&mysql));
121 mysql_close (&mysql); 115 mysql_close(&mysql);
122 die (STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error); 116 die(STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error);
123 } 117 }
124 118
125 /* Check there is some data */ 119 /* Check there is some data */
126 if (mysql_num_rows(res) == 0) { 120 if (mysql_num_rows(res) == 0) {
127 mysql_close(&mysql); 121 mysql_close(&mysql);
128 die (STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned")); 122 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned"));
129 } 123 }
130 124
125 MYSQL_ROW row;
131 /* fetch the first row */ 126 /* fetch the first row */
132 if ( (row = mysql_fetch_row (res)) == NULL) { 127 if ((row = mysql_fetch_row(res)) == NULL) {
133 error = strdup(mysql_error(&mysql)); 128 error = strdup(mysql_error(&mysql));
134 mysql_free_result (res); 129 mysql_free_result(res);
135 mysql_close (&mysql); 130 mysql_close(&mysql);
136 die (STATE_CRITICAL, "QUERY %s: Fetch row error - %s\n", _("CRITICAL"), error); 131 die(STATE_CRITICAL, "QUERY %s: Fetch row error - %s\n", _("CRITICAL"), error);
137 } 132 }
138 133
139 if (! is_numeric(row[0])) { 134 if (!is_numeric(row[0])) {
140 die (STATE_CRITICAL, "QUERY %s: %s - '%s'\n", _("CRITICAL"), _("Is not a numeric"), row[0]); 135 die(STATE_CRITICAL, "QUERY %s: %s - '%s'\n", _("CRITICAL"), _("Is not a numeric"), row[0]);
141 } 136 }
142 137
143 value = strtod(row[0], NULL); 138 double value = strtod(row[0], NULL);
144 139
145 /* free the result */ 140 /* free the result */
146 mysql_free_result (res); 141 mysql_free_result(res);
147 142
148 /* close the connection */ 143 /* close the connection */
149 mysql_close (&mysql); 144 mysql_close(&mysql);
150 145
151 if (verbose >= 3) 146 if (verbose >= 3) {
152 printf("mysql result: %f\n", value); 147 printf("mysql result: %f\n", value);
148 }
153 149
154 status = get_status(value, my_thresholds); 150 int status = get_status(value, config.my_thresholds);
155 151
156 if (status == STATE_OK) { 152 if (status == STATE_OK) {
157 printf("QUERY %s: ", _("OK")); 153 printf("QUERY %s: ", _("OK"));
@@ -160,75 +156,75 @@ main (int argc, char **argv)
160 } else if (status == STATE_CRITICAL) { 156 } else if (status == STATE_CRITICAL) {
161 printf("QUERY %s: ", _("CRITICAL")); 157 printf("QUERY %s: ", _("CRITICAL"));
162 } 158 }
163 printf(_("'%s' returned %f | %s"), sql_query, value, 159 printf(_("'%s' returned %f | %s"), config.sql_query, value,
164 fperfdata("result", value, "", 160 fperfdata("result", value, "", config.my_thresholds->warning,
165 my_thresholds->warning?true:false, my_thresholds->warning?my_thresholds->warning->end:0, 161 config.my_thresholds->warning ? config.my_thresholds->warning->end : 0,
166 my_thresholds->critical?true:false, my_thresholds->critical?my_thresholds->critical->end:0, 162 config.my_thresholds->critical,
167 false, 0, 163 config.my_thresholds->critical ? config.my_thresholds->critical->end : 0,
168 false, 0) 164 false, 0, false, 0));
169 );
170 printf("\n"); 165 printf("\n");
171 166
172 return status; 167 return status;
173} 168}
174 169
175
176/* process command-line arguments */ 170/* process command-line arguments */
177int 171check_mysql_query_config_wrapper process_arguments(int argc, char **argv) {
178process_arguments (int argc, char **argv) 172 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
179{ 173 {"socket", required_argument, 0, 's'},
180 int c; 174 {"database", required_argument, 0, 'd'},
181 char *warning = NULL; 175 {"username", required_argument, 0, 'u'},
182 char *critical = NULL; 176 {"password", required_argument, 0, 'p'},
183 177 {"file", required_argument, 0, 'f'},
184 int option = 0; 178 {"group", required_argument, 0, 'g'},
185 static struct option longopts[] = { 179 {"port", required_argument, 0, 'P'},
186 {"hostname", required_argument, 0, 'H'}, 180 {"verbose", no_argument, 0, 'v'},
187 {"socket", required_argument, 0, 's'}, 181 {"version", no_argument, 0, 'V'},
188 {"database", required_argument, 0, 'd'}, 182 {"help", no_argument, 0, 'h'},
189 {"username", required_argument, 0, 'u'}, 183 {"query", required_argument, 0, 'q'},
190 {"password", required_argument, 0, 'p'}, 184 {"warning", required_argument, 0, 'w'},
191 {"file", required_argument, 0, 'f'}, 185 {"critical", required_argument, 0, 'c'},
192 {"group", required_argument, 0, 'g'}, 186 {0, 0, 0, 0}};
193 {"port", required_argument, 0, 'P'}, 187
194 {"verbose", no_argument, 0, 'v'}, 188 check_mysql_query_config_wrapper result = {
195 {"version", no_argument, 0, 'V'}, 189 .errorcode = OK,
196 {"help", no_argument, 0, 'h'}, 190 .config = check_mysql_query_config_init(),
197 {"query", required_argument, 0, 'q'},
198 {"warning", required_argument, 0, 'w'},
199 {"critical", required_argument, 0, 'c'},
200 {0, 0, 0, 0}
201 }; 191 };
202 192
203 if (argc < 1) 193 if (argc < 1) {
204 return ERROR; 194 result.errorcode = ERROR;
195 return result;
196 }
205 197
206 while (1) { 198 char *warning = NULL;
207 c = getopt_long (argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option); 199 char *critical = NULL;
208 200
209 if (c == -1 || c == EOF) 201 while (true) {
202 int option = 0;
203 int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option);
204
205 if (option_char == -1 || option_char == EOF) {
210 break; 206 break;
207 }
211 208
212 switch (c) { 209 switch (option_char) {
213 case 'H': /* hostname */ 210 case 'H': /* hostname */
214 if (is_host (optarg)) { 211 if (is_host(optarg)) {
215 db_host = optarg; 212 result.config.db_host = optarg;
216 } 213 } else {
217 else { 214 usage2(_("Invalid hostname/address"), optarg);
218 usage2 (_("Invalid hostname/address"), optarg);
219 } 215 }
220 break; 216 break;
221 case 's': /* socket */ 217 case 's': /* socket */
222 db_socket = optarg; 218 result.config.db_socket = optarg;
223 break; 219 break;
224 case 'd': /* database */ 220 case 'd': /* database */
225 db = optarg; 221 result.config.db = optarg;
226 break; 222 break;
227 case 'u': /* username */ 223 case 'u': /* username */
228 db_user = optarg; 224 result.config.db_user = optarg;
229 break; 225 break;
230 case 'p': /* authentication information: password */ 226 case 'p': /* authentication information: password */
231 db_pass = strdup(optarg); 227 result.config.db_pass = strdup(optarg);
232 228
233 /* Delete the password from process list */ 229 /* Delete the password from process list */
234 while (*optarg != '\0') { 230 while (*optarg != '\0') {
@@ -236,26 +232,26 @@ process_arguments (int argc, char **argv)
236 optarg++; 232 optarg++;
237 } 233 }
238 break; 234 break;
239 case 'f': /* client options file */ 235 case 'f': /* client options file */
240 opt_file = optarg; 236 result.config.opt_file = optarg;
241 break; 237 break;
242 case 'g': /* client options group */ 238 case 'g': /* client options group */
243 opt_group = optarg; 239 result.config.opt_group = optarg;
244 break; 240 break;
245 case 'P': /* critical time threshold */ 241 case 'P': /* critical time threshold */
246 db_port = atoi (optarg); 242 result.config.db_port = atoi(optarg);
247 break; 243 break;
248 case 'v': 244 case 'v':
249 verbose++; 245 verbose++;
250 break; 246 break;
251 case 'V': /* version */ 247 case 'V': /* version */
252 print_revision (progname, NP_VERSION); 248 print_revision(progname, NP_VERSION);
253 exit (STATE_UNKNOWN); 249 exit(STATE_UNKNOWN);
254 case 'h': /* help */ 250 case 'h': /* help */
255 print_help (); 251 print_help();
256 exit (STATE_UNKNOWN); 252 exit(STATE_UNKNOWN);
257 case 'q': 253 case 'q':
258 xasprintf(&sql_query, "%s", optarg); 254 xasprintf(&result.config.sql_query, "%s", optarg);
259 break; 255 break;
260 case 'w': 256 case 'w':
261 warning = optarg; 257 warning = optarg;
@@ -263,92 +259,86 @@ process_arguments (int argc, char **argv)
263 case 'c': 259 case 'c':
264 critical = optarg; 260 critical = optarg;
265 break; 261 break;
266 case '?': /* help */ 262 case '?': /* help */
267 usage5 (); 263 usage5();
268 } 264 }
269 } 265 }
270 266
271 c = optind; 267 set_thresholds(&result.config.my_thresholds, warning, critical);
272 268
273 set_thresholds(&my_thresholds, warning, critical); 269 return validate_arguments(result);
274
275 return validate_arguments ();
276} 270}
277 271
278 272check_mysql_query_config_wrapper
279int 273validate_arguments(check_mysql_query_config_wrapper config_wrapper) {
280validate_arguments (void) 274 if (config_wrapper.config.sql_query == NULL) {
281{
282 if (sql_query == NULL)
283 usage("Must specify a SQL query to run"); 275 usage("Must specify a SQL query to run");
276 }
284 277
285 if (db_user == NULL) 278 if (config_wrapper.config.db_user == NULL) {
286 db_user = strdup(""); 279 config_wrapper.config.db_user = strdup("");
280 }
287 281
288 if (db_host == NULL) 282 if (config_wrapper.config.db_host == NULL) {
289 db_host = strdup(""); 283 config_wrapper.config.db_host = strdup("");
284 }
290 285
291 if (db == NULL) 286 if (config_wrapper.config.db == NULL) {
292 db = strdup(""); 287 config_wrapper.config.db = strdup("");
288 }
293 289
294 return OK; 290 return config_wrapper;
295} 291}
296 292
297 293void print_help(void) {
298void
299print_help (void)
300{
301 char *myport; 294 char *myport;
302 xasprintf (&myport, "%d", MYSQL_PORT); 295 xasprintf(&myport, "%d", MYSQL_PORT);
303 296
304 print_revision (progname, NP_VERSION); 297 print_revision(progname, NP_VERSION);
305 298
306 printf (_(COPYRIGHT), copyright, email); 299 printf(_(COPYRIGHT), copyright, email);
307 300
308 printf ("%s\n", _("This program checks a query result against threshold levels")); 301 printf("%s\n", _("This program checks a query result against threshold levels"));
309 302
310 printf ("\n\n"); 303 printf("\n\n");
311 304
312 print_usage (); 305 print_usage();
313 306
314 printf (UT_HELP_VRSN); 307 printf(UT_HELP_VRSN);
315 printf (UT_EXTRA_OPTS); 308 printf(UT_EXTRA_OPTS);
316 printf (" -q, --query=STRING\n"); 309 printf(" -q, --query=STRING\n");
317 printf (" %s\n", _("SQL query to run. Only first column in first row will be read")); 310 printf(" %s\n", _("SQL query to run. Only first column in first row will be read"));
318 printf (UT_WARN_CRIT_RANGE); 311 printf(UT_WARN_CRIT_RANGE);
319 printf (UT_HOST_PORT, 'P', myport); 312 printf(UT_HOST_PORT, 'P', myport);
320 printf (" %s\n", "-s, --socket=STRING"); 313 printf(" %s\n", "-s, --socket=STRING");
321 printf (" %s\n", _("Use the specified socket (has no effect if -H is used)")); 314 printf(" %s\n", _("Use the specified socket (has no effect if -H is used)"));
322 printf (" -d, --database=STRING\n"); 315 printf(" -d, --database=STRING\n");
323 printf (" %s\n", _("Database to check")); 316 printf(" %s\n", _("Database to check"));
324 printf (" %s\n", "-f, --file=STRING"); 317 printf(" %s\n", "-f, --file=STRING");
325 printf (" %s\n", _("Read from the specified client options file")); 318 printf(" %s\n", _("Read from the specified client options file"));
326 printf (" %s\n", "-g, --group=STRING"); 319 printf(" %s\n", "-g, --group=STRING");
327 printf (" %s\n", _("Use a client options group")); 320 printf(" %s\n", _("Use a client options group"));
328 printf (" -u, --username=STRING\n"); 321 printf(" -u, --username=STRING\n");
329 printf (" %s\n", _("Username to login with")); 322 printf(" %s\n", _("Username to login with"));
330 printf (" -p, --password=STRING\n"); 323 printf(" -p, --password=STRING\n");
331 printf (" %s\n", _("Password to login with")); 324 printf(" %s\n", _("Password to login with"));
332 printf (" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); 325 printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!"));
333 printf (" %s\n", _("Your clear-text password could be visible as a process table entry")); 326 printf(" %s\n", _("Your clear-text password could be visible as a process table entry"));
334
335 printf ("\n");
336 printf (" %s\n", _("A query is required. The result from the query should be numeric."));
337 printf (" %s\n", _("For extra security, create a user with minimal access."));
338
339 printf ("\n");
340 printf ("%s\n", _("Notes:"));
341 printf (" %s\n", _("You must specify -p with an empty string to force an empty password,"));
342 printf (" %s\n", _("overriding any my.cnf settings."));
343
344 printf (UT_SUPPORT);
345}
346 327
328 printf("\n");
329 printf(" %s\n", _("A query is required. The result from the query should be numeric."));
330 printf(" %s\n", _("For extra security, create a user with minimal access."));
331
332 printf("\n");
333 printf("%s\n", _("Notes:"));
334 printf(" %s\n", _("You must specify -p with an empty string to force an empty password,"));
335 printf(" %s\n", _("overriding any my.cnf settings."));
336
337 printf(UT_SUPPORT);
338}
347 339
348void 340void print_usage(void) {
349print_usage (void) 341 printf("%s\n", _("Usage:"));
350{ 342 printf(" %s -q SQL_query [-w warn] [-c crit] [-H host] [-P port] [-s socket]\n", progname);
351 printf ("%s\n", _("Usage:")); 343 printf(" [-d database] [-u user] [-p password] [-f optfile] [-g group]\n");
352 printf (" %s -q SQL_query [-w warn] [-c crit] [-H host] [-P port] [-s socket]\n",progname);
353 printf (" [-d database] [-u user] [-p password] [-f optfile] [-g group]\n");
354} 344}
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 40d68f03..a46dc1ed 100644
--- a/plugins/check_nagios.c
+++ b/plugins/check_nagios.c
@@ -1,325 +1,317 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_nagios plugin 3 * Monitoring check_nagios plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_nagios plugin 10 * This file contains the check_nagios plugin
11* 11 *
12* This plugin checks the status of the Nagios process on the local machine. 12 * This plugin checks the status of the Nagios process on the local machine.
13* The plugin will check to make sure the Nagios status log is no older than 13 * The plugin will check to make sure the Nagios status log is no older than
14* the number of minutes specified by the expires option. 14 * the number of minutes specified by the expires option.
15* It also checks the process table for a process matching the command 15 * It also checks the process table for a process matching the command
16* argument. 16 * argument.
17* 17 *
18* 18 *
19* This program is free software: you can redistribute it and/or modify 19 * This program is free software: you can redistribute it and/or modify
20* it under the terms of the GNU General Public License as published by 20 * it under the terms of the GNU General Public License as published by
21* the Free Software Foundation, either version 3 of the License, or 21 * the Free Software Foundation, either version 3 of the License, or
22* (at your option) any later version. 22 * (at your option) any later version.
23* 23 *
24* This program is distributed in the hope that it will be useful, 24 * This program is distributed in the hope that it will be useful,
25* but WITHOUT ANY WARRANTY; without even the implied warranty of 25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27* GNU General Public License for more details. 27 * GNU General Public License for more details.
28* 28 *
29* You should have received a copy of the GNU General Public License 29 * You should have received a copy of the GNU General Public License
30* along with this program. If not, see <http://www.gnu.org/licenses/>. 30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31* 31 *
32* 32 *
33*****************************************************************************/ 33 *****************************************************************************/
34 34
35const char *progname = "check_nagios"; 35const char *progname = "check_nagios";
36const char *copyright = "1999-2007"; 36const char *copyright = "1999-2024";
37const char *email = "devel@monitoring-plugins.org"; 37const char *email = "devel@monitoring-plugins.org";
38 38
39#include "common.h" 39#include "common.h"
40#include "runcmd.h" 40#include "runcmd.h"
41#include "utils.h" 41#include "utils.h"
42#include "states.h"
43#include "check_nagios.d/config.h"
42 44
43int process_arguments (int, char **); 45typedef struct {
44void print_help (void); 46 int errorcode;
45void print_usage (void); 47 check_nagios_config config;
48} check_nagios_config_wrapper;
49static check_nagios_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50static void print_help(void);
51void print_usage(void);
46 52
47char *status_log = NULL; 53static int verbose = 0;
48char *process_string = NULL;
49int expire_minutes = 0;
50 54
51int verbose = 0; 55int main(int argc, char **argv) {
52 56 setlocale(LC_ALL, "");
53int 57 bindtextdomain(PACKAGE, LOCALEDIR);
54main (int argc, char **argv) 58 textdomain(PACKAGE);
55{
56 int result = STATE_UNKNOWN;
57 char input_buffer[MAX_INPUT_BUFFER];
58 unsigned long latest_entry_time = 0L;
59 unsigned long temp_entry_time = 0L;
60 int proc_entries = 0;
61 time_t current_time;
62 char *temp_ptr;
63 FILE *fp;
64 int procuid = 0;
65 int procpid = 0;
66 int procppid = 0;
67 int procvsz = 0;
68 int procrss = 0;
69 float procpcpu = 0;
70 char procstat[8];
71#ifdef PS_USES_PROCETIME
72 char procetime[MAX_INPUT_BUFFER];
73#endif /* PS_USES_PROCETIME */
74 char procprog[MAX_INPUT_BUFFER];
75 char *procargs;
76 int pos, cols;
77 int expected_cols = PS_COLS - 1;
78 const char *zombie = "Z";
79 char *temp_string;
80 output chld_out, chld_err;
81 size_t i;
82
83 setlocale (LC_ALL, "");
84 bindtextdomain (PACKAGE, LOCALEDIR);
85 textdomain (PACKAGE);
86 59
87 /* Parse extra opts if any */ 60 /* Parse extra opts if any */
88 argv=np_extra_opts (&argc, argv, progname); 61 argv = np_extra_opts(&argc, argv, progname);
62
63 check_nagios_config_wrapper tmp_config = process_arguments(argc, argv);
89 64
90 if (process_arguments (argc, argv) == ERROR) 65 if (tmp_config.errorcode == ERROR) {
91 usage_va(_("Could not parse arguments")); 66 usage_va(_("Could not parse arguments"));
67 }
68
69 const check_nagios_config config = tmp_config.config;
92 70
93 /* Set signal handling and alarm timeout */ 71 /* Set signal handling and alarm timeout */
94 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 72 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
95 usage_va(_("Cannot catch SIGALRM")); 73 usage_va(_("Cannot catch SIGALRM"));
96 } 74 }
97 75
98 /* handle timeouts gracefully... */ 76 /* handle timeouts gracefully... */
99 alarm (timeout_interval); 77 alarm(timeout_interval);
100 78
101 /* open the status log */ 79 /* open the status log */
102 fp = fopen (status_log, "r"); 80 FILE *log_file = fopen(config.status_log, "r");
103 if (fp == NULL) { 81 if (log_file == NULL) {
104 die (STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot open status log for reading!")); 82 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot open status log for reading!"));
105 } 83 }
106 84
85 unsigned long latest_entry_time = 0L;
86 unsigned long temp_entry_time = 0L;
87 char input_buffer[MAX_INPUT_BUFFER];
88 char *temp_ptr;
107 /* get the date/time of the last item updated in the log */ 89 /* get the date/time of the last item updated in the log */
108 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) { 90 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, log_file)) {
109 if ((temp_ptr = strstr (input_buffer, "created=")) != NULL) { 91 if ((temp_ptr = strstr(input_buffer, "created=")) != NULL) {
110 temp_entry_time = strtoul (temp_ptr + 8, NULL, 10); 92 temp_entry_time = strtoul(temp_ptr + 8, NULL, 10);
111 latest_entry_time = temp_entry_time; 93 latest_entry_time = temp_entry_time;
112 break; 94 break;
113 } else if ((temp_ptr = strtok (input_buffer, "]")) != NULL) { 95 }
114 temp_entry_time = strtoul (temp_ptr + 1, NULL, 10); 96 if ((temp_ptr = strtok(input_buffer, "]")) != NULL) {
115 if (temp_entry_time > latest_entry_time) 97 temp_entry_time = strtoul(temp_ptr + 1, NULL, 10);
98 if (temp_entry_time > latest_entry_time) {
116 latest_entry_time = temp_entry_time; 99 latest_entry_time = temp_entry_time;
100 }
117 } 101 }
118 } 102 }
119 fclose (fp); 103 fclose(log_file);
120 104
121 if (verbose >= 2) 105 if (verbose >= 2) {
122 printf("command: %s\n", PS_COMMAND); 106 printf("command: %s\n", PS_COMMAND);
107 }
123 108
124 /* run the command to check for the Nagios process.. */ 109 /* run the command to check for the Nagios process.. */
125 if((result = np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0)) != 0) 110 mp_state_enum result = STATE_UNKNOWN;
111 output chld_out;
112 output chld_err;
113 if ((result = np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0)) != 0) {
126 result = STATE_WARNING; 114 result = STATE_WARNING;
115 }
127 116
117 int procuid = 0;
118 int procpid = 0;
119 int procppid = 0;
120 int procvsz = 0;
121 int procrss = 0;
122 int proc_entries = 0;
123 float procpcpu = 0;
124 char procstat[8];
125 char procprog[MAX_INPUT_BUFFER];
126 char *procargs;
127#ifdef PS_USES_PROCETIME
128 char procetime[MAX_INPUT_BUFFER];
129#endif /* PS_USES_PROCETIME */
130 int pos;
131 int expected_cols = PS_COLS - 1;
132 const char *zombie = "Z";
128 /* count the number of matching Nagios processes... */ 133 /* count the number of matching Nagios processes... */
129 for(i = 0; i < chld_out.lines; i++) { 134 for (size_t i = 0; i < chld_out.lines; i++) {
130 cols = sscanf (chld_out.line[i], PS_FORMAT, PS_VARLIST); 135 int cols = sscanf(chld_out.line[i], PS_FORMAT, PS_VARLIST);
131 /* Zombie processes do not give a procprog command */ 136 /* Zombie processes do not give a procprog command */
132 if ( cols == (expected_cols - 1) && strstr(procstat, zombie) ) { 137 if (cols == (expected_cols - 1) && strstr(procstat, zombie)) {
133 cols = expected_cols; 138 cols = expected_cols;
134 /* Set some value for procargs for the strip command further below 139 /* Set some value for procargs for the strip command further below
135 * Seen to be a problem on some Solaris 7 and 8 systems */ 140 * Seen to be a problem on some Solaris 7 and 8 systems */
136 chld_out.line[i][pos] = '\n'; 141 chld_out.line[i][pos] = '\n';
137 chld_out.line[i][pos+1] = 0x0; 142 chld_out.line[i][pos + 1] = 0x0;
138 } 143 }
139 if ( cols >= expected_cols ) { 144 if (cols >= expected_cols) {
140 xasprintf (&procargs, "%s", chld_out.line[i] + pos); 145 xasprintf(&procargs, "%s", chld_out.line[i] + pos);
141 strip (procargs); 146 strip(procargs);
142 147
143 /* Some ps return full pathname for command. This removes path */ 148 /* Some ps return full pathname for command. This removes path */
144 temp_string = strtok ((char *)procprog, "/"); 149 char *temp_string = strtok((char *)procprog, "/");
145 while (temp_string) { 150 while (temp_string) {
146 strcpy(procprog, temp_string); 151 strcpy(procprog, temp_string);
147 temp_string = strtok (NULL, "/"); 152 temp_string = strtok(NULL, "/");
148 } 153 }
149 154
150 /* May get empty procargs */ 155 /* May get empty procargs */
151 if (!strstr(procargs, argv[0]) && strstr(procargs, process_string) && strcmp(procargs,"")) { 156 if (!strstr(procargs, argv[0]) && strstr(procargs, config.process_string) && strcmp(procargs, "")) {
152 proc_entries++; 157 proc_entries++;
153 if (verbose >= 2) { 158 if (verbose >= 2) {
154 printf (_("Found process: %s %s\n"), procprog, procargs); 159 printf(_("Found process: %s %s\n"), procprog, procargs);
155 } 160 }
156 } 161 }
157 } 162 }
158 } 163 }
159 164
160 /* If we get anything on stderr, at least set warning */ 165 /* If we get anything on stderr, at least set warning */
161 if(chld_err.buflen) 166 if (chld_err.buflen) {
162 result = max_state (result, STATE_WARNING); 167 result = max_state(result, STATE_WARNING);
168 }
163 169
164 /* reset the alarm handler */ 170 /* reset the alarm handler */
165 alarm (0); 171 alarm(0);
166 172
167 if (proc_entries == 0) { 173 if (proc_entries == 0) {
168 die (STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Could not locate a running Nagios process!")); 174 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Could not locate a running Nagios process!"));
169 } 175 }
170 176
171 if (latest_entry_time == 0L) { 177 if (latest_entry_time == 0L) {
172 die (STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot parse Nagios log file for valid time")); 178 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot parse Nagios log file for valid time"));
173 } 179 }
174 180
175 time (&current_time); 181 time_t current_time;
176 if ((int)(current_time - latest_entry_time) > (expire_minutes * 60)) { 182 time(&current_time);
183 if ((int)(current_time - latest_entry_time) > (config.expire_minutes * 60)) {
177 result = STATE_WARNING; 184 result = STATE_WARNING;
178 } else { 185 } else {
179 result = STATE_OK; 186 result = STATE_OK;
180 } 187 }
181 188
182 printf ("NAGIOS %s: ", (result == STATE_OK) ? _("OK") : _("WARNING")); 189 printf("NAGIOS %s: ", (result == STATE_OK) ? _("OK") : _("WARNING"));
183 printf (ngettext ("%d process", "%d processes", proc_entries), proc_entries); 190 printf(ngettext("%d process", "%d processes", proc_entries), proc_entries);
184 printf (", "); 191 printf(", ");
185 printf ( 192 printf(ngettext("status log updated %d second ago", "status log updated %d seconds ago", (int)(current_time - latest_entry_time)),
186 ngettext ("status log updated %d second ago", 193 (int)(current_time - latest_entry_time));
187 "status log updated %d seconds ago", 194 printf("\n");
188 (int) (current_time - latest_entry_time) ),
189 (int) (current_time - latest_entry_time) );
190 printf ("\n");
191 195
192 return result; 196 exit(result);
193} 197}
194 198
195
196
197/* process command-line arguments */ 199/* process command-line arguments */
198int 200check_nagios_config_wrapper process_arguments(int argc, char **argv) {
199process_arguments (int argc, char **argv) 201 static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'},
200{ 202 {"command", required_argument, 0, 'C'}, {"timeout", optional_argument, 0, 't'},
201 int c; 203 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
202 204 {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}};
203 int option = 0; 205
204 static struct option longopts[] = { 206 check_nagios_config_wrapper result = {
205 {"filename", required_argument, 0, 'F'}, 207 .errorcode = OK,
206 {"expires", required_argument, 0, 'e'}, 208 .config = check_nagios_config_init(),
207 {"command", required_argument, 0, 'C'},
208 {"timeout", optional_argument, 0, 't'},
209 {"version", no_argument, 0, 'V'},
210 {"help", no_argument, 0, 'h'},
211 {"verbose", no_argument, 0, 'v'},
212 {0, 0, 0, 0}
213 }; 209 };
210 if (argc < 2) {
211 result.errorcode = ERROR;
212 return result;
213 }
214 214
215 if (argc < 2) 215 if (!is_option(argv[1])) {
216 return ERROR; 216 result.config.status_log = argv[1];
217 217 if (is_intnonneg(argv[2])) {
218 if (!is_option (argv[1])) { 218 result.config.expire_minutes = atoi(argv[2]);
219 status_log = argv[1]; 219 } else {
220 if (is_intnonneg (argv[2])) 220 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n"));
221 expire_minutes = atoi (argv[2]); 221 }
222 else 222 result.config.process_string = argv[3];
223 die (STATE_UNKNOWN, 223 return result;
224 _("Expiration time must be an integer (seconds)\n"));
225 process_string = argv[3];
226 return OK;
227 } 224 }
228 225
229 while (1) { 226 int option = 0;
230 c = getopt_long (argc, argv, "+hVvF:C:e:t:", longopts, &option); 227 while (true) {
228 int option_index = getopt_long(argc, argv, "+hVvF:C:e:t:", longopts, &option);
231 229
232 if (c == -1 || c == EOF || c == 1) 230 if (option_index == -1 || option_index == EOF || option_index == 1) {
233 break; 231 break;
232 }
234 233
235 switch (c) { 234 switch (option_index) {
236 case 'h': /* help */ 235 case 'h': /* help */
237 print_help (); 236 print_help();
238 exit (STATE_UNKNOWN); 237 exit(STATE_UNKNOWN);
239 case 'V': /* version */ 238 case 'V': /* version */
240 print_revision (progname, NP_VERSION); 239 print_revision(progname, NP_VERSION);
241 exit (STATE_UNKNOWN); 240 exit(STATE_UNKNOWN);
242 case 'F': /* status log */ 241 case 'F': /* status log */
243 status_log = optarg; 242 result.config.status_log = optarg;
244 break; 243 break;
245 case 'C': /* command */ 244 case 'C': /* command */
246 process_string = optarg; 245 result.config.process_string = optarg;
247 break; 246 break;
248 case 'e': /* expiry time */ 247 case 'e': /* expiry time */
249 if (is_intnonneg (optarg)) 248 if (is_intnonneg(optarg)) {
250 expire_minutes = atoi (optarg); 249 result.config.expire_minutes = atoi(optarg);
251 else 250 } else {
252 die (STATE_UNKNOWN, 251 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n"));
253 _("Expiration time must be an integer (seconds)\n")); 252 }
254 break; 253 break;
255 case 't': /* timeout */ 254 case 't': /* timeout */
256 if (is_intnonneg (optarg)) 255 if (is_intnonneg(optarg)) {
257 timeout_interval = atoi (optarg); 256 timeout_interval = atoi(optarg);
258 else 257 } else {
259 die (STATE_UNKNOWN, 258 die(STATE_UNKNOWN, _("Timeout must be an integer (seconds)\n"));
260 _("Timeout must be an integer (seconds)\n")); 259 }
261 break; 260 break;
262 case 'v': 261 case 'v':
263 verbose++; 262 verbose++;
264 break; 263 break;
265 default: /* print short usage_va statement if args not parsable */ 264 default: /* print short usage_va statement if args not parsable */
266 usage5(); 265 usage5();
267 } 266 }
268 } 267 }
269 268
269 if (result.config.status_log == NULL) {
270 die(STATE_UNKNOWN, _("You must provide the status_log\n"));
271 }
270 272
271 if (status_log == NULL) 273 if (result.config.process_string == NULL) {
272 die (STATE_UNKNOWN, _("You must provide the status_log\n")); 274 die(STATE_UNKNOWN, _("You must provide a process string\n"));
273 275 }
274 if (process_string == NULL)
275 die (STATE_UNKNOWN, _("You must provide a process string\n"));
276 276
277 return OK; 277 return result;
278} 278}
279 279
280void print_help(void) {
281 print_revision(progname, NP_VERSION);
280 282
283 printf(_(COPYRIGHT), copyright, email);
281 284
282void 285 printf("%s\n", _("This plugin checks the status of the Nagios process on the local machine"));
283print_help (void) 286 printf("%s\n", _("The plugin will check to make sure the Nagios status log is no older than"));
284{ 287 printf("%s\n", _("the number of minutes specified by the expires option."));
285 print_revision (progname, NP_VERSION); 288 printf("%s\n", _("It also checks the process table for a process matching the command argument."));
286 289
287 printf (_(COPYRIGHT), copyright, email); 290 printf("\n\n");
288 291
289 printf ("%s\n", _("This plugin checks the status of the Nagios process on the local machine")); 292 print_usage();
290 printf ("%s\n", _("The plugin will check to make sure the Nagios status log is no older than"));
291 printf ("%s\n", _("the number of minutes specified by the expires option."));
292 printf ("%s\n", _("It also checks the process table for a process matching the command argument."));
293 293
294 printf ("\n\n"); 294 printf(UT_HELP_VRSN);
295 printf(UT_EXTRA_OPTS);
295 296
296 print_usage (); 297 printf(" %s\n", "-F, --filename=FILE");
298 printf(" %s\n", _("Name of the log file to check"));
299 printf(" %s\n", "-e, --expires=INTEGER");
300 printf(" %s\n", _("Minutes aging after which logfile is considered stale"));
301 printf(" %s\n", "-C, --command=STRING");
302 printf(" %s\n", _("Substring to search for in process arguments"));
303 printf(" %s\n", "-t, --timeout=INTEGER");
304 printf(" %s\n", _("Timeout for the plugin in seconds"));
305 printf(UT_VERBOSE);
297 306
298 printf (UT_HELP_VRSN); 307 printf("\n");
299 printf (UT_EXTRA_OPTS); 308 printf("%s\n", _("Examples:"));
309 printf(" %s\n", "check_nagios -t 20 -e 5 -F /usr/local/nagios/var/status.log -C /usr/local/nagios/bin/nagios");
300 310
301 printf (" %s\n", "-F, --filename=FILE"); 311 printf(UT_SUPPORT);
302 printf (" %s\n", _("Name of the log file to check"));
303 printf (" %s\n", "-e, --expires=INTEGER");
304 printf (" %s\n", _("Minutes aging after which logfile is considered stale"));
305 printf (" %s\n", "-C, --command=STRING");
306 printf (" %s\n", _("Substring to search for in process arguments"));
307 printf (" %s\n", "-t, --timeout=INTEGER");
308 printf (" %s\n", _("Timeout for the plugin in seconds"));
309 printf (UT_VERBOSE);
310
311 printf ("\n");
312 printf ("%s\n", _("Examples:"));
313 printf (" %s\n", "check_nagios -t 20 -e 5 -F /usr/local/nagios/var/status.log -C /usr/local/nagios/bin/nagios");
314
315 printf (UT_SUPPORT);
316} 312}
317 313
318 314void print_usage(void) {
319 315 printf("%s\n", _("Usage:"));
320void 316 printf("%s -F <status log file> -t <timeout_seconds> -e <expire_minutes> -C <process_string>\n", progname);
321print_usage (void)
322{
323 printf ("%s\n", _("Usage:"));
324 printf ("%s -F <status log file> -t <timeout_seconds> -e <expire_minutes> -C <process_string>\n", progname);
325} 317}
diff --git a/plugins/check_nagios.d/config.h b/plugins/check_nagios.d/config.h
new file mode 100644
index 00000000..efe139f9
--- /dev/null
+++ b/plugins/check_nagios.d/config.h
@@ -0,0 +1,19 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6typedef struct {
7 char *status_log;
8 char *process_string;
9 int expire_minutes;
10} check_nagios_config;
11
12check_nagios_config check_nagios_config_init() {
13 check_nagios_config tmp = {
14 .status_log = NULL,
15 .process_string = NULL,
16 .expire_minutes = 0,
17 };
18 return tmp;
19}
diff --git a/plugins/check_nt.c b/plugins/check_nt.c
index 19c050de..35ca92cd 100644
--- a/plugins/check_nt.c
+++ b/plugins/check_nt.c
@@ -1,336 +1,313 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_nt plugin 3 * Monitoring check_nt plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2002 Yves Rubin (rubiyz@yahoo.com) 6 * Copyright (c) 2000-2002 Yves Rubin (rubiyz@yahoo.com)
7* Copyright (c) 2003-2007 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_nt plugin 11 * This file contains the check_nt plugin
12* 12 *
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
20* it under the terms of the GNU General Public License as published by 20 * it under the terms of the GNU General Public License as published by
21* the Free Software Foundation, either version 3 of the License, or 21 * the Free Software Foundation, either version 3 of the License, or
22* (at your option) any later version. 22 * (at your option) any later version.
23* 23 *
24* This program is distributed in the hope that it will be useful, 24 * This program is distributed in the hope that it will be useful,
25* but WITHOUT ANY WARRANTY; without even the implied warranty of 25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27* GNU General Public License for more details. 27 * GNU General Public License for more details.
28* 28 *
29* You should have received a copy of the GNU General Public License 29 * You should have received a copy of the GNU General Public License
30* along with this program. If not, see <http://www.gnu.org/licenses/>. 30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31* 31 *
32* 32 *
33*****************************************************************************/ 33 *****************************************************************************/
34 34
35const char *progname = "check_nt"; 35const char *progname = "check_nt";
36const char *copyright = "2000-2007"; 36const char *copyright = "2000-2024";
37const char *email = "devel@monitoring-plugins.org"; 37const char *email = "devel@monitoring-plugins.org";
38 38
39#include "common.h" 39#include "common.h"
40#include "netutils.h" 40#include "netutils.h"
41#include "utils.h" 41#include "utils.h"
42 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
62char *server_address=NULL; 48static char recv_buffer[MAX_INPUT_BUFFER];
63char *volume_name=NULL;
64int server_port=PORT;
65char *value_list=NULL;
66char *req_password=NULL;
67unsigned long lvalue_list[MAX_VALUE_LIST];
68unsigned long warning_value=0L;
69unsigned long critical_value=0L;
70bool check_warning_value=false;
71bool check_critical_value=false;
72enum checkvars vars_to_check = CHECK_NONE;
73bool show_all = false;
74
75char recv_buffer[MAX_INPUT_BUFFER];
76
77void fetch_data (const char* address, int port, const char* sendb);
78int process_arguments(int, char **);
79void preparelist(char *string);
80bool strtoularray(unsigned long *array, char *string, const char *delim);
81void print_help(void);
82void print_usage(void);
83 49
84int main(int argc, char **argv){ 50static void fetch_data(const char *address, int port, const char *sendb);
85 51
86/* should be int result = STATE_UNKNOWN; */ 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*/);
87 57
88 int return_code = STATE_UNKNOWN; 58static void preparelist(char *string);
89 char *send_buffer=NULL; 59static bool strtoularray(unsigned long *array, char *string, const char *delim);
90 char *output_message=NULL; 60static void print_help(void);
91 char *perfdata=NULL; 61void print_usage(void);
92 char *temp_string=NULL; 62
93 char *temp_string_perf=NULL; 63int main(int argc, char **argv) {
94 char *description=NULL,*counter_unit = NULL; 64 setlocale(LC_ALL, "");
95 char *minval = NULL, *maxval = NULL, *errcvt = NULL; 65 bindtextdomain(PACKAGE, LOCALEDIR);
96 char *fds=NULL, *tds=NULL; 66 textdomain(PACKAGE);
97 char *numstr;
98
99 double total_disk_space=0;
100 double free_disk_space=0;
101 double percent_used_space=0;
102 double warning_used_space=0;
103 double critical_used_space=0;
104 double mem_commitLimit=0;
105 double mem_commitByte=0;
106 double fminval = 0, fmaxval = 0;
107 unsigned long utilization;
108 unsigned long uptime;
109 unsigned long age_in_minutes;
110 double counter_value = 0.0;
111 int offset=0;
112 int updays=0;
113 int uphours=0;
114 int upminutes=0;
115
116 bool isPercent = false;
117 bool allRight = false;
118
119 setlocale (LC_ALL, "");
120 bindtextdomain (PACKAGE, LOCALEDIR);
121 textdomain (PACKAGE);
122 67
123 /* Parse extra opts if any */ 68 /* Parse extra opts if any */
124 argv=np_extra_opts (&argc, argv, progname); 69 argv = np_extra_opts(&argc, argv, progname);
70
71 check_nt_config_wrapper tmp_config = process_arguments(argc, argv);
72 if (tmp_config.errorcode == ERROR) {
73 usage4(_("Could not parse arguments"));
74 }
125 75
126 if(process_arguments(argc,argv) == ERROR) 76 const check_nt_config config = tmp_config.config;
127 usage4 (_("Could not parse arguments"));
128 77
129 /* initialize alarm signal handling */ 78 /* initialize alarm signal handling */
130 signal(SIGALRM,socket_timeout_alarm_handler); 79 signal(SIGALRM, socket_timeout_alarm_handler);
131 80
132 /* set socket timeout */ 81 /* set socket timeout */
133 alarm(socket_timeout); 82 alarm(socket_timeout);
134 83
135 switch (vars_to_check) { 84 int return_code = STATE_UNKNOWN;
136 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) {
137 case CHECK_CLIENTVERSION: 95 case CHECK_CLIENTVERSION:
138 96 xasprintf(&send_buffer, "%s&1", config.req_password);
139 xasprintf(&send_buffer, "%s&1", req_password); 97 fetch_data(config.server_address, config.server_port, send_buffer);
140 fetch_data (server_address, server_port, send_buffer); 98 if (config.value_list != NULL && strcmp(recv_buffer, config.value_list) != 0) {
141 if (value_list != NULL && strcmp(recv_buffer, value_list) != 0) { 99 xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"),
142 xasprintf (&output_message, _("Wrong client version - running: %s, required: %s"), recv_buffer, value_list); 100 recv_buffer, config.value_list);
143 return_code = STATE_WARNING; 101 return_code = STATE_WARNING;
144 } else { 102 } else {
145 xasprintf (&output_message, "%s", recv_buffer); 103 xasprintf(&output_message, "%s", recv_buffer);
146 return_code = STATE_OK; 104 return_code = STATE_OK;
147 } 105 }
148 break; 106 break;
149
150 case CHECK_CPULOAD: 107 case CHECK_CPULOAD:
151 108 if (config.value_list == NULL) {
152 if (value_list==NULL) 109 output_message = strdup(_("missing -l parameters"));
153 output_message = strdup (_("missing -l parameters")); 110 } else if (!strtoularray(lvalue_list, config.value_list, ",")) {
154 else if (! strtoularray(lvalue_list,value_list,",")) 111 output_message = strdup(_("wrong -l parameter."));
155 output_message = strdup (_("wrong -l parameter.")); 112 } else {
156 else {
157 /* -l parameters is present with only integers */ 113 /* -l parameters is present with only integers */
158 return_code=STATE_OK; 114 return_code = STATE_OK;
159 temp_string = strdup (_("CPU Load")); 115 temp_string = strdup(_("CPU Load"));
160 temp_string_perf = strdup (" "); 116 temp_string_perf = strdup(" ");
161 117
162 /* loop until one of the parameters is wrong or not present */ 118 /* loop until one of the parameters is wrong or not present */
163 while (lvalue_list[0+offset]> (unsigned long)0 && 119 int offset = 0;
164 lvalue_list[0+offset]<=(unsigned long)17280 && 120 while (lvalue_list[0 + offset] > (unsigned long)0 &&
165 lvalue_list[1+offset]> (unsigned long)0 && 121 lvalue_list[0 + offset] <= (unsigned long)17280 &&
166 lvalue_list[1+offset]<=(unsigned long)100 && 122 lvalue_list[1 + offset] > (unsigned long)0 &&
167 lvalue_list[2+offset]> (unsigned long)0 && 123 lvalue_list[1 + offset] <= (unsigned long)100 &&
168 lvalue_list[2+offset]<=(unsigned long)100) { 124 lvalue_list[2 + offset] > (unsigned long)0 &&
125 lvalue_list[2 + offset] <= (unsigned long)100) {
169 126
170 /* Send request and retrieve data */ 127 /* Send request and retrieve data */
171 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]);
172 fetch_data (server_address, server_port, send_buffer); 129 fetch_data(config.server_address, config.server_port, send_buffer);
173 130
174 utilization=strtoul(recv_buffer,NULL,10); 131 unsigned long utilization = strtoul(recv_buffer, NULL, 10);
175 132
176 /* 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 */
177 if(utilization >= lvalue_list[2+offset]) 134 if (utilization >= lvalue_list[2 + offset]) {
178 return_code=STATE_CRITICAL; 135 return_code = STATE_CRITICAL;
179 else if(utilization >= lvalue_list[1+offset] && return_code<STATE_WARNING) 136 } else if (utilization >= lvalue_list[1 + offset] && return_code < STATE_WARNING) {
180 return_code=STATE_WARNING; 137 return_code = STATE_WARNING;
181 138 }
182 xasprintf(&output_message,_(" %lu%% (%lu min average)"), utilization, lvalue_list[0+offset]); 139
183 xasprintf(&temp_string,"%s%s",temp_string,output_message); 140 xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization,
184 xasprintf(&perfdata,_(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"), lvalue_list[0+offset], utilization, 141 lvalue_list[0 + offset]);
185 lvalue_list[1+offset], lvalue_list[2+offset]); 142 xasprintf(&temp_string, "%s%s", temp_string, output_message);
186 xasprintf(&temp_string_perf,"%s%s",temp_string_perf,perfdata); 143 xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"),
187 offset+=3; /* move across the array */ 144 lvalue_list[0 + offset], utilization, lvalue_list[1 + offset],
145 lvalue_list[2 + offset]);
146 xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata);
147 offset += 3; /* move across the array */
188 } 148 }
189 149
190 if (strlen(temp_string)>10) { /* we had at least one loop */ 150 if (strlen(temp_string) > 10) { /* we had at least one loop */
191 output_message = strdup (temp_string); 151 output_message = strdup(temp_string);
192 perfdata = temp_string_perf; 152 perfdata = temp_string_perf;
193 } else 153 } else {
194 output_message = strdup (_("not enough values for -l parameters")); 154 output_message = strdup(_("not enough values for -l parameters"));
155 }
195 } 156 }
196 break; 157 break;
197 158 case CHECK_UPTIME: {
198 case CHECK_UPTIME: 159 char *tmp_value_list = config.value_list;
199 160 if (config.value_list == NULL) {
200 if (value_list == NULL) { 161 tmp_value_list = "minutes";
201 value_list = "minutes";
202 } 162 }
203 if (strncmp(value_list, "seconds", strlen("seconds") + 1 ) && 163 if (strncmp(tmp_value_list, "seconds", strlen("seconds") + 1) &&
204 strncmp(value_list, "minutes", strlen("minutes") + 1) && 164 strncmp(tmp_value_list, "minutes", strlen("minutes") + 1) &&
205 strncmp(value_list, "hours", strlen("hours") + 1) && 165 strncmp(config.value_list, "hours", strlen("hours") + 1) &&
206 strncmp(value_list, "days", strlen("days") + 1)) { 166 strncmp(tmp_value_list, "days", strlen("days") + 1)) {
207 167
208 output_message = strdup (_("wrong -l argument")); 168 output_message = strdup(_("wrong -l argument"));
209 } else { 169 } else {
210 xasprintf(&send_buffer, "%s&3", req_password); 170 xasprintf(&send_buffer, "%s&3", config.req_password);
211 fetch_data (server_address, server_port, send_buffer); 171 fetch_data(config.server_address, config.server_port, send_buffer);
212 uptime=strtoul(recv_buffer,NULL,10); 172 unsigned long uptime = strtoul(recv_buffer, NULL, 10);
213 updays = uptime / 86400; 173 int updays = uptime / 86400;
214 uphours = (uptime % 86400) / 3600; 174 int uphours = (uptime % 86400) / 3600;
215 upminutes = ((uptime % 86400) % 3600) / 60; 175 int upminutes = ((uptime % 86400) % 3600) / 60;
216 176
217 if (!strncmp(value_list, "minutes", strlen("minutes"))) 177 if (!strncmp(tmp_value_list, "minutes", strlen("minutes"))) {
218 uptime = uptime / 60; 178 uptime = uptime / 60;
219 else if (!strncmp(value_list, "hours", strlen("hours"))) 179 } else if (!strncmp(tmp_value_list, "hours", strlen("hours"))) {
220 uptime = uptime / 3600; 180 uptime = uptime / 3600;
221 else if (!strncmp(value_list, "days", strlen("days"))) 181 } else if (!strncmp(tmp_value_list, "days", strlen("days"))) {
222 uptime = uptime / 86400; 182 uptime = uptime / 86400;
183 }
223 /* else uptime in seconds, nothing to do */ 184 /* else uptime in seconds, nothing to do */
224 185
225 xasprintf(&output_message,_("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"),updays, uphours, upminutes, uptime); 186 xasprintf(&output_message,
187 _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays,
188 uphours, upminutes, uptime);
226 189
227 if (check_critical_value && uptime <= critical_value) 190 if (config.check_critical_value && uptime <= config.critical_value) {
228 return_code=STATE_CRITICAL; 191 return_code = STATE_CRITICAL;
229 else if (check_warning_value && uptime <= warning_value) 192 } else if (config.check_warning_value && uptime <= config.warning_value) {
230 return_code=STATE_WARNING; 193 return_code = STATE_WARNING;
231 else 194 } else {
232 return_code=STATE_OK; 195 return_code = STATE_OK;
196 }
233 } 197 }
234 break; 198 } break;
235
236 case CHECK_USEDDISKSPACE: 199 case CHECK_USEDDISKSPACE:
200 if (config.value_list == NULL) {
201 output_message = strdup(_("missing -l parameters"));
202 } else if (strlen(config.value_list) != 1) {
203 output_message = strdup(_("wrong -l argument"));
204 } else {
205 xasprintf(&send_buffer, "%s&4&%s", config.req_password, config.value_list);
206 fetch_data(config.server_address, config.server_port, send_buffer);
207 char *fds = strtok(recv_buffer, "&");
208 char *tds = strtok(NULL, "&");
209 double total_disk_space = 0;
210 double free_disk_space = 0;
211 if (fds != NULL) {
212 free_disk_space = atof(fds);
213 }
214 if (tds != NULL) {
215 total_disk_space = atof(tds);
216 }
237 217
238 if (value_list==NULL) 218 if (total_disk_space > 0 && free_disk_space >= 0) {
239 output_message = strdup (_("missing -l parameters")); 219 double percent_used_space =
240 else if (strlen(value_list)!=1) 220 ((total_disk_space - free_disk_space) / total_disk_space) * 100;
241 output_message = strdup (_("wrong -l argument")); 221 double warning_used_space = ((float)config.warning_value / 100) * total_disk_space;
242 else { 222 double critical_used_space =
243 xasprintf(&send_buffer,"%s&4&%s", req_password, value_list); 223 ((float)config.critical_value / 100) * total_disk_space;
244 fetch_data (server_address, server_port, send_buffer); 224
245 fds=strtok(recv_buffer,"&"); 225 xasprintf(
246 tds=strtok(NULL,"&"); 226 &temp_string,
247 if(fds!=NULL) 227 _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"),
248 free_disk_space=atof(fds); 228 config.value_list, total_disk_space / 1073741824,
249 if(tds!=NULL) 229 (total_disk_space - free_disk_space) / 1073741824, percent_used_space,
250 total_disk_space=atof(tds); 230 free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100);
251 231 xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"),
252 if (total_disk_space>0 && free_disk_space>=0) { 232 config.value_list, (total_disk_space - free_disk_space) / 1073741824,
253 percent_used_space = ((total_disk_space - free_disk_space) / total_disk_space) * 100; 233 warning_used_space / 1073741824, critical_used_space / 1073741824,
254 warning_used_space = ((float)warning_value / 100) * total_disk_space; 234 total_disk_space / 1073741824);
255 critical_used_space = ((float)critical_value / 100) * total_disk_space; 235
256 236 if (config.check_critical_value && percent_used_space >= config.critical_value) {
257 xasprintf(&temp_string,_("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"), 237 return_code = STATE_CRITICAL;
258 value_list, total_disk_space / 1073741824, (total_disk_space - free_disk_space) / 1073741824, 238 } else if (config.check_warning_value &&
259 percent_used_space, free_disk_space / 1073741824, (free_disk_space / total_disk_space)*100); 239 percent_used_space >= config.warning_value) {
260 xasprintf(&temp_string_perf,_("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"), value_list, 240 return_code = STATE_WARNING;
261 (total_disk_space - free_disk_space) / 1073741824, warning_used_space / 1073741824, 241 } else {
262 critical_used_space / 1073741824, total_disk_space / 1073741824); 242 return_code = STATE_OK;
263 243 }
264 if(check_critical_value && percent_used_space >= critical_value) 244
265 return_code=STATE_CRITICAL; 245 output_message = strdup(temp_string);
266 else if (check_warning_value && percent_used_space >= warning_value)
267 return_code=STATE_WARNING;
268 else
269 return_code=STATE_OK;
270
271 output_message = strdup (temp_string);
272 perfdata = temp_string_perf; 246 perfdata = temp_string_perf;
273 } else { 247 } else {
274 output_message = strdup (_("Free disk space : Invalid drive")); 248 output_message = strdup(_("Free disk space : Invalid drive"));
275 return_code=STATE_UNKNOWN; 249 return_code = STATE_UNKNOWN;
276 } 250 }
277 } 251 }
278 break; 252 break;
279
280 case CHECK_SERVICESTATE: 253 case CHECK_SERVICESTATE:
281 case CHECK_PROCSTATE: 254 case CHECK_PROCSTATE:
282 255 if (config.value_list == NULL) {
283 if (value_list==NULL) 256 output_message = strdup(_("No service/process specified"));
284 output_message = strdup (_("No service/process specified")); 257 } else {
285 else { 258 preparelist(
286 preparelist(value_list); /* replace , between services with & to send the request */ 259 config.value_list); /* replace , between services with & to send the request */
287 xasprintf(&send_buffer,"%s&%u&%s&%s", req_password,(vars_to_check==CHECK_SERVICESTATE)?5:6, 260 xasprintf(&send_buffer, "%s&%u&%s&%s", config.req_password,
288 (show_all) ? "ShowAll" : "ShowFail",value_list); 261 (config.vars_to_check == CHECK_SERVICESTATE) ? 5 : 6,
289 fetch_data (server_address, server_port, send_buffer); 262 (config.show_all) ? "ShowAll" : "ShowFail", config.value_list);
290 numstr = strtok(recv_buffer,"&"); 263 fetch_data(config.server_address, config.server_port, send_buffer);
291 if (numstr == NULL) 264 char *numstr = strtok(recv_buffer, "&");
265 if (numstr == NULL) {
292 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 266 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
293 return_code=atoi(numstr); 267 }
294 temp_string=strtok(NULL,"&"); 268 return_code = atoi(numstr);
295 output_message = strdup (temp_string); 269 temp_string = strtok(NULL, "&");
270 output_message = strdup(temp_string);
296 } 271 }
297 break; 272 break;
298
299 case CHECK_MEMUSE: 273 case CHECK_MEMUSE:
300 274 xasprintf(&send_buffer, "%s&7", config.req_password);
301 xasprintf(&send_buffer,"%s&7", req_password); 275 fetch_data(config.server_address, config.server_port, send_buffer);
302 fetch_data (server_address, server_port, send_buffer); 276 char *numstr = strtok(recv_buffer, "&");
303 numstr = strtok(recv_buffer,"&"); 277 if (numstr == NULL) {
304 if (numstr == NULL)
305 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 278 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
306 mem_commitLimit=atof(numstr); 279 }
307 numstr = strtok(NULL,"&"); 280 double mem_commitLimit = atof(numstr);
308 if (numstr == NULL) 281 numstr = strtok(NULL, "&");
282 if (numstr == NULL) {
309 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 283 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
310 mem_commitByte=atof(numstr); 284 }
311 percent_used_space = (mem_commitByte / mem_commitLimit) * 100; 285 double mem_commitByte = atof(numstr);
312 warning_used_space = ((float)warning_value / 100) * mem_commitLimit; 286 double percent_used_space = (mem_commitByte / mem_commitLimit) * 100;
313 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;
314 289
315 /* 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,
316 which equals RAM + Pagefiles. */ 291 which equals RAM + Pagefiles. */
317 xasprintf(&output_message,_("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"), 292 xasprintf(
318 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space, 293 &output_message,
319 (mem_commitLimit - mem_commitByte) / 1048567, (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100); 294 _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"),
320 xasprintf(&perfdata,_("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"), mem_commitByte / 1048567, 295 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space,
321 warning_used_space / 1048567, critical_used_space / 1048567, mem_commitLimit / 1048567); 296 (mem_commitLimit - mem_commitByte) / 1048567,
322 297 (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100);
323 return_code=STATE_OK; 298 xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"),
324 if(check_critical_value && percent_used_space >= critical_value) 299 mem_commitByte / 1048567, warning_used_space / 1048567,
325 return_code=STATE_CRITICAL; 300 critical_used_space / 1048567, mem_commitLimit / 1048567);
326 else if (check_warning_value && percent_used_space >= warning_value) 301
327 return_code=STATE_WARNING; 302 return_code = STATE_OK;
303 if (config.check_critical_value && percent_used_space >= config.critical_value) {
304 return_code = STATE_CRITICAL;
305 } else if (config.check_warning_value && percent_used_space >= config.warning_value) {
306 return_code = STATE_WARNING;
307 }
328 308
329 break; 309 break;
330 310 case CHECK_COUNTER: {
331 case CHECK_COUNTER:
332
333
334 /* 311 /*
335 CHECK_COUNTER has been modified to provide extensive perfdata information. 312 CHECK_COUNTER has been modified to provide extensive perfdata information.
336 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
@@ -347,455 +324,466 @@ int main(int argc, char **argv){
347 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:
348 pages/s, packets transferred, etc. 325 pages/s, packets transferred, etc.
349 326
350 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
351 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
352 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.
353 */ 330 */
354 331
355 if (value_list == NULL) 332 double counter_value = 0.0;
356 output_message = strdup (_("No counter specified")); 333 if (config.value_list == NULL) {
357 else 334 output_message = strdup(_("No counter specified"));
358 { 335 } else {
359 preparelist (value_list); /* replace , between services with & to send the request */ 336 preparelist(
360 isPercent = (strchr (value_list, '%') != NULL); 337 config.value_list); /* replace , between services with & to send the request */
361 338 bool isPercent = (strchr(config.value_list, '%') != NULL);
362 strtok (value_list, "&"); /* burn the first parameters */ 339
363 description = strtok (NULL, "&"); 340 strtok(config.value_list, "&"); /* burn the first parameters */
364 counter_unit = strtok (NULL, "&"); 341 description = strtok(NULL, "&");
365 xasprintf (&send_buffer, "%s&8&%s", req_password, value_list); 342 counter_unit = strtok(NULL, "&");
366 fetch_data (server_address, server_port, send_buffer); 343 xasprintf(&send_buffer, "%s&8&%s", config.req_password, config.value_list);
367 counter_value = atof (recv_buffer); 344 fetch_data(config.server_address, config.server_port, send_buffer);
368 345 counter_value = atof(recv_buffer);
369 if (description == NULL) 346
370 xasprintf (&output_message, "%.f", counter_value); 347 bool allRight = false;
371 else if (isPercent) 348 if (description == NULL) {
372 { 349 xasprintf(&output_message, "%.f", counter_value);
373 counter_unit = strdup ("%"); 350 } else if (isPercent) {
351 counter_unit = strdup("%");
374 allRight = true; 352 allRight = true;
375 } 353 }
376 354
377 if ((counter_unit != NULL) && (!allRight)) 355 char *minval = NULL;
378 { 356 char *maxval = NULL;
379 minval = strtok (NULL, "&"); 357 double fminval = 0;
380 maxval = strtok (NULL, "&"); 358 double fmaxval = 0;
359 if ((counter_unit != NULL) && (!allRight)) {
360 minval = strtok(NULL, "&");
361 maxval = strtok(NULL, "&");
381 362
382 /* All parameters specified. Let's check the numbers */ 363 /* All parameters specified. Let's check the numbers */
383 364
384 fminval = (minval != NULL) ? strtod (minval, &errcvt) : -1; 365 fminval = (minval != NULL) ? strtod(minval, &errcvt) : -1;
385 fmaxval = (minval != NULL) ? strtod (maxval, &errcvt) : -1; 366 fmaxval = (minval != NULL) ? strtod(maxval, &errcvt) : -1;
386
387 if ((fminval == 0) && (minval == errcvt))
388 output_message = strdup (_("Minimum value contains non-numbers"));
389 else
390 {
391 if ((fmaxval == 0) && (maxval == errcvt))
392 output_message = strdup (_("Maximum value contains non-numbers"));
393 else
394 allRight = true; /* Everything is OK. */
395 367
368 if ((fminval == 0) && (minval == errcvt)) {
369 output_message = strdup(_("Minimum value contains non-numbers"));
370 } else {
371 if ((fmaxval == 0) && (maxval == errcvt)) {
372 output_message = strdup(_("Maximum value contains non-numbers"));
373 } else {
374 allRight = true; /* Everything is OK. */
375 }
396 } 376 }
377 } else if ((counter_unit == NULL) && (description != NULL)) {
378 output_message = strdup(_("No unit counter specified"));
397 } 379 }
398 else if ((counter_unit == NULL) && (description != NULL))
399 output_message = strdup (_("No unit counter specified"));
400 380
401 if (allRight) 381 if (allRight) {
402 {
403 /* Let's format the output string, finally... */ 382 /* Let's format the output string, finally... */
404 if (strstr(description, "%") == NULL) { 383 if (strstr(description, "%") == NULL) {
405 xasprintf (&output_message, "%s = %.2f %s", description, counter_value, counter_unit); 384 xasprintf(&output_message, "%s = %.2f %s", description, counter_value,
406 } else { 385 counter_unit);
407 /* has formatting, will segv if wrong */ 386 } else {
408 xasprintf (&output_message, description, counter_value); 387 /* has formatting, will segv if wrong */
409 } 388 xasprintf(&output_message, description, counter_value);
410 xasprintf (&output_message, "%s |", output_message); 389 }
411 xasprintf (&output_message,"%s %s", output_message, 390 xasprintf(&output_message, "%s |", output_message);
412 fperfdata (description, counter_value, 391 xasprintf(&output_message, "%s %s", output_message,
413 counter_unit, 1, warning_value, 1, critical_value, 392 fperfdata(description, counter_value, counter_unit, 1,
414 (!(isPercent) && (minval != NULL)), fminval, 393 config.warning_value, 1, config.critical_value,
415 (!(isPercent) && (minval != NULL)), fmaxval)); 394 (!(isPercent) && (minval != NULL)), fminval,
395 (!(isPercent) && (minval != NULL)), fmaxval));
416 } 396 }
417 } 397 }
418 398
419 if (critical_value > warning_value) 399 if (config.critical_value > config.warning_value) { /* Normal thresholds */
420 { /* Normal thresholds */ 400 if (config.check_critical_value && counter_value >= config.critical_value) {
421 if (check_critical_value && counter_value >= critical_value)
422 return_code = STATE_CRITICAL; 401 return_code = STATE_CRITICAL;
423 else if (check_warning_value && counter_value >= warning_value) 402 } else if (config.check_warning_value && counter_value >= config.warning_value) {
424 return_code = STATE_WARNING; 403 return_code = STATE_WARNING;
425 else 404 } else {
426 return_code = STATE_OK; 405 return_code = STATE_OK;
427 } 406 }
428 else 407 } else { /* inverse thresholds */
429 { /* inverse thresholds */
430 return_code = STATE_OK; 408 return_code = STATE_OK;
431 if (check_critical_value && counter_value <= critical_value) 409 if (config.check_critical_value && counter_value <= config.critical_value) {
432 return_code = STATE_CRITICAL; 410 return_code = STATE_CRITICAL;
433 else if (check_warning_value && counter_value <= warning_value) 411 } else if (config.check_warning_value && counter_value <= config.warning_value) {
434 return_code = STATE_WARNING; 412 return_code = STATE_WARNING;
413 }
435 } 414 }
436 break; 415 } break;
437
438 case CHECK_FILEAGE: 416 case CHECK_FILEAGE:
439 417 if (config.value_list == NULL) {
440 if (value_list==NULL) 418 output_message = strdup(_("No counter specified"));
441 output_message = strdup (_("No counter specified")); 419 } else {
442 else { 420 preparelist(
443 preparelist(value_list); /* replace , between services with & to send the request */ 421 config.value_list); /* replace , between services with & to send the request */
444 xasprintf(&send_buffer,"%s&9&%s", req_password,value_list); 422 xasprintf(&send_buffer, "%s&9&%s", config.req_password, config.value_list);
445 fetch_data (server_address, server_port, send_buffer); 423 fetch_data(config.server_address, config.server_port, send_buffer);
446 age_in_minutes = atoi(strtok(recv_buffer,"&")); 424 unsigned long age_in_minutes = atoi(strtok(recv_buffer, "&"));
447 description = strtok(NULL,"&"); 425 description = strtok(NULL, "&");
448 output_message = strdup (description); 426 output_message = strdup(description);
449 427
450 if (critical_value > warning_value) { /* Normal thresholds */ 428 if (config.critical_value > config.warning_value) { /* Normal thresholds */
451 if(check_critical_value && age_in_minutes >= critical_value) 429 if (config.check_critical_value && age_in_minutes >= config.critical_value) {
452 return_code=STATE_CRITICAL; 430 return_code = STATE_CRITICAL;
453 else if (check_warning_value && age_in_minutes >= warning_value) 431 } else if (config.check_warning_value && age_in_minutes >= config.warning_value) {
454 return_code=STATE_WARNING; 432 return_code = STATE_WARNING;
455 else 433 } else {
456 return_code=STATE_OK; 434 return_code = STATE_OK;
457 } 435 }
458 else { /* inverse thresholds */ 436 } else { /* inverse thresholds */
459 if(check_critical_value && age_in_minutes <= critical_value) 437 if (config.check_critical_value && age_in_minutes <= config.critical_value) {
460 return_code=STATE_CRITICAL; 438 return_code = STATE_CRITICAL;
461 else if (check_warning_value && age_in_minutes <= warning_value) 439 } else if (config.check_warning_value && age_in_minutes <= config.warning_value) {
462 return_code=STATE_WARNING; 440 return_code = STATE_WARNING;
463 else 441 } else {
464 return_code=STATE_OK; 442 return_code = STATE_OK;
443 }
465 } 444 }
466 } 445 }
467 break; 446 break;
468 447
469 case CHECK_INSTANCES: 448 case CHECK_INSTANCES:
470 if (value_list==NULL) 449 if (config.value_list == NULL) {
471 output_message = strdup (_("No counter specified")); 450 output_message = strdup(_("No counter specified"));
472 else { 451 } else {
473 xasprintf(&send_buffer,"%s&10&%s", req_password,value_list); 452 xasprintf(&send_buffer, "%s&10&%s", config.req_password, config.value_list);
474 fetch_data (server_address, server_port, send_buffer); 453 fetch_data(config.server_address, config.server_port, send_buffer);
475 if (!strncmp(recv_buffer,"ERROR",5)) { 454 if (!strncmp(recv_buffer, "ERROR", 5)) {
476 printf("NSClient - %s\n",recv_buffer); 455 printf("NSClient - %s\n", recv_buffer);
477 exit(STATE_UNKNOWN); 456 exit(STATE_UNKNOWN);
478 } 457 }
479 xasprintf(&output_message,"%s",recv_buffer); 458 xasprintf(&output_message, "%s", recv_buffer);
480 return_code=STATE_OK; 459 return_code = STATE_OK;
481 } 460 }
482 break; 461 break;
483 462
484 case CHECK_NONE: 463 case CHECK_NONE:
485 default: 464 default:
486 usage4 (_("Please specify a variable to check")); 465 usage4(_("Please specify a variable to check"));
487 break; 466 break;
488
489 } 467 }
490 468
491 /* reset timeout */ 469 /* reset timeout */
492 alarm(0); 470 alarm(0);
493 471
494 if (perfdata==NULL) 472 if (perfdata == NULL) {
495 printf("%s\n",output_message); 473 printf("%s\n", output_message);
496 else 474 } else {
497 printf("%s | %s\n",output_message,perfdata); 475 printf("%s | %s\n", output_message, perfdata);
476 }
498 return return_code; 477 return return_code;
499} 478}
500 479
501
502
503/* process command-line arguments */ 480/* process command-line arguments */
504int process_arguments(int argc, char **argv){ 481check_nt_config_wrapper process_arguments(int argc, char **argv) {
505 int c; 482 static struct option longopts[] = {{"port", required_argument, 0, 'p'},
506 483 {"timeout", required_argument, 0, 't'},
507 int option = 0; 484 {"critical", required_argument, 0, 'c'},
508 static struct option longopts[] = 485 {"warning", required_argument, 0, 'w'},
509 { 486 {"variable", required_argument, 0, 'v'},
510 {"port", required_argument,0,'p'}, 487 {"hostname", required_argument, 0, 'H'},
511 {"timeout", required_argument,0,'t'}, 488 {"params", required_argument, 0, 'l'},
512 {"critical", required_argument,0,'c'}, 489 {"secret", required_argument, 0, 's'},
513 {"warning", required_argument,0,'w'}, 490 {"display", required_argument, 0, 'd'},
514 {"variable", required_argument,0,'v'}, 491 {"unknown-timeout", no_argument, 0, 'u'},
515 {"hostname", required_argument,0,'H'}, 492 {"version", no_argument, 0, 'V'},
516 {"params", required_argument,0,'l'}, 493 {"help", no_argument, 0, 'h'},
517 {"secret", required_argument,0,'s'}, 494 {0, 0, 0, 0}};
518 {"display", required_argument,0,'d'}, 495
519 {"unknown-timeout", no_argument, 0, 'u'}, 496 check_nt_config_wrapper result = {
520 {"version", no_argument, 0,'V'}, 497 .errorcode = OK,
521 {"help", no_argument, 0,'h'}, 498 .config = check_nt_config_init(),
522 {0,0,0,0}
523 }; 499 };
524 500
525 /* no options were supplied */ 501 /* no options were supplied */
526 if(argc<2) return ERROR; 502 if (argc < 2) {
503 result.errorcode = ERROR;
504 return result;
505 }
527 506
528 /* backwards compatibility */ 507 /* backwards compatibility */
529 if (! is_option(argv[1])) { 508 if (!is_option(argv[1])) {
530 server_address = strdup(argv[1]); 509 result.config.server_address = strdup(argv[1]);
531 argv[1]=argv[0]; 510 argv[1] = argv[0];
532 argv=&argv[1]; 511 argv = &argv[1];
533 argc--; 512 argc--;
534 } 513 }
535 514
536 for (c=1;c<argc;c++) { 515 for (int index = 1; index < argc; index++) {
537 if(strcmp("-to",argv[c])==0) 516 if (strcmp("-to", argv[index]) == 0) {
538 strcpy(argv[c],"-t"); 517 strcpy(argv[index], "-t");
539 else if (strcmp("-wv",argv[c])==0) 518 } else if (strcmp("-wv", argv[index]) == 0) {
540 strcpy(argv[c],"-w"); 519 strcpy(argv[index], "-w");
541 else if (strcmp("-cv",argv[c])==0) 520 } else if (strcmp("-cv", argv[index]) == 0) {
542 strcpy(argv[c],"-c"); 521 strcpy(argv[index], "-c");
522 }
543 } 523 }
544 524
545 while (1) { 525 int option = 0;
546 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);
547 528
548 if (c==-1||c==EOF||c==1) 529 if (option_index == -1 || option_index == EOF || option_index == 1) {
549 break; 530 break;
531 }
550 532
551 switch (c) { 533 switch (option_index) {
552 case '?': /* print short usage statement if args not parsable */ 534 case '?': /* print short usage statement if args not parsable */
553 usage5 (); 535 usage5();
554 case 'h': /* help */ 536 case 'h': /* help */
555 print_help(); 537 print_help();
556 exit(STATE_UNKNOWN); 538 exit(STATE_UNKNOWN);
557 case 'V': /* version */ 539 case 'V': /* version */
558 print_revision(progname, NP_VERSION); 540 print_revision(progname, NP_VERSION);
559 exit(STATE_UNKNOWN); 541 exit(STATE_UNKNOWN);
560 case 'H': /* hostname */ 542 case 'H': /* hostname */
561 server_address = optarg; 543 result.config.server_address = optarg;
562 break; 544 break;
563 case 's': /* password */ 545 case 's': /* password */
564 req_password = optarg; 546 result.config.req_password = optarg;
565 break; 547 break;
566 case 'p': /* port */ 548 case 'p': /* port */
567 if (is_intnonneg(optarg)) 549 if (is_intnonneg(optarg)) {
568 server_port=atoi(optarg); 550 result.config.server_port = atoi(optarg);
569 else 551 } else {
570 die(STATE_UNKNOWN,_("Server port must be an integer\n")); 552 die(STATE_UNKNOWN, _("Server port must be an integer\n"));
571 break;
572 case 'v':
573 if(strlen(optarg)<4)
574 return ERROR;
575 if(!strcmp(optarg,"CLIENTVERSION"))
576 vars_to_check=CHECK_CLIENTVERSION;
577 else if(!strcmp(optarg,"CPULOAD"))
578 vars_to_check=CHECK_CPULOAD;
579 else if(!strcmp(optarg,"UPTIME"))
580 vars_to_check=CHECK_UPTIME;
581 else if(!strcmp(optarg,"USEDDISKSPACE"))
582 vars_to_check=CHECK_USEDDISKSPACE;
583 else if(!strcmp(optarg,"SERVICESTATE"))
584 vars_to_check=CHECK_SERVICESTATE;
585 else if(!strcmp(optarg,"PROCSTATE"))
586 vars_to_check=CHECK_PROCSTATE;
587 else if(!strcmp(optarg,"MEMUSE"))
588 vars_to_check=CHECK_MEMUSE;
589 else if(!strcmp(optarg,"COUNTER"))
590 vars_to_check=CHECK_COUNTER;
591 else if(!strcmp(optarg,"FILEAGE"))
592 vars_to_check=CHECK_FILEAGE;
593 else if(!strcmp(optarg,"INSTANCES"))
594 vars_to_check=CHECK_INSTANCES;
595 else
596 return ERROR;
597 break;
598 case 'l': /* value list */
599 value_list = optarg;
600 break;
601 case 'w': /* warning threshold */
602 warning_value=strtoul(optarg,NULL,10);
603 check_warning_value=true;
604 break;
605 case 'c': /* critical threshold */
606 critical_value=strtoul(optarg,NULL,10);
607 check_critical_value=true;
608 break;
609 case 'd': /* Display select for services */
610 if (!strcmp(optarg,"SHOWALL"))
611 show_all = true;
612 break;
613 case 'u':
614 socket_timeout_state=STATE_UNKNOWN;
615 break;
616 case 't': /* timeout */
617 socket_timeout=atoi(optarg);
618 if(socket_timeout<=0)
619 return ERROR;
620 } 553 }
621 554 break;
555 case 'v':
556 if (strlen(optarg) < 4) {
557 result.errorcode = ERROR;
558 return result;
559 }
560 if (!strcmp(optarg, "CLIENTVERSION")) {
561 result.config.vars_to_check = CHECK_CLIENTVERSION;
562 } else if (!strcmp(optarg, "CPULOAD")) {
563 result.config.vars_to_check = CHECK_CPULOAD;
564 } else if (!strcmp(optarg, "UPTIME")) {
565 result.config.vars_to_check = CHECK_UPTIME;
566 } else if (!strcmp(optarg, "USEDDISKSPACE")) {
567 result.config.vars_to_check = CHECK_USEDDISKSPACE;
568 } else if (!strcmp(optarg, "SERVICESTATE")) {
569 result.config.vars_to_check = CHECK_SERVICESTATE;
570 } else if (!strcmp(optarg, "PROCSTATE")) {
571 result.config.vars_to_check = CHECK_PROCSTATE;
572 } else if (!strcmp(optarg, "MEMUSE")) {
573 result.config.vars_to_check = CHECK_MEMUSE;
574 } else if (!strcmp(optarg, "COUNTER")) {
575 result.config.vars_to_check = CHECK_COUNTER;
576 } else if (!strcmp(optarg, "FILEAGE")) {
577 result.config.vars_to_check = CHECK_FILEAGE;
578 } else if (!strcmp(optarg, "INSTANCES")) {
579 result.config.vars_to_check = CHECK_INSTANCES;
580 } else {
581 result.errorcode = ERROR;
582 return result;
583 }
584 break;
585 case 'l': /* value list */
586 result.config.value_list = optarg;
587 break;
588 case 'w': /* warning threshold */
589 result.config.warning_value = strtoul(optarg, NULL, 10);
590 result.config.check_warning_value = true;
591 break;
592 case 'c': /* critical threshold */
593 result.config.critical_value = strtoul(optarg, NULL, 10);
594 result.config.check_critical_value = true;
595 break;
596 case 'd': /* Display select for services */
597 if (!strcmp(optarg, "SHOWALL")) {
598 result.config.show_all = true;
599 }
600 break;
601 case 'u':
602 socket_timeout_state = STATE_UNKNOWN;
603 break;
604 case 't': /* timeout */
605 socket_timeout = atoi(optarg);
606 if (socket_timeout <= 0) {
607 result.errorcode = ERROR;
608 return result;
609 }
610 }
611 }
612 if (result.config.server_address == NULL) {
613 usage4(_("You must provide a server address or host name"));
622 } 614 }
623 if (server_address == NULL)
624 usage4 (_("You must provide a server address or host name"));
625 615
626 if (vars_to_check==CHECK_NONE) 616 if (result.config.vars_to_check == CHECK_NONE) {
627 return ERROR; 617 result.errorcode = ERROR;
618 return result;
619 }
628 620
629 if (req_password == NULL) 621 if (result.config.req_password == NULL) {
630 req_password = strdup (_("None")); 622 result.config.req_password = strdup(_("None"));
623 }
631 624
632 return OK; 625 return result;
633} 626}
634 627
628void fetch_data(const char *address, int port, const char *sendb) {
629 int result = process_tcp_request(address, port, sendb, recv_buffer, sizeof(recv_buffer));
635 630
631 if (result != STATE_OK) {
632 die(result, _("could not fetch information from server\n"));
633 }
636 634
637void fetch_data (const char *address, int port, const char *sendb) { 635 if (!strncmp(recv_buffer, "ERROR", 5)) {
638 int result; 636 die(STATE_UNKNOWN, "NSClient - %s\n", recv_buffer);
639 637 }
640 result=process_tcp_request(address, port, sendb, recv_buffer,sizeof(recv_buffer));
641
642 if(result!=STATE_OK)
643 die (result, _("could not fetch information from server\n"));
644
645 if (!strncmp(recv_buffer,"ERROR",5))
646 die (STATE_UNKNOWN, "NSClient - %s\n",recv_buffer);
647} 638}
648 639
649bool strtoularray(unsigned long *array, char *string, const char *delim) { 640bool strtoularray(unsigned long *array, char *string, const char *delim) {
650 /* split a <delim> delimited string into a long array */ 641 /* split a <delim> delimited string into a long array */
651 int idx=0; 642 for (int idx = 0; idx < MAX_VALUE_LIST; idx++) {
652 char *t1; 643 array[idx] = 0;
653 644 }
654 for (idx=0;idx<MAX_VALUE_LIST;idx++)
655 array[idx]=0;
656 645
657 idx=0; 646 int idx = 0;
658 for(t1 = strtok(string,delim);t1 != NULL; t1 = strtok(NULL, delim)) { 647 for (char *t1 = strtok(string, delim); t1 != NULL; t1 = strtok(NULL, delim)) {
659 if (is_numeric(t1) && idx<MAX_VALUE_LIST) { 648 if (is_numeric(t1) && idx < MAX_VALUE_LIST) {
660 array[idx]=strtoul(t1,NULL,10); 649 array[idx] = strtoul(t1, NULL, 10);
661 idx++; 650 idx++;
662 } else 651 } else {
663 return false; 652 return false;
653 }
664 } 654 }
665 return true; 655 return true;
666} 656}
667 657
668void preparelist(char *string) { 658void preparelist(char *string) {
669 /* Replace all , with & which is the delimiter for the request */ 659 /* Replace all , with & which is the delimiter for the request */
670 int i; 660 for (int i = 0; (size_t)i < strlen(string); i++) {
671
672 for (i = 0; (size_t)i < strlen(string); i++)
673 if (string[i] == ',') { 661 if (string[i] == ',') {
674 string[i]='&'; 662 string[i] = '&';
675 } 663 }
664 }
676} 665}
677 666
678 667void print_help(void) {
679
680void print_help(void)
681{
682 print_revision(progname, NP_VERSION); 668 print_revision(progname, NP_VERSION);
683 669
684 printf ("Copyright (c) 2000 Yves Rubin (rubiyz@yahoo.com)\n"); 670 printf("Copyright (c) 2000 Yves Rubin (rubiyz@yahoo.com)\n");
685 printf (COPYRIGHT, copyright, email); 671 printf(COPYRIGHT, copyright, email);
686 672
687 printf ("%s\n", _("This plugin collects data from the NSClient service running on a")); 673 printf("%s\n", _("This plugin collects data from the NSClient service running on a"));
688 printf ("%s\n", _("Windows NT/2000/XP/2003 server.")); 674 printf("%s\n", _("Windows NT/2000/XP/2003 server."));
689 675
690 printf ("\n\n"); 676 printf("\n\n");
691 677
692 print_usage(); 678 print_usage();
693 679
694 printf (UT_HELP_VRSN); 680 printf(UT_HELP_VRSN);
695 printf (UT_EXTRA_OPTS); 681 printf(UT_EXTRA_OPTS);
696 682
697 printf ("%s\n", _("Options:")); 683 printf("%s\n", _("Options:"));
698 printf (" %s\n", "-H, --hostname=HOST"); 684 printf(" %s\n", "-H, --hostname=HOST");
699 printf (" %s\n", _("Name of the host to check")); 685 printf(" %s\n", _("Name of the host to check"));
700 printf (" %s\n", "-p, --port=INTEGER"); 686 printf(" %s\n", "-p, --port=INTEGER");
701 printf (" %s", _("Optional port number (default: ")); 687 printf(" %s", _("Optional port number (default: "));
702 printf ("%d)\n", PORT); 688 printf("%d)\n", PORT);
703 printf (" %s\n", "-s, --secret=<password>"); 689 printf(" %s\n", "-s, --secret=<password>");
704 printf (" %s\n", _("Password needed for the request")); 690 printf(" %s\n", _("Password needed for the request"));
705 printf (" %s\n", "-w, --warning=INTEGER"); 691 printf(" %s\n", "-w, --warning=INTEGER");
706 printf (" %s\n", _("Threshold which will result in a warning status")); 692 printf(" %s\n", _("Threshold which will result in a warning status"));
707 printf (" %s\n", "-c, --critical=INTEGER"); 693 printf(" %s\n", "-c, --critical=INTEGER");
708 printf (" %s\n", _("Threshold which will result in a critical status")); 694 printf(" %s\n", _("Threshold which will result in a critical status"));
709 printf (" %s\n", "-t, --timeout=INTEGER"); 695 printf(" %s\n", "-t, --timeout=INTEGER");
710 printf (" %s", _("Seconds before connection attempt times out (default: ")); 696 printf(" %s", _("Seconds before connection attempt times out (default: "));
711 printf (" %s\n", "-l, --params=<parameters>"); 697 printf(" %s\n", "-l, --params=<parameters>");
712 printf (" %s", _("Parameters passed to specified check (see below)")); 698 printf(" %s", _("Parameters passed to specified check (see below)"));
713 printf (" %s\n", "-d, --display={SHOWALL}"); 699 printf(" %s\n", "-d, --display={SHOWALL}");
714 printf (" %s", _("Display options (currently only SHOWALL works)")); 700 printf(" %s", _("Display options (currently only SHOWALL works)"));
715 printf (" %s\n", "-u, --unknown-timeout"); 701 printf(" %s\n", "-u, --unknown-timeout");
716 printf (" %s", _("Return UNKNOWN on timeouts")); 702 printf(" %s", _("Return UNKNOWN on timeouts"));
717 printf ("%d)\n", DEFAULT_SOCKET_TIMEOUT); 703 printf("%d)\n", DEFAULT_SOCKET_TIMEOUT);
718 printf (" %s\n", "-h, --help"); 704 printf(" %s\n", "-h, --help");
719 printf (" %s\n", _("Print this help screen")); 705 printf(" %s\n", _("Print this help screen"));
720 printf (" %s\n", "-V, --version"); 706 printf(" %s\n", "-V, --version");
721 printf (" %s\n", _("Print version information")); 707 printf(" %s\n", _("Print version information"));
722 printf (" %s\n", "-v, --variable=STRING"); 708 printf(" %s\n", "-v, --variable=STRING");
723 printf (" %s\n\n", _("Variable to check")); 709 printf(" %s\n\n", _("Variable to check"));
724 printf ("%s\n", _("Valid variables are:")); 710 printf("%s\n", _("Valid variables are:"));
725 printf (" %s", "CLIENTVERSION ="); 711 printf(" %s", "CLIENTVERSION =");
726 printf (" %s\n", _("Get the NSClient version")); 712 printf(" %s\n", _("Get the NSClient version"));
727 printf (" %s\n", _("If -l <version> is specified, will return warning if versions differ.")); 713 printf(" %s\n", _("If -l <version> is specified, will return warning if versions differ."));
728 printf (" %s\n", "CPULOAD ="); 714 printf(" %s\n", "CPULOAD =");
729 printf (" %s\n", _("Average CPU load on last x minutes.")); 715 printf(" %s\n", _("Average CPU load on last x minutes."));
730 printf (" %s\n", _("Request a -l parameter with the following syntax:")); 716 printf(" %s\n", _("Request a -l parameter with the following syntax:"));
731 printf (" %s\n", _("-l <minutes range>,<warning threshold>,<critical threshold>.")); 717 printf(" %s\n", _("-l <minutes range>,<warning threshold>,<critical threshold>."));
732 printf (" %s\n", _("<minute range> should be less than 24*60.")); 718 printf(" %s\n", _("<minute range> should be less than 24*60."));
733 printf (" %s\n", _("Thresholds are percentage and up to 10 requests can be done in one shot.")); 719 printf(" %s\n", _("Thresholds are percentage and up to 10 requests can be done in one shot."));
734 printf (" %s\n", "ie: -l 60,90,95,120,90,95"); 720 printf(" %s\n", "ie: -l 60,90,95,120,90,95");
735 printf (" %s\n", "UPTIME ="); 721 printf(" %s\n", "UPTIME =");
736 printf (" %s\n", _("Get the uptime of the machine.")); 722 printf(" %s\n", _("Get the uptime of the machine."));
737 printf (" %s\n", _("-l <unit> ")); 723 printf(" %s\n", _("-l <unit> "));
738 printf (" %s\n", _("<unit> = seconds, minutes, hours, or days. (default: minutes)")); 724 printf(" %s\n", _("<unit> = seconds, minutes, hours, or days. (default: minutes)"));
739 printf (" %s\n", _("Thresholds will use the unit specified above.")); 725 printf(" %s\n", _("Thresholds will use the unit specified above."));
740 printf (" %s\n", "USEDDISKSPACE ="); 726 printf(" %s\n", "USEDDISKSPACE =");
741 printf (" %s\n", _("Size and percentage of disk use.")); 727 printf(" %s\n", _("Size and percentage of disk use."));
742 printf (" %s\n", _("Request a -l parameter containing the drive letter only.")); 728 printf(" %s\n", _("Request a -l parameter containing the drive letter only."));
743 printf (" %s\n", _("Warning and critical thresholds can be specified with -w and -c.")); 729 printf(" %s\n", _("Warning and critical thresholds can be specified with -w and -c."));
744 printf (" %s\n", "MEMUSE ="); 730 printf(" %s\n", "MEMUSE =");
745 printf (" %s\n", _("Memory use.")); 731 printf(" %s\n", _("Memory use."));
746 printf (" %s\n", _("Warning and critical thresholds can be specified with -w and -c.")); 732 printf(" %s\n", _("Warning and critical thresholds can be specified with -w and -c."));
747 printf (" %s\n", "SERVICESTATE ="); 733 printf(" %s\n", "SERVICESTATE =");
748 printf (" %s\n", _("Check the state of one or several services.")); 734 printf(" %s\n", _("Check the state of one or several services."));
749 printf (" %s\n", _("Request a -l parameters with the following syntax:")); 735 printf(" %s\n", _("Request a -l parameters with the following syntax:"));
750 printf (" %s\n", _("-l <service1>,<service2>,<service3>,...")); 736 printf(" %s\n", _("-l <service1>,<service2>,<service3>,..."));
751 printf (" %s\n", _("You can specify -d SHOWALL in case you want to see working services")); 737 printf(" %s\n", _("You can specify -d SHOWALL in case you want to see working services"));
752 printf (" %s\n", _("in the returned string.")); 738 printf(" %s\n", _("in the returned string."));
753 printf (" %s\n", "PROCSTATE ="); 739 printf(" %s\n", "PROCSTATE =");
754 printf (" %s\n", _("Check if one or several process are running.")); 740 printf(" %s\n", _("Check if one or several process are running."));
755 printf (" %s\n", _("Same syntax as SERVICESTATE.")); 741 printf(" %s\n", _("Same syntax as SERVICESTATE."));
756 printf (" %s\n", "COUNTER ="); 742 printf(" %s\n", "COUNTER =");
757 printf (" %s\n", _("Check any performance counter of Windows NT/2000.")); 743 printf(" %s\n", _("Check any performance counter of Windows NT/2000."));
758 printf (" %s\n", _("Request a -l parameters with the following syntax:")); 744 printf(" %s\n", _("Request a -l parameters with the following syntax:"));
759 printf (" %s\n", _("-l \"\\\\<performance object>\\\\counter\",\"<description>")); 745 printf(" %s\n", _("-l \"\\\\<performance object>\\\\counter\",\"<description>"));
760 printf (" %s\n", _("The <description> parameter is optional and is given to a printf ")); 746 printf(" %s\n", _("The <description> parameter is optional and is given to a printf "));
761 printf (" %s\n", _("output command which requires a float parameter.")); 747 printf(" %s\n", _("output command which requires a float parameter."));
762 printf (" %s\n", _("If <description> does not include \"%%\", it is used as a label.")); 748 printf(" %s\n", _("If <description> does not include \"%%\", it is used as a label."));
763 printf (" %s\n", _("Some examples:")); 749 printf(" %s\n", _("Some examples:"));
764 printf (" %s\n", "\"Paging file usage is %%.2f %%%%\""); 750 printf(" %s\n", "\"Paging file usage is %%.2f %%%%\"");
765 printf (" %s\n", "\"%%.f %%%% paging file used.\""); 751 printf(" %s\n", "\"%%.f %%%% paging file used.\"");
766 printf (" %s\n", "INSTANCES ="); 752 printf(" %s\n", "INSTANCES =");
767 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."));
768 printf (" %s\n", _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>")); 754 printf(" %s\n",
769 printf (" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),")); 755 _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>"));
770 printf (" %s\n", _("if it is two words, it should be enclosed in quotes")); 756 printf(" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),"));
771 printf (" %s\n", _("The returned results will be a comma-separated list of instances on ")); 757 printf(" %s\n", _("if it is two words, it should be enclosed in quotes"));
772 printf (" %s\n", _(" the selected computer for that object.")); 758 printf(" %s\n", _("The returned results will be a comma-separated list of instances on "));
773 printf (" %s\n", _("The purpose of this is to be run from command line to determine what instances")); 759 printf(" %s\n", _(" the selected computer for that object."));
774 printf (" %s\n", _(" are available for monitoring without having to log onto the Windows server")); 760 printf(" %s\n",
775 printf (" %s\n", _(" to run Perfmon directly.")); 761 _("The purpose of this is to be run from command line to determine what instances"));
776 printf (" %s\n", _("It can also be used in scripts that automatically create the monitoring service")); 762 printf(" %s\n",
777 printf (" %s\n", _(" configuration files.")); 763 _(" are available for monitoring without having to log onto the Windows server"));
778 printf (" %s\n", _("Some examples:")); 764 printf(" %s\n", _(" to run Perfmon directly."));
779 printf (" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process")); 765 printf(" %s\n",
780 766 _("It can also be used in scripts that automatically create the monitoring service"));
781 printf ("%s\n", _("Notes:")); 767 printf(" %s\n", _(" configuration files."));
782 printf (" %s\n", _("- The NSClient service should be running on the server to get any information")); 768 printf(" %s\n", _("Some examples:"));
783 printf (" %s\n", "(http://nsclient.ready2run.nl)."); 769 printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process"));
784 printf (" %s\n", _("- Critical thresholds should be lower than warning thresholds")); 770
785 printf (" %s\n", _("- Default port 1248 is sometimes in use by other services. The error")); 771 printf("%s\n", _("Notes:"));
786 printf (" %s\n", _("output when this happens contains \"Cannot map xxxxx to protocol number\".")); 772 printf(" %s\n",
787 printf (" %s\n", _("One fix for this is to change the port to something else on check_nt ")); 773 _("- The NSClient service should be running on the server to get any information"));
788 printf (" %s\n", _("and on the client service it\'s connecting to.")); 774 printf(" %s\n", "(http://nsclient.ready2run.nl).");
789 775 printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds"));
790 printf (UT_SUPPORT); 776 printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error"));
777 printf(" %s\n",
778 _("output when this happens contains \"Cannot map xxxxx to protocol number\"."));
779 printf(" %s\n", _("One fix for this is to change the port to something else on check_nt "));
780 printf(" %s\n", _("and on the client service it\'s connecting to."));
781
782 printf(UT_SUPPORT);
791} 783}
792 784
793 785void print_usage(void) {
794 786 printf("%s\n", _("Usage:"));
795void print_usage(void) 787 printf("%s -H host -v variable [-p port] [-w warning] [-c critical]\n", progname);
796{ 788 printf("[-l params] [-d SHOWALL] [-u] [-t timeout]\n");
797 printf ("%s\n", _("Usage:"));
798 printf ("%s -H host -v variable [-p port] [-w warning] [-c critical]\n",progname);
799 printf ("[-l params] [-d SHOWALL] [-u] [-t timeout]\n");
800} 789}
801
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 61b2d699..b22cc3c1 100644
--- a/plugins/check_ntp.c
+++ b/plugins/check_ntp.c
@@ -1,61 +1,61 @@
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-2008 Monitoring Plugins Development Team 7 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_ntp 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-2008"; 34const char *copyright = "2006-2024";
35const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
36 36
37#include "common.h" 37#include "common.h"
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
50int process_arguments (int, char **); 50static int process_arguments(int /*argc*/, char ** /*argv*/);
51thresholds *offset_thresholds = NULL; 51static thresholds *offset_thresholds = NULL;
52thresholds *jitter_thresholds = NULL; 52static thresholds *jitter_thresholds = NULL;
53void 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 464a9e10..24d1c9b5 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -1,88 +1,77 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ntp_peer plugin 3 * Monitoring check_ntp_peer plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006 Sean Finney <seanius@seanius.net> 6 * Copyright (c) 2006 Sean Finney <seanius@seanius.net>
7* Copyright (c) 2006-2008 Monitoring Plugins Development Team 7 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_ntp_peer plugin 11 * This file contains the check_ntp_peer plugin
12* 12 *
13* This plugin checks an NTP server independent of any commandline 13 * This plugin checks an NTP server independent of any commandline
14* programs or external libraries. 14 * programs or external libraries.
15* 15 *
16* Use this plugin to check the health of an NTP server. It supports 16 * Use this plugin to check the health of an NTP server. It supports
17* checking the offset with the sync peer, the jitter and stratum. This 17 * checking the offset with the sync peer, the jitter and stratum. This
18* plugin will not check the clock offset between the local host and NTP 18 * plugin will not check the clock offset between the local host and NTP
19* server; please use check_ntp_time for that purpose. 19 * server; please use check_ntp_time for that purpose.
20* 20 *
21* 21 *
22* This program is free software: you can redistribute it and/or modify 22 * This program is free software: you can redistribute it and/or modify
23* it under the terms of the GNU General Public License as published by 23 * it under the terms of the GNU General Public License as published by
24* the Free Software Foundation, either version 3 of the License, or 24 * the Free Software Foundation, either version 3 of the License, or
25* (at your option) any later version. 25 * (at your option) any later version.
26* 26 *
27* This program is distributed in the hope that it will be useful, 27 * This program is distributed in the hope that it will be useful,
28* but WITHOUT ANY WARRANTY; without even the implied warranty of 28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30* GNU General Public License for more details. 30 * GNU General Public License for more details.
31* 31 *
32* You should have received a copy of the GNU General Public License 32 * You should have received a copy of the GNU General Public License
33* along with this program. If not, see <http://www.gnu.org/licenses/>. 33 * along with this program. If not, see <http://www.gnu.org/licenses/>.
34* 34 *
35* 35 *
36*****************************************************************************/ 36 *****************************************************************************/
37 37
38#include "thresholds.h"
38const char *progname = "check_ntp_peer"; 39const char *progname = "check_ntp_peer";
39const char *copyright = "2006-2008"; 40const char *copyright = "2006-2024";
40const char *email = "devel@monitoring-plugins.org"; 41const char *email = "devel@monitoring-plugins.org";
41 42
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; 49static int verbose = 0;
47static int port=123;
48static 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
64int process_arguments (int, char **); 53typedef struct {
65thresholds *offset_thresholds = NULL; 54 int errorcode;
66thresholds *jitter_thresholds = NULL; 55 check_ntp_peer_config config;
67thresholds *stratum_thresholds = NULL; 56} check_ntp_peer_config_wrapper;
68thresholds *truechimer_thresholds = NULL; 57static check_ntp_peer_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
69void print_help (void); 58static void print_help(void);
70void print_usage (void); 59void print_usage(void);
71 60
72/* max size of control message data */ 61/* max size of control message data */
73#define MAX_CM_SIZE 468 62#define MAX_CM_SIZE 468
74 63
75/* this structure holds everything in an ntp control message as per rfc1305 */ 64/* this structure holds everything in an ntp control message as per rfc1305 */
76typedef struct { 65typedef struct {
77 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 66 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
78 uint8_t op; /* R,E,M bits and Opcode */ 67 uint8_t op; /* R,E,M bits and Opcode */
79 uint16_t seq; /* Packet sequence */ 68 uint16_t seq; /* Packet sequence */
80 uint16_t status; /* Clock status */ 69 uint16_t status; /* Clock status */
81 uint16_t assoc; /* Association */ 70 uint16_t assoc; /* Association */
82 uint16_t offset; /* Similar to TCP sequence # */ 71 uint16_t offset; /* Similar to TCP sequence # */
83 uint16_t count; /* # bytes of data */ 72 uint16_t count; /* # bytes of data */
84 char data[MAX_CM_SIZE]; /* ASCII data of the request */ 73 char data[MAX_CM_SIZE]; /* ASCII data of the request */
85 /* NB: not necessarily NULL terminated! */ 74 /* NB: not necessarily NULL terminated! */
86} ntp_control_message; 75} ntp_control_message;
87 76
88/* this is an association/status-word pair found in control packet responses */ 77/* this is an association/status-word pair found in control packet responses */
@@ -93,82 +82,97 @@ typedef struct {
93 82
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) do{ x |= ((y<<6)&LI_MASK); }while(0) 86#define LI_SET(x, y) \
87 do { \
88 x |= ((y << 6) & LI_MASK); \
89 } while (0)
98/* and these are the values of the leap indicator */ 90/* and these are the values of the leap indicator */
99#define LI_NOWARNING 0x00 91#define LI_NOWARNING 0x00
100#define LI_EXTRASEC 0x01 92#define LI_EXTRASEC 0x01
101#define LI_MISSINGSEC 0x02 93#define LI_MISSINGSEC 0x02
102#define LI_ALARM 0x03 94#define LI_ALARM 0x03
103/* bits 3,4,5 are the ntp version */ 95/* bits 3,4,5 are the ntp version */
104#define VN_MASK 0x38 96#define VN_MASK 0x38
105#define VN(x) ((x&VN_MASK)>>3) 97#define VN(x) ((x & VN_MASK) >> 3)
106#define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) 98#define VN_SET(x, y) \
99 do { \
100 x |= ((y << 3) & VN_MASK); \
101 } while (0)
107#define VN_RESERVED 0x02 102#define VN_RESERVED 0x02
108/* bits 6,7,8 are the ntp mode */ 103/* bits 6,7,8 are the ntp mode */
109#define MODE_MASK 0x07 104#define MODE_MASK 0x07
110#define MODE(x) (x&MODE_MASK) 105#define MODE(x) (x & MODE_MASK)
111#define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) 106#define MODE_SET(x, y) \
107 do { \
108 x |= (y & MODE_MASK); \
109 } while (0)
112/* here are some values */ 110/* here are some values */
113#define MODE_CLIENT 0x03 111#define MODE_CLIENT 0x03
114#define MODE_CONTROLMSG 0x06 112#define MODE_CONTROLMSG 0x06
115/* In control message, bits 8-10 are R,E,M bits */ 113/* In control message, bits 8-10 are R,E,M bits */
116#define REM_MASK 0xe0 114#define REM_MASK 0xe0
117#define REM_RESP 0x80 115#define REM_RESP 0x80
118#define REM_ERROR 0x40 116#define REM_ERROR 0x40
119#define REM_MORE 0x20 117#define REM_MORE 0x20
120/* In control message, bits 11 - 15 are opcode */ 118/* In control message, bits 11 - 15 are opcode */
121#define OP_MASK 0x1f 119#define OP_MASK 0x1f
122#define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) 120#define OP_SET(x, y) \
121 do { \
122 x |= (y & OP_MASK); \
123 } while (0)
123#define OP_READSTAT 0x01 124#define OP_READSTAT 0x01
124#define OP_READVAR 0x02 125#define OP_READVAR 0x02
125/* In peer status bytes, bits 6,7,8 determine clock selection status */ 126/* In peer status bytes, bits 6,7,8 determine clock selection status */
126#define PEER_SEL(x) ((ntohs(x)>>8)&0x07) 127#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07)
127#define PEER_TRUECHIMER 0x02 128#define PEER_TRUECHIMER 0x02
128#define PEER_INCLUDED 0x04 129#define PEER_INCLUDED 0x04
129#define PEER_SYNCSOURCE 0x06 130#define PEER_SYNCSOURCE 0x06
130 131
131/* 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
132 * 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.
133 */ 134 */
134#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))
135 137
136/* finally, a little helper or two for debugging: */ 138/* finally, a little helper or two for debugging: */
137#define DBG(x) do{if(verbose>1){ x; }}while(0); 139#define DBG(x) \
138#define PRINTSOCKADDR(x) \ 140 do { \
139 do{ \ 141 if (verbose > 1) { \
140 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ 142 x; \
141 }while(0); 143 } \
142 144 } while (0);
143void print_ntp_control_message(const ntp_control_message *p){ 145#define PRINTSOCKADDR(x) \
144 int i=0, numpeers=0; 146 do { \
145 const ntp_assoc_status_pair *peer=NULL; 147 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
146 148 } while (0);
149
150void print_ntp_control_message(const ntp_control_message *message) {
147 printf("control packet contents:\n"); 151 printf("control packet contents:\n");
148 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); 152 printf("\tflags: 0x%.2x , 0x%.2x\n", message->flags, message->op);
149 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); 153 printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK);
150 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); 154 printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK);
151 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); 155 printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK);
152 printf("\t response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP); 156 printf("\t response=%d (0x%.2x)\n", (message->op & REM_RESP) > 0, message->op & REM_RESP);
153 printf("\t more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE); 157 printf("\t more=%d (0x%.2x)\n", (message->op & REM_MORE) > 0, message->op & REM_MORE);
154 printf("\t error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR); 158 printf("\t error=%d (0x%.2x)\n", (message->op & REM_ERROR) > 0, message->op & REM_ERROR);
155 printf("\t op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK); 159 printf("\t op=%d (0x%.2x)\n", message->op & OP_MASK, message->op & OP_MASK);
156 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); 160 printf("\tsequence: %d (0x%.2x)\n", ntohs(message->seq), ntohs(message->seq));
157 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); 161 printf("\tstatus: %d (0x%.2x)\n", ntohs(message->status), ntohs(message->status));
158 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); 162 printf("\tassoc: %d (0x%.2x)\n", ntohs(message->assoc), ntohs(message->assoc));
159 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); 163 printf("\toffset: %d (0x%.2x)\n", ntohs(message->offset), ntohs(message->offset));
160 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); 164 printf("\tcount: %d (0x%.2x)\n", ntohs(message->count), ntohs(message->count));
161 numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair)); 165
162 if(p->op&REM_RESP && p->op&OP_READSTAT){ 166 int numpeers = ntohs(message->count) / (sizeof(ntp_assoc_status_pair));
163 peer=(ntp_assoc_status_pair*)p->data; 167 if (message->op & REM_RESP && message->op & OP_READSTAT) {
164 for(i=0;i<numpeers;i++){ 168 const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)message->data;
165 printf("\tpeer id %.2x status %.2x", 169 for (int i = 0; i < numpeers; i++) {
166 ntohs(peer[i].assoc), ntohs(peer[i].status)); 170 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
167 if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){ 171 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
168 printf(" <-- current sync source"); 172 printf(" <-- current sync source");
169 } else if(PEER_SEL(peer[i].status) >= PEER_INCLUDED){ 173 } else if (PEER_SEL(peer[i].status) >= PEER_INCLUDED) {
170 printf(" <-- current sync candidate"); 174 printf(" <-- current sync candidate");
171 } else if(PEER_SEL(peer[i].status) >= PEER_TRUECHIMER){ 175 } else if (PEER_SEL(peer[i].status) >= PEER_TRUECHIMER) {
172 printf(" <-- outlyer, but truechimer"); 176 printf(" <-- outlyer, but truechimer");
173 } 177 }
174 printf("\n"); 178 printf("\n");
@@ -176,14 +180,13 @@ void print_ntp_control_message(const ntp_control_message *p){
176 } 180 }
177} 181}
178 182
179void 183void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_t seq) {
180setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){ 184 memset(message, 0, sizeof(ntp_control_message));
181 memset(p, 0, sizeof(ntp_control_message)); 185 LI_SET(message->flags, LI_NOWARNING);
182 LI_SET(p->flags, LI_NOWARNING); 186 VN_SET(message->flags, VN_RESERVED);
183 VN_SET(p->flags, VN_RESERVED); 187 MODE_SET(message->flags, MODE_CONTROLMSG);
184 MODE_SET(p->flags, MODE_CONTROLMSG); 188 OP_SET(message->op, opcode);
185 OP_SET(p->op, opcode); 189 message->seq = htons(seq);
186 p->seq = htons(seq);
187 /* Remaining fields are zero for requests */ 190 /* Remaining fields are zero for requests */
188} 191}
189 192
@@ -198,22 +201,23 @@ setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
198 * 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
199 * 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
200 * so I left it alone */ 203 * so I left it alone */
201int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum, int *num_truechimers){ 204typedef struct {
202 int conn=-1, i, npeers=0, num_candidates=0; 205 mp_state_enum state;
203 double tmp_offset = 0; 206 mp_state_enum offset_result;
204 int min_peer_sel=PEER_INCLUDED; 207 double offset;
205 int peers_size=0, peer_offset=0; 208 double jitter;
206 int status; 209 long stratum;
207 ntp_assoc_status_pair *peers=NULL; 210 int num_truechimers;
208 ntp_control_message req; 211} ntp_request_result;
209 const char *getvar = "stratum,offset,jitter"; 212ntp_request_result ntp_request(const check_ntp_peer_config config) {
210 char *data, *value, *nptr; 213
211 void *tmp; 214 ntp_request_result result = {
212 215 .state = STATE_OK,
213 status = STATE_OK; 216 .offset_result = STATE_UNKNOWN,
214 *offset_result = STATE_UNKNOWN; 217 .jitter = -1,
215 *jitter = *stratum = -1; 218 .stratum = -1,
216 *num_truechimers = 0; 219 .num_truechimers = 0,
220 };
217 221
218 /* Long-winded explanation: 222 /* Long-winded explanation:
219 * 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
@@ -231,11 +235,20 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
231 * 4) Extract the offset, jitter and stratum value from the data[] 235 * 4) Extract the offset, jitter and stratum value from the data[]
232 * (it's ASCII) 236 * (it's ASCII)
233 */ 237 */
234 my_udp_connect(server_address, port, &conn); 238 int min_peer_sel = PEER_INCLUDED;
239 int num_candidates = 0;
240 void *tmp;
241 ntp_assoc_status_pair *peers = NULL;
242 int peer_offset = 0;
243 size_t peers_size = 0;
244 size_t npeers = 0;
245 int conn = -1;
246 my_udp_connect(config.server_address, config.port, &conn);
235 247
236 /* keep sending requests until the server stops setting the 248 /* keep sending requests until the server stops setting the
237 * REM_MORE bit, though usually this is only 1 packet. */ 249 * REM_MORE bit, though usually this is only 1 packet. */
238 do{ 250 ntp_control_message req;
251 do {
239 setup_control_request(&req, OP_READSTAT, 1); 252 setup_control_request(&req, OP_READSTAT, 1);
240 DBG(printf("sending READSTAT request")); 253 DBG(printf("sending READSTAT request"));
241 write(conn, &req, SIZEOF_NTPCM(req)); 254 write(conn, &req, SIZEOF_NTPCM(req));
@@ -243,63 +256,81 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
243 256
244 do { 257 do {
245 /* Attempt to read the largest size packet possible */ 258 /* Attempt to read the largest size packet possible */
246 req.count=htons(MAX_CM_SIZE); 259 req.count = htons(MAX_CM_SIZE);
247 DBG(printf("receiving READSTAT response")) 260 DBG(printf("receiving READSTAT response"))
248 if(read(conn, &req, SIZEOF_NTPCM(req)) == -1) 261 if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) {
249 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 262 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
263 }
250 DBG(print_ntp_control_message(&req)); 264 DBG(print_ntp_control_message(&req));
251 /* discard obviously invalid packets */ 265 /* discard obviously invalid packets */
252 if (ntohs(req.count) > MAX_CM_SIZE) 266 if (ntohs(req.count) > MAX_CM_SIZE) {
253 die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n"); 267 die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n");
254 } while (!(req.op&OP_READSTAT && ntohs(req.seq) == 1)); 268 }
269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1));
255 270
256 if (LI(req.flags) == LI_ALARM) li_alarm = true; 271 if (LI(req.flags) == LI_ALARM) {
272 li_alarm = true;
273 }
257 /* Each peer identifier is 4 bytes in the data section, which 274 /* Each peer identifier is 4 bytes in the data section, which
258 * we represent as a ntp_assoc_status_pair datatype. 275 * we represent as a ntp_assoc_status_pair datatype.
259 */ 276 */
260 peers_size+=ntohs(req.count); 277 peers_size += ntohs(req.count);
261 if((tmp=realloc(peers, peers_size)) == NULL) 278 if ((tmp = realloc(peers, peers_size)) == NULL) {
262 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");
263 peers=tmp; 280 }
264 memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count)); 281 peers = tmp;
265 npeers=peers_size/sizeof(ntp_assoc_status_pair); 282 memcpy((peers + peer_offset), (void *)req.data, ntohs(req.count));
266 peer_offset+=ntohs(req.count); 283 npeers = peers_size / sizeof(ntp_assoc_status_pair);
267 } while(req.op&REM_MORE); 284 peer_offset += ntohs(req.count);
285 } while (req.op & REM_MORE);
268 286
269 /* 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
270 * at least some candidates. In the latter case we'll issue 288 * at least some candidates. In the latter case we'll issue
271 * a warning but go ahead with the check on them. */ 289 * a warning but go ahead with the check on them. */
272 for (i = 0; i < npeers; i++){ 290 for (size_t i = 0; i < npeers; i++) {
273 if(PEER_SEL(peers[i].status) >= PEER_TRUECHIMER){ 291 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) {
274 (*num_truechimers)++; 292 result.num_truechimers++;
275 if(PEER_SEL(peers[i].status) >= PEER_INCLUDED){ 293 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
276 num_candidates++; 294 num_candidates++;
277 if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){ 295 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
278 syncsource_found = true; 296 syncsource_found = true;
279 min_peer_sel=PEER_SYNCSOURCE; 297 min_peer_sel = PEER_SYNCSOURCE;
280 } 298 }
281 } 299 }
282 } 300 }
283 } 301 }
284 if(verbose) printf("%d candidate peers available\n", num_candidates); 302
285 if(verbose && syncsource_found) printf("synchronization source found\n"); 303 if (verbose) {
286 if(! syncsource_found){ 304 printf("%d candidate peers available\n", num_candidates);
287 status = STATE_WARNING;
288 if(verbose) printf("warning: no synchronization source found\n");
289 } 305 }
290 if(li_alarm){ 306 if (verbose && syncsource_found) {
291 status = STATE_WARNING; 307 printf("synchronization source found\n");
292 if(verbose) printf("warning: LI_ALARM bit is set\n");
293 } 308 }
294 309
310 if (!syncsource_found) {
311 result.state = STATE_WARNING;
312 if (verbose) {
313 printf("warning: no synchronization source found\n");
314 }
315 }
316 if (li_alarm) {
317 result.state = STATE_WARNING;
318 if (verbose) {
319 printf("warning: LI_ALARM bit is set\n");
320 }
321 }
295 322
296 for (i = 0; i < npeers; i++){ 323 const char *getvar = "stratum,offset,jitter";
324 char *data;
325 for (size_t i = 0; i < npeers; i++) {
297 /* Only query this server if it is the current sync source */ 326 /* Only query this server if it is the current sync source */
298 /* If there's no sync.peer, query all candidates and use the best one */ 327 /* If there's no sync.peer, query all candidates and use the best one */
299 if (PEER_SEL(peers[i].status) >= min_peer_sel){ 328 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
300 if(verbose) printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); 329 if (verbose) {
330 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
331 }
301 xasprintf(&data, ""); 332 xasprintf(&data, "");
302 do{ 333 do {
303 setup_control_request(&req, OP_READVAR, 2); 334 setup_control_request(&req, OP_READVAR, 2);
304 req.assoc = peers[i].assoc; 335 req.assoc = peers[i].assoc;
305 /* Putting the wanted variable names in the request 336 /* Putting the wanted variable names in the request
@@ -309,7 +340,7 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
309 */ 340 */
310 /* Older servers doesn't know what jitter is, so if we get an 341 /* Older servers doesn't know what jitter is, so if we get an
311 * error on the first pass we redo it with "dispersion" */ 342 * error on the first pass we redo it with "dispersion" */
312 strncpy(req.data, getvar, MAX_CM_SIZE-1); 343 strncpy(req.data, getvar, MAX_CM_SIZE - 1);
313 req.count = htons(strlen(getvar)); 344 req.count = htons(strlen(getvar));
314 DBG(printf("sending READVAR request...\n")); 345 DBG(printf("sending READVAR request...\n"));
315 write(conn, &req, SIZEOF_NTPCM(req)); 346 write(conn, &req, SIZEOF_NTPCM(req));
@@ -320,131 +351,159 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
320 DBG(printf("receiving READVAR response...\n")); 351 DBG(printf("receiving READVAR response...\n"));
321 read(conn, &req, SIZEOF_NTPCM(req)); 352 read(conn, &req, SIZEOF_NTPCM(req));
322 DBG(print_ntp_control_message(&req)); 353 DBG(print_ntp_control_message(&req));
323 } while (!(req.op&OP_READVAR && ntohs(req.seq) == 2)); 354 } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2));
324 355
325 if(!(req.op&REM_ERROR)) 356 if (!(req.op & REM_ERROR)) {
326 xasprintf(&data, "%s%s", data, req.data); 357 xasprintf(&data, "%s%s", data, req.data);
327 } while(req.op&REM_MORE); 358 }
328 359 } while (req.op & REM_MORE);
329 if(req.op&REM_ERROR) { 360
330 if(strstr(getvar, "jitter")) { 361 if (req.op & REM_ERROR) {
331 if(verbose) printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with 'dispersion'...\n"); 362 if (strstr(getvar, "jitter")) {
363 if (verbose) {
364 printf("The command failed. This is usually caused by servers refusing the "
365 "'jitter'\nvariable. Restarting with "
366 "'dispersion'...\n");
367 }
332 getvar = "stratum,offset,dispersion"; 368 getvar = "stratum,offset,dispersion";
333 i--; 369 i--;
334 continue; 370 continue;
335 } else if(strlen(getvar)) { 371 }
336 if(verbose) printf("Server didn't like dispersion either; will retrieve everything\n"); 372 if (strlen(getvar)) {
373 if (verbose) {
374 printf("Server didn't like dispersion either; will retrieve everything\n");
375 }
337 getvar = ""; 376 getvar = "";
338 i--; 377 i--;
339 continue; 378 continue;
340 } 379 }
341 } 380 }
342 381
343 if(verbose > 1) 382 if (verbose > 1) {
344 printf("Server responded: >>>%s<<<\n", data); 383 printf("Server responded: >>>%s<<<\n", data);
384 }
345 385
386 double tmp_offset = 0;
387 char *value;
388 char *nptr;
346 /* get the offset */ 389 /* get the offset */
347 if(verbose) 390 if (verbose) {
348 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc)); 391 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc));
392 }
349 393
350 value = np_extract_ntpvar(data, "offset"); 394 value = np_extract_ntpvar(data, "offset");
351 nptr=NULL; 395 nptr = NULL;
352 /* Convert the value if we have one */ 396 /* Convert the value if we have one */
353 if(value != NULL) 397 if (value != NULL) {
354 tmp_offset = strtod(value, &nptr) / 1000; 398 tmp_offset = strtod(value, &nptr) / 1000;
399 }
355 /* If value is null or no conversion was performed */ 400 /* If value is null or no conversion was performed */
356 if(value == NULL || value==nptr) { 401 if (value == NULL || value == nptr) {
357 if(verbose) printf("error: unable to read server offset response.\n"); 402 if (verbose) {
403 printf("error: unable to read server offset response.\n");
404 }
358 } else { 405 } else {
359 if(verbose) printf("%.10g\n", tmp_offset); 406 if (verbose) {
360 if(*offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(*offset)) { 407 printf("%.10g\n", tmp_offset);
361 *offset = tmp_offset; 408 }
362 *offset_result = STATE_OK; 409 if (result.offset_result == STATE_UNKNOWN ||
410 fabs(tmp_offset) < fabs(result.offset)) {
411 result.offset = tmp_offset;
412 result.offset_result = STATE_OK;
363 } else { 413 } else {
364 /* Skip this one; move to the next */ 414 /* Skip this one; move to the next */
365 continue; 415 continue;
366 } 416 }
367 } 417 }
368 418
369 if(do_jitter) { 419 if (config.do_jitter) {
370 /* get the jitter */ 420 /* get the jitter */
371 if(verbose) { 421 if (verbose) {
372 printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", ntohs(peers[i].assoc)); 422 printf("parsing %s from peer %.2x: ",
423 strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter",
424 ntohs(peers[i].assoc));
373 } 425 }
374 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter"); 426 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion"
375 nptr=NULL; 427 : "jitter");
428 nptr = NULL;
376 /* Convert the value if we have one */ 429 /* Convert the value if we have one */
377 if(value != NULL) 430 if (value != NULL) {
378 *jitter = strtod(value, &nptr); 431 result.jitter = strtod(value, &nptr);
432 }
379 /* If value is null or no conversion was performed */ 433 /* If value is null or no conversion was performed */
380 if(value == NULL || value==nptr) { 434 if (value == NULL || value == nptr) {
381 if(verbose) printf("error: unable to read server jitter/dispersion response.\n"); 435 if (verbose) {
382 *jitter = -1; 436 printf("error: unable to read server jitter/dispersion response.\n");
383 } else if(verbose) { 437 }
384 printf("%.10g\n", *jitter); 438 result.jitter = -1;
439 } else if (verbose) {
440 printf("%.10g\n", result.jitter);
385 } 441 }
386 } 442 }
387 443
388 if(do_stratum) { 444 if (config.do_stratum) {
389 /* get the stratum */ 445 /* get the stratum */
390 if(verbose) { 446 if (verbose) {
391 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc)); 447 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc));
392 } 448 }
393 value = np_extract_ntpvar(data, "stratum"); 449 value = np_extract_ntpvar(data, "stratum");
394 nptr=NULL; 450 nptr = NULL;
395 /* Convert the value if we have one */ 451 /* Convert the value if we have one */
396 if(value != NULL) 452 if (value != NULL) {
397 *stratum = strtol(value, &nptr, 10); 453 result.stratum = strtol(value, &nptr, 10);
398 if(value == NULL || value==nptr) { 454 }
399 if(verbose) printf("error: unable to read server stratum response.\n"); 455 if (value == NULL || value == nptr) {
400 *stratum = -1; 456 if (verbose) {
457 printf("error: unable to read server stratum response.\n");
458 }
459 result.stratum = -1;
401 } else { 460 } else {
402 if(verbose) printf("%i\n", *stratum); 461 if (verbose) {
462 printf("%li\n", result.stratum);
463 }
403 } 464 }
404 } 465 }
405 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */ 466 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */
406 } /* for (i = 0; i < npeers; i++) */ 467 } /* for (i = 0; i < npeers; i++) */
407 468
408 close(conn); 469 close(conn);
409 if(peers!=NULL) free(peers); 470 if (peers != NULL) {
471 free(peers);
472 }
410 473
411 return status; 474 return result;
412} 475}
413 476
414int process_arguments(int argc, char **argv){ 477check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
415 int c;
416 int option=0;
417 static struct option longopts[] = { 478 static struct option longopts[] = {
418 {"version", no_argument, 0, 'V'}, 479 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
419 {"help", no_argument, 0, 'h'}, 480 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'},
420 {"verbose", no_argument, 0, 'v'}, 481 {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'},
421 {"use-ipv4", no_argument, 0, '4'}, 482 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'},
422 {"use-ipv6", no_argument, 0, '6'}, 483 {"swarn", required_argument, 0, 'W'}, {"scrit", required_argument, 0, 'C'},
423 {"quiet", no_argument, 0, 'q'}, 484 {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'},
424 {"warning", required_argument, 0, 'w'}, 485 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'},
425 {"critical", required_argument, 0, 'c'}, 486 {"timeout", required_argument, 0, 't'}, {"hostname", required_argument, 0, 'H'},
426 {"swarn", required_argument, 0, 'W'}, 487 {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}};
427 {"scrit", required_argument, 0, 'C'}, 488
428 {"jwarn", required_argument, 0, 'j'}, 489 if (argc < 2) {
429 {"jcrit", required_argument, 0, 'k'}, 490 usage("\n");
430 {"twarn", required_argument, 0, 'm'}, 491 }
431 {"tcrit", required_argument, 0, 'n'},
432 {"timeout", required_argument, 0, 't'},
433 {"hostname", required_argument, 0, 'H'},
434 {"port", required_argument, 0, 'p'},
435 {0, 0, 0, 0}
436 };
437
438 492
439 if (argc < 2) 493 check_ntp_peer_config_wrapper result = {
440 usage ("\n"); 494 .errorcode = OK,
495 .config = check_ntp_peer_config_init(),
496 };
441 497
442 while (true) { 498 while (true) {
443 c = getopt_long (argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); 499 int option = 0;
444 if (c == -1 || c == EOF || c == 1) 500 int option_char =
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) {
445 break; 503 break;
504 }
446 505
447 switch (c) { 506 switch (option_char) {
448 case 'h': 507 case 'h':
449 print_help(); 508 print_help();
450 exit(STATE_UNKNOWN); 509 exit(STATE_UNKNOWN);
@@ -457,48 +516,49 @@ int process_arguments(int argc, char **argv){
457 verbose++; 516 verbose++;
458 break; 517 break;
459 case 'q': 518 case 'q':
460 quiet = true; 519 result.config.quiet = true;
461 break; 520 break;
462 case 'w': 521 case 'w':
463 owarn = optarg; 522 result.config.owarn = optarg;
464 break; 523 break;
465 case 'c': 524 case 'c':
466 ocrit = optarg; 525 result.config.ocrit = optarg;
467 break; 526 break;
468 case 'W': 527 case 'W':
469 do_stratum = true; 528 result.config.do_stratum = true;
470 swarn = optarg; 529 result.config.swarn = optarg;
471 break; 530 break;
472 case 'C': 531 case 'C':
473 do_stratum = true; 532 result.config.do_stratum = true;
474 scrit = optarg; 533 result.config.scrit = optarg;
475 break; 534 break;
476 case 'j': 535 case 'j':
477 do_jitter = true; 536 result.config.do_jitter = true;
478 jwarn = optarg; 537 result.config.jwarn = optarg;
479 break; 538 break;
480 case 'k': 539 case 'k':
481 do_jitter = true; 540 result.config.do_jitter = true;
482 jcrit = optarg; 541 result.config.jcrit = optarg;
483 break; 542 break;
484 case 'm': 543 case 'm':
485 do_truechimers = true; 544 result.config.do_truechimers = true;
486 twarn = optarg; 545 result.config.twarn = optarg;
487 break; 546 break;
488 case 'n': 547 case 'n':
489 do_truechimers = true; 548 result.config.do_truechimers = true;
490 tcrit = optarg; 549 result.config.tcrit = optarg;
491 break; 550 break;
492 case 'H': 551 case 'H':
493 if(!is_host(optarg)) 552 if (!is_host(optarg)) {
494 usage2(_("Invalid hostname/address"), optarg); 553 usage2(_("Invalid hostname/address"), optarg);
495 server_address = strdup(optarg); 554 }
555 result.config.server_address = strdup(optarg);
496 break; 556 break;
497 case 'p': 557 case 'p':
498 port=atoi(optarg); 558 result.config.port = atoi(optarg);
499 break; 559 break;
500 case 't': 560 case 't':
501 socket_timeout=atoi(optarg); 561 socket_timeout = atoi(optarg);
502 break; 562 break;
503 case '4': 563 case '4':
504 address_family = AF_INET; 564 address_family = AF_INET;
@@ -507,222 +567,230 @@ int process_arguments(int argc, char **argv){
507#ifdef USE_IPV6 567#ifdef USE_IPV6
508 address_family = AF_INET6; 568 address_family = AF_INET6;
509#else 569#else
510 usage4 (_("IPv6 support not available")); 570 usage4(_("IPv6 support not available"));
511#endif 571#endif
512 break; 572 break;
513 case '?': 573 case '?':
514 /* print short usage statement if args not parsable */ 574 /* print short usage statement if args not parsable */
515 usage5 (); 575 usage5();
516 break; 576 break;
517 } 577 }
518 } 578 }
519 579
520 if(server_address == NULL){ 580 if (result.config.server_address == NULL) {
521 usage4(_("Hostname was not supplied")); 581 usage4(_("Hostname was not supplied"));
522 } 582 }
523 583
524 return 0; 584 set_thresholds(&result.config.offset_thresholds, result.config.owarn, result.config.ocrit);
525} 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);
526 588
527char *perfd_offset (double offset) 589 return result;
528{
529 return fperfdata ("offset", offset, "s",
530 true, offset_thresholds->warning->end,
531 true, offset_thresholds->critical->end,
532 false, 0, false, 0);
533} 590}
534 591
535char *perfd_jitter (double jitter) 592char *perfd_offset(double offset, thresholds *offset_thresholds) {
536{ 593 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
537 return fperfdata ("jitter", jitter, "", 594 offset_thresholds->critical->end, false, 0, false, 0);
538 do_jitter, jitter_thresholds->warning->end,
539 do_jitter, jitter_thresholds->critical->end,
540 true, 0, false, 0);
541} 595}
542 596
543char *perfd_stratum (int stratum) 597char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) {
544{ 598 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter,
545 return perfdata ("stratum", stratum, "", 599 jitter_thresholds->critical->end, true, 0, false, 0);
546 do_stratum, (int)stratum_thresholds->warning->end,
547 do_stratum, (int)stratum_thresholds->critical->end,
548 true, 0, true, 16);
549} 600}
550 601
551char *perfd_truechimers (int num_truechimers) 602char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) {
552{ 603 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end,
553 return perfdata ("truechimers", num_truechimers, "", 604 do_stratum, (int)stratum_thresholds->critical->end, true, 0, true, 16);
554 do_truechimers, (int)truechimer_thresholds->warning->end,
555 do_truechimers, (int)truechimer_thresholds->critical->end,
556 true, 0, false, 0);
557} 605}
558 606
559int main(int argc, char *argv[]){ 607char *perfd_truechimers(int num_truechimers, const bool do_truechimers,
560 int result, offset_result, stratum, num_truechimers; 608 thresholds *truechimer_thresholds) {
561 double offset=0, jitter=0; 609 return perfdata("truechimers", num_truechimers, "", do_truechimers,
562 char *result_line, *perfdata_line; 610 (int)truechimer_thresholds->warning->end, do_truechimers,
611 (int)truechimer_thresholds->critical->end, true, 0, false, 0);
612}
563 613
564 setlocale (LC_ALL, ""); 614int main(int argc, char *argv[]) {
565 bindtextdomain (PACKAGE, LOCALEDIR); 615 setlocale(LC_ALL, "");
566 textdomain (PACKAGE); 616 bindtextdomain(PACKAGE, LOCALEDIR);
617 textdomain(PACKAGE);
567 618
568 /* Parse extra opts if any */ 619 /* Parse extra opts if any */
569 argv=np_extra_opts (&argc, argv, progname); 620 argv = np_extra_opts(&argc, argv, progname);
570 621
571 if (process_arguments (argc, argv) == ERROR) 622 check_ntp_peer_config_wrapper tmp_config = process_arguments(argc, argv);
572 usage4 (_("Could not parse arguments"));
573 623
574 set_thresholds(&offset_thresholds, owarn, ocrit); 624 if (tmp_config.errorcode == ERROR) {
575 set_thresholds(&jitter_thresholds, jwarn, jcrit); 625 usage4(_("Could not parse arguments"));
576 set_thresholds(&stratum_thresholds, swarn, scrit); 626 }
577 set_thresholds(&truechimer_thresholds, twarn, tcrit); 627
628 const check_ntp_peer_config config = tmp_config.config;
578 629
579 /* initialize alarm signal handling */ 630 /* initialize alarm signal handling */
580 signal (SIGALRM, socket_timeout_alarm_handler); 631 signal(SIGALRM, socket_timeout_alarm_handler);
581 632
582 /* set socket timeout */ 633 /* set socket timeout */
583 alarm (socket_timeout); 634 alarm(socket_timeout);
584 635
585 /* This returns either OK or WARNING (See comment preceding ntp_request) */ 636 /* This returns either OK or WARNING (See comment preceding ntp_request) */
586 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;
587 639
588 if(offset_result == STATE_UNKNOWN) { 640 if (ntp_res.offset_result == STATE_UNKNOWN) {
589 /* if there's no sync peer (this overrides ntp_request output): */ 641 /* if there's no sync peer (this overrides ntp_request output): */
590 result = (quiet ? STATE_UNKNOWN : STATE_CRITICAL); 642 result = (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL);
591 } else { 643 } else {
592 /* Be quiet if there's no candidates either */ 644 /* Be quiet if there's no candidates either */
593 if (quiet && result == STATE_WARNING) 645 if (config.quiet && result == STATE_WARNING) {
594 result = STATE_UNKNOWN; 646 result = STATE_UNKNOWN;
595 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));
596 } 649 }
597 650
598 int oresult = result; 651 mp_state_enum oresult = result;
599 652 mp_state_enum tresult = STATE_UNKNOWN;
600 653
601 int tresult = STATE_UNKNOWN; 654 if (config.do_truechimers) {
602 655 tresult = get_status(ntp_res.num_truechimers, config.truechimer_thresholds);
603 if(do_truechimers) {
604 tresult = get_status(num_truechimers, truechimer_thresholds);
605 result = max_state_alt(result, tresult); 656 result = max_state_alt(result, tresult);
606 } 657 }
607 658
659 mp_state_enum sresult = STATE_UNKNOWN;
608 660
609 int sresult = STATE_UNKNOWN; 661 if (config.do_stratum) {
610 662 sresult = get_status((double)ntp_res.stratum, config.stratum_thresholds);
611 if(do_stratum) {
612 sresult = get_status(stratum, stratum_thresholds);
613 result = max_state_alt(result, sresult); 663 result = max_state_alt(result, sresult);
614 } 664 }
615 665
666 mp_state_enum jresult = STATE_UNKNOWN;
616 667
617 int jresult = STATE_UNKNOWN; 668 if (config.do_jitter) {
618 669 jresult = get_status(ntp_res.jitter, config.jitter_thresholds);
619 if(do_jitter) {
620 jresult = get_status(jitter, jitter_thresholds);
621 result = max_state_alt(result, jresult); 670 result = max_state_alt(result, jresult);
622 } 671 }
623 672
673 char *result_line;
624 switch (result) { 674 switch (result) {
625 case STATE_CRITICAL : 675 case STATE_CRITICAL:
626 xasprintf(&result_line, _("NTP CRITICAL:")); 676 xasprintf(&result_line, _("NTP CRITICAL:"));
627 break; 677 break;
628 case STATE_WARNING : 678 case STATE_WARNING:
629 xasprintf(&result_line, _("NTP WARNING:")); 679 xasprintf(&result_line, _("NTP WARNING:"));
630 break; 680 break;
631 case STATE_OK : 681 case STATE_OK:
632 xasprintf(&result_line, _("NTP OK:")); 682 xasprintf(&result_line, _("NTP OK:"));
633 break; 683 break;
634 default : 684 default:
635 xasprintf(&result_line, _("NTP UNKNOWN:")); 685 xasprintf(&result_line, _("NTP UNKNOWN:"));
636 break; 686 break;
637 } 687 }
638 if(!syncsource_found) 688
689 if (!syncsource_found) {
639 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); 690 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized"));
640 else if(li_alarm) 691 } else if (li_alarm) {
641 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 }
642 694
643 if(offset_result == STATE_UNKNOWN){ 695 char *perfdata_line;
696 if (ntp_res.offset_result == STATE_UNKNOWN) {
644 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 697 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
645 xasprintf(&perfdata_line, ""); 698 xasprintf(&perfdata_line, "");
646 } else if (oresult == STATE_WARNING) { 699 } else if (oresult == STATE_WARNING) {
647 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);
648 } else if (oresult == STATE_CRITICAL) { 702 } else if (oresult == STATE_CRITICAL) {
649 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);
650 } else { 705 } else {
651 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);
652 } 707 }
653 xasprintf(&perfdata_line, "%s", perfd_offset(offset)); 708 xasprintf(&perfdata_line, "%s", perfd_offset(ntp_res.offset, config.offset_thresholds));
654 709
655 if (do_jitter) { 710 if (config.do_jitter) {
656 if (jresult == STATE_WARNING) { 711 if (jresult == STATE_WARNING) {
657 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, jitter); 712 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, ntp_res.jitter);
658 } else if (jresult == STATE_CRITICAL) { 713 } else if (jresult == STATE_CRITICAL) {
659 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, jitter); 714 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, ntp_res.jitter);
660 } else { 715 } else {
661 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); 716 xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter);
662 } 717 }
663 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));
664 } 720 }
665 if (do_stratum) { 721
722 if (config.do_stratum) {
666 if (sresult == STATE_WARNING) { 723 if (sresult == STATE_WARNING) {
667 xasprintf(&result_line, "%s, stratum=%i (WARNING)", result_line, stratum); 724 xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum);
668 } else if (sresult == STATE_CRITICAL) { 725 } else if (sresult == STATE_CRITICAL) {
669 xasprintf(&result_line, "%s, stratum=%i (CRITICAL)", result_line, stratum); 726 xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum);
670 } else { 727 } else {
671 xasprintf(&result_line, "%s, stratum=%i", result_line, stratum); 728 xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum);
672 } 729 }
673 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));
674 } 732 }
675 if (do_truechimers) { 733
734 if (config.do_truechimers) {
676 if (tresult == STATE_WARNING) { 735 if (tresult == STATE_WARNING) {
677 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);
678 } else if (tresult == STATE_CRITICAL) { 738 } else if (tresult == STATE_CRITICAL) {
679 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);
680 } else { 741 } else {
681 xasprintf(&result_line, "%s, truechimers=%i", result_line, num_truechimers); 742 xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers);
682 } 743 }
683 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));
684 } 747 }
748
685 printf("%s|%s\n", result_line, perfdata_line); 749 printf("%s|%s\n", result_line, perfdata_line);
686 750
687 if(server_address!=NULL) free(server_address); 751 if (config.server_address != NULL) {
688 return result; 752 free(config.server_address);
753 }
754
755 exit(result);
689} 756}
690 757
691void print_help(void){ 758void print_help(void) {
692 print_revision(progname, NP_VERSION); 759 print_revision(progname, NP_VERSION);
693 760
694 printf ("Copyright (c) 2006 Sean Finney\n"); 761 printf("Copyright (c) 2006 Sean Finney\n");
695 printf (COPYRIGHT, copyright, email); 762 printf(COPYRIGHT, copyright, email);
696 763
697 printf ("%s\n", _("This plugin checks the selected ntp server")); 764 printf("%s\n", _("This plugin checks the selected ntp server"));
698 765
699 printf ("\n\n"); 766 printf("\n\n");
700 767
701 print_usage(); 768 print_usage();
702 printf (UT_HELP_VRSN); 769 printf(UT_HELP_VRSN);
703 printf (UT_EXTRA_OPTS); 770 printf(UT_EXTRA_OPTS);
704 printf (UT_IPv46); 771 printf(UT_IPv46);
705 printf (UT_HOST_PORT, 'p', "123"); 772 printf(UT_HOST_PORT, 'p', "123");
706 printf (" %s\n", "-q, --quiet"); 773 printf(" %s\n", "-q, --quiet");
707 printf (" %s\n", _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); 774 printf(" %s\n",
708 printf (" %s\n", "-w, --warning=THRESHOLD"); 775 _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized"));
709 printf (" %s\n", _("Offset to result in warning status (seconds)")); 776 printf(" %s\n", "-w, --warning=THRESHOLD");
710 printf (" %s\n", "-c, --critical=THRESHOLD"); 777 printf(" %s\n", _("Offset to result in warning status (seconds)"));
711 printf (" %s\n", _("Offset to result in critical status (seconds)")); 778 printf(" %s\n", "-c, --critical=THRESHOLD");
712 printf (" %s\n", "-W, --swarn=THRESHOLD"); 779 printf(" %s\n", _("Offset to result in critical status (seconds)"));
713 printf (" %s\n", _("Warning threshold for stratum of server's synchronization peer")); 780 printf(" %s\n", "-W, --swarn=THRESHOLD");
714 printf (" %s\n", "-C, --scrit=THRESHOLD"); 781 printf(" %s\n", _("Warning threshold for stratum of server's synchronization peer"));
715 printf (" %s\n", _("Critical threshold for stratum of server's synchronization peer")); 782 printf(" %s\n", "-C, --scrit=THRESHOLD");
716 printf (" %s\n", "-j, --jwarn=THRESHOLD"); 783 printf(" %s\n", _("Critical threshold for stratum of server's synchronization peer"));
717 printf (" %s\n", _("Warning threshold for jitter")); 784 printf(" %s\n", "-j, --jwarn=THRESHOLD");
718 printf (" %s\n", "-k, --jcrit=THRESHOLD"); 785 printf(" %s\n", _("Warning threshold for jitter"));
719 printf (" %s\n", _("Critical threshold for jitter")); 786 printf(" %s\n", "-k, --jcrit=THRESHOLD");
720 printf (" %s\n", "-m, --twarn=THRESHOLD"); 787 printf(" %s\n", _("Critical threshold for jitter"));
721 printf (" %s\n", _("Warning threshold for number of usable time sources (\"truechimers\")")); 788 printf(" %s\n", "-m, --twarn=THRESHOLD");
722 printf (" %s\n", "-n, --tcrit=THRESHOLD"); 789 printf(" %s\n", _("Warning threshold for number of usable time sources (\"truechimers\")"));
723 printf (" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")")); 790 printf(" %s\n", "-n, --tcrit=THRESHOLD");
724 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 791 printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")"));
725 printf (UT_VERBOSE); 792 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
793 printf(UT_VERBOSE);
726 794
727 printf("\n"); 795 printf("\n");
728 printf("%s\n", _("This plugin checks an NTP server independent of any commandline")); 796 printf("%s\n", _("This plugin checks an NTP server independent of any commandline"));
@@ -741,7 +809,8 @@ void print_help(void){
741 printf(" %s\n", _("Simple NTP server check:")); 809 printf(" %s\n", _("Simple NTP server check:"));
742 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"));
743 printf("\n"); 811 printf("\n");
744 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"));
745 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):"));
746 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"));
747 printf("\n"); 816 printf("\n");
@@ -751,13 +820,11 @@ void print_help(void){
751 printf(" %s\n", _("Check only stratum:")); 820 printf(" %s\n", _("Check only stratum:"));
752 printf(" %s\n", ("./check_ntp_peer -H ntpserv -W 4 -C 6")); 821 printf(" %s\n", ("./check_ntp_peer -H ntpserv -W 4 -C 6"));
753 822
754 printf (UT_SUPPORT); 823 printf(UT_SUPPORT);
755} 824}
756 825
757void 826void print_usage(void) {
758print_usage(void) 827 printf("%s\n", _("Usage:"));
759{
760 printf ("%s\n", _("Usage:"));
761 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-W <warn>] [-C <crit>]\n", progname); 828 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-W <warn>] [-C <crit>]\n", progname);
762 printf(" [-j <warn>] [-k <crit>] [-v verbose]\n"); 829 printf(" [-j <warn>] [-k <crit>] [-v verbose]\n");
763} 830}
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 b2e16556..ad69b804 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -1,63 +1,64 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ntp_time plugin 3 * Monitoring check_ntp_time plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006 Sean Finney <seanius@seanius.net> 6 * Copyright (c) 2006 Sean Finney <seanius@seanius.net>
7* Copyright (c) 2006-2008 Monitoring Plugins Development Team 7 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_ntp_time plugin 11 * This file contains the check_ntp_time plugin
12* 12 *
13* This plugin checks the clock offset between the local host and a 13 * This plugin checks the clock offset between the local host and a
14* remote NTP server. It is independent of any commandline programs or 14 * remote NTP server. It is independent of any commandline programs or
15* external libraries. 15 * external libraries.
16* 16 *
17* If you'd rather want to monitor an NTP server, please use 17 * If you'd rather want to monitor an NTP server, please use
18* check_ntp_peer. 18 * check_ntp_peer.
19* 19 *
20* 20 *
21* This program is free software: you can redistribute it and/or modify 21 * This program is free software: you can redistribute it and/or modify
22* it under the terms of the GNU General Public License as published by 22 * it under the terms of the GNU General Public License as published by
23* the Free Software Foundation, either version 3 of the License, or 23 * the Free Software Foundation, either version 3 of the License, or
24* (at your option) any later version. 24 * (at your option) any later version.
25* 25 *
26* This program is distributed in the hope that it will be useful, 26 * This program is distributed in the hope that it will be useful,
27* but WITHOUT ANY WARRANTY; without even the implied warranty of 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29* GNU General Public License for more details. 29 * GNU General Public License for more details.
30* 30 *
31* You should have received a copy of the GNU General Public License 31 * You should have received a copy of the GNU General Public License
32* along with this program. If not, see <http://www.gnu.org/licenses/>. 32 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33* 33 *
34* 34 *
35*****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_ntp_time"; 37const char *progname = "check_ntp_time";
38const char *copyright = "2006-2008"; 38const char *copyright = "2006-2024";
39const char *email = "devel@monitoring-plugins.org"; 39const char *email = "devel@monitoring-plugins.org";
40 40
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; 48static int verbose = 0;
46static char *port="123";
47static int verbose=0;
48static bool quiet = false;
49static char *owarn="60";
50static char *ocrit="120";
51static int time_offset=0;
52 49
53int process_arguments (int, char **); 50typedef struct {
54thresholds *offset_thresholds = NULL; 51 int errorcode;
55void print_help (void); 52 check_ntp_time_config config;
56void print_usage (void); 53} check_ntp_time_config_wrapper;
54static check_ntp_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
55
56static void print_help(void);
57void print_usage(void);
57 58
58/* number of times to perform each request to get a good average. */ 59/* number of times to perform each request to get a good average. */
59#ifndef AVG_NUM 60#ifndef AVG_NUM
60#define AVG_NUM 4 61# define AVG_NUM 4
61#endif 62#endif
62 63
63/* max size of control message data */ 64/* max size of control message data */
@@ -65,17 +66,17 @@ void print_usage (void);
65 66
66/* this structure holds everything in an ntp request/response as per rfc1305 */ 67/* this structure holds everything in an ntp request/response as per rfc1305 */
67typedef struct { 68typedef struct {
68 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 69 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
69 uint8_t stratum; /* clock stratum */ 70 uint8_t stratum; /* clock stratum */
70 int8_t poll; /* polling interval */ 71 int8_t poll; /* polling interval */
71 int8_t precision; /* precision of the local clock */ 72 int8_t precision; /* precision of the local clock */
72 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */ 73 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */
73 uint32_t rtdisp; /* like above, but for max err to primary src */ 74 uint32_t rtdisp; /* like above, but for max err to primary src */
74 uint32_t refid; /* ref clock identifier */ 75 uint32_t refid; /* ref clock identifier */
75 uint64_t refts; /* reference timestamp. local time local clock */ 76 uint64_t refts; /* reference timestamp. local time local clock */
76 uint64_t origts; /* time at which request departed client */ 77 uint64_t origts; /* time at which request departed client */
77 uint64_t rxts; /* time at which request arrived at server */ 78 uint64_t rxts; /* time at which request arrived at server */
78 uint64_t txts; /* time at which request departed server */ 79 uint64_t txts; /* time at which request departed server */
79} ntp_message; 80} ntp_message;
80 81
81/* this structure holds data about results from querying offset from a peer */ 82/* this structure holds data about results from querying offset from a peer */
@@ -86,43 +87,55 @@ typedef struct {
86 double rtdelay; /* converted from the ntp_message */ 87 double rtdelay; /* converted from the ntp_message */
87 double rtdisp; /* converted from the ntp_message */ 88 double rtdisp; /* converted from the ntp_message */
88 double offset[AVG_NUM]; /* offsets from each response */ 89 double offset[AVG_NUM]; /* offsets from each response */
89 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 90 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
90} ntp_server_results; 91} ntp_server_results;
91 92
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) do{ x |= ((y<<6)&LI_MASK); }while(0) 96#define LI_SET(x, y) \
97 do { \
98 x |= ((y << 6) & LI_MASK); \
99 } while (0)
96/* and these are the values of the leap indicator */ 100/* and these are the values of the leap indicator */
97#define LI_NOWARNING 0x00 101#define LI_NOWARNING 0x00
98#define LI_EXTRASEC 0x01 102#define LI_EXTRASEC 0x01
99#define LI_MISSINGSEC 0x02 103#define LI_MISSINGSEC 0x02
100#define LI_ALARM 0x03 104#define LI_ALARM 0x03
101/* bits 3,4,5 are the ntp version */ 105/* bits 3,4,5 are the ntp version */
102#define VN_MASK 0x38 106#define VN_MASK 0x38
103#define VN(x) ((x&VN_MASK)>>3) 107#define VN(x) ((x & VN_MASK) >> 3)
104#define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) 108#define VN_SET(x, y) \
109 do { \
110 x |= ((y << 3) & VN_MASK); \
111 } while (0)
105#define VN_RESERVED 0x02 112#define VN_RESERVED 0x02
106/* bits 6,7,8 are the ntp mode */ 113/* bits 6,7,8 are the ntp mode */
107#define MODE_MASK 0x07 114#define MODE_MASK 0x07
108#define MODE(x) (x&MODE_MASK) 115#define MODE(x) (x & MODE_MASK)
109#define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) 116#define MODE_SET(x, y) \
117 do { \
118 x |= (y & MODE_MASK); \
119 } while (0)
110/* here are some values */ 120/* here are some values */
111#define MODE_CLIENT 0x03 121#define MODE_CLIENT 0x03
112#define MODE_CONTROLMSG 0x06 122#define MODE_CONTROLMSG 0x06
113/* In control message, bits 8-10 are R,E,M bits */ 123/* In control message, bits 8-10 are R,E,M bits */
114#define REM_MASK 0xe0 124#define REM_MASK 0xe0
115#define REM_RESP 0x80 125#define REM_RESP 0x80
116#define REM_ERROR 0x40 126#define REM_ERROR 0x40
117#define REM_MORE 0x20 127#define REM_MORE 0x20
118/* In control message, bits 11 - 15 are opcode */ 128/* In control message, bits 11 - 15 are opcode */
119#define OP_MASK 0x1f 129#define OP_MASK 0x1f
120#define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) 130#define OP_SET(x, y) \
131 do { \
132 x |= (y & OP_MASK); \
133 } while (0)
121#define OP_READSTAT 0x01 134#define OP_READSTAT 0x01
122#define OP_READVAR 0x02 135#define OP_READVAR 0x02
123/* In peer status bytes, bits 6,7,8 determine clock selection status */ 136/* In peer status bytes, bits 6,7,8 determine clock selection status */
124#define PEER_SEL(x) ((ntohs(x)>>8)&0x07) 137#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07)
125#define PEER_INCLUDED 0x04 138#define PEER_INCLUDED 0x04
126#define PEER_SYNCSOURCE 0x06 139#define PEER_SYNCSOURCE 0x06
127 140
128/** 141/**
@@ -136,129 +149,139 @@ typedef struct {
136 149
137/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point" 150/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point"
138 number. note that these can be used as lvalues too */ 151 number. note that these can be used as lvalues too */
139#define L16(x) (((uint16_t*)&x)[0]) 152#define L16(x) (((uint16_t *)&x)[0])
140#define R16(x) (((uint16_t*)&x)[1]) 153#define R16(x) (((uint16_t *)&x)[1])
141/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point" 154/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
142 number. these too can be used as lvalues */ 155 number. these too can be used as lvalues */
143#define L32(x) (((uint32_t*)&x)[0]) 156#define L32(x) (((uint32_t *)&x)[0])
144#define R32(x) (((uint32_t*)&x)[1]) 157#define R32(x) (((uint32_t *)&x)[1])
145 158
146/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */ 159/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */
147#define EPOCHDIFF 0x83aa7e80UL 160#define EPOCHDIFF 0x83aa7e80UL
148 161
149/* extract a 32-bit ntp fixed point number into a double */ 162/* extract a 32-bit ntp fixed point number into a double */
150#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))
151 164
152/* likewise for a 64-bit ntp fp number */ 165/* likewise for a 64-bit ntp fp number */
153#define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\ 166#define NTP64asDOUBLE(n) \
154 (ntohl(L32(n))-EPOCHDIFF) + \ 167 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \
155 (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\ 168 (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \
156 0) 169 : 0)
157 170
158/* convert a struct timeval to a double */ 171/* convert a struct timeval to a double */
159#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))
160 173
161/* convert an ntp 64-bit fp number to a struct timeval */ 174/* convert an ntp 64-bit fp number to a struct timeval */
162#define NTP64toTV(n,t) \ 175#define NTP64toTV(n, t) \
163 do{ if(!n) t.tv_sec = t.tv_usec = 0; \ 176 do { \
164 else { \ 177 if (!n) \
165 t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \ 178 t.tv_sec = t.tv_usec = 0; \
166 t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \ 179 else { \
167 } \ 180 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \
168 }while(0) 181 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \
182 } \
183 } while (0)
169 184
170/* convert a struct timeval to an ntp 64-bit fp number */ 185/* convert a struct timeval to an ntp 64-bit fp number */
171#define TVtoNTP64(t,n) \ 186#define TVtoNTP64(t, n) \
172 do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \ 187 do { \
173 else { \ 188 if (!t.tv_usec && !t.tv_sec) \
174 L32(n)=htonl(t.tv_sec + EPOCHDIFF); \ 189 n = 0x0UL; \
175 R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \ 190 else { \
176 } \ 191 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \
177 } while(0) 192 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \
193 } \
194 } while (0)
178 195
179/* 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
180 * field, plus null padding to the nearest 32-bit boundary per rfc. 197 * field, plus null padding to the nearest 32-bit boundary per rfc.
181 */ 198 */
182#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))
183 200
184/* finally, a little helper or two for debugging: */ 201/* finally, a little helper or two for debugging: */
185#define DBG(x) do{if(verbose>1){ x; }}while(0); 202#define DBG(x) \
186#define PRINTSOCKADDR(x) \ 203 do { \
187 do{ \ 204 if (verbose > 1) { \
188 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ 205 x; \
189 }while(0); 206 } \
207 } while (0);
208#define PRINTSOCKADDR(x) \
209 do { \
210 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
211 } while (0);
190 212
191/* calculate the offset of the local clock */ 213/* calculate the offset of the local clock */
192static 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) {
193 double client_tx, peer_rx, peer_tx, client_rx; 215 double client_tx = NTP64asDOUBLE(message->origts);
194 client_tx = NTP64asDOUBLE(m->origts); 216 double peer_rx = NTP64asDOUBLE(message->rxts);
195 peer_rx = NTP64asDOUBLE(m->rxts); 217 double peer_tx = NTP64asDOUBLE(message->txts);
196 peer_tx = NTP64asDOUBLE(m->txts); 218 double client_rx = TVasDOUBLE((*time_value));
197 client_rx=TVasDOUBLE((*t)); 219 return (((peer_tx - client_rx) + (peer_rx - client_tx)) / 2);
198 return (.5*((peer_tx-client_rx)+(peer_rx-client_tx)));
199} 220}
200 221
201/* print out a ntp packet in human readable/debuggable format */ 222/* print out a ntp packet in human readable/debuggable format */
202void print_ntp_message(const ntp_message *p){ 223void print_ntp_message(const ntp_message *message) {
203 struct timeval ref, orig, rx, tx; 224 struct timeval ref;
225 struct timeval orig;
204 226
205 NTP64toTV(p->refts,ref); 227 NTP64toTV(message->refts, ref);
206 NTP64toTV(p->origts,orig); 228 NTP64toTV(message->origts, orig);
207 NTP64toTV(p->rxts,rx);
208 NTP64toTV(p->txts,tx);
209 229
210 printf("packet contents:\n"); 230 printf("packet contents:\n");
211 printf("\tflags: 0x%.2x\n", p->flags); 231 printf("\tflags: 0x%.2x\n", message->flags);
212 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);
213 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);
214 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);
215 printf("\tstratum = %d\n", p->stratum); 235 printf("\tstratum = %d\n", message->stratum);
216 printf("\tpoll = %g\n", pow(2, p->poll)); 236 printf("\tpoll = %g\n", pow(2, message->poll));
217 printf("\tprecision = %g\n", pow(2, p->precision)); 237 printf("\tprecision = %g\n", pow(2, message->precision));
218 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay)); 238 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(message->rtdelay));
219 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp)); 239 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(message->rtdisp));
220 printf("\trefid = %x\n", p->refid); 240 printf("\trefid = %x\n", message->refid);
221 printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts)); 241 printf("\trefts = %-.16g\n", NTP64asDOUBLE(message->refts));
222 printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts)); 242 printf("\torigts = %-.16g\n", NTP64asDOUBLE(message->origts));
223 printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts)); 243 printf("\trxts = %-.16g\n", NTP64asDOUBLE(message->rxts));
224 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); 244 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(message->txts));
225} 245}
226 246
227void setup_request(ntp_message *p){ 247void setup_request(ntp_message *message) {
228 struct timeval t; 248 memset(message, 0, sizeof(ntp_message));
229 249 LI_SET(message->flags, LI_ALARM);
230 memset(p, 0, sizeof(ntp_message)); 250 VN_SET(message->flags, 4);
231 LI_SET(p->flags, LI_ALARM); 251 MODE_SET(message->flags, MODE_CLIENT);
232 VN_SET(p->flags, 4); 252 message->poll = 4;
233 MODE_SET(p->flags, MODE_CLIENT); 253 message->precision = (int8_t)0xfa;
234 p->poll=4; 254 L16(message->rtdelay) = htons(1);
235 p->precision=(int8_t)0xfa; 255 L16(message->rtdisp) = htons(1);
236 L16(p->rtdelay)=htons(1);
237 L16(p->rtdisp)=htons(1);
238 256
257 struct timeval t;
239 gettimeofday(&t, NULL); 258 gettimeofday(&t, NULL);
240 TVtoNTP64(t,p->txts); 259 TVtoNTP64(t, message->txts);
241} 260}
242 261
243/* 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.
244 * this is done by filtering servers based on stratum, dispersion, and 263 * this is done by filtering servers based on stratum, dispersion, and
245 * finally round-trip delay. */ 264 * finally round-trip delay. */
246int best_offset_server(const ntp_server_results *slist, int nservers){ 265int best_offset_server(const ntp_server_results *slist, int nservers) {
247 int cserver=0, best_server=-1; 266 int best_server = -1;
248 267
249 /* for each server */ 268 /* for each server */
250 for(cserver=0; cserver<nservers; cserver++){ 269 for (int cserver = 0; cserver < nservers; cserver++) {
251 /* We don't want any servers that fails these tests */ 270 /* We don't want any servers that fails these tests */
252 /* Sort out servers that didn't respond or responede with a 0 stratum; 271 /* Sort out servers that didn't respond or responede with a 0 stratum;
253 * stratum 0 is for reference clocks so no NTP server should ever report 272 * stratum 0 is for reference clocks so no NTP server should ever report
254 * a stratum 0 */ 273 * a stratum 0 */
255 if ( slist[cserver].stratum == 0){ 274 if (slist[cserver].stratum == 0) {
256 if (verbose) printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); 275 if (verbose) {
276 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
277 }
257 continue; 278 continue;
258 } 279 }
259 /* Sort out servers with error flags */ 280 /* Sort out servers with error flags */
260 if ( LI(slist[cserver].flags) == LI_ALARM ){ 281 if (LI(slist[cserver].flags) == LI_ALARM) {
261 if (verbose) printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); 282 if (verbose) {
283 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
284 }
262 continue; 285 continue;
263 } 286 }
264 287
@@ -272,13 +295,13 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
272 /* compare the server to the best one we've seen so far */ 295 /* compare the server to the best one we've seen so far */
273 /* does it have an equal or better stratum? */ 296 /* does it have an equal or better stratum? */
274 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); 297 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server));
275 if(slist[cserver].stratum <= slist[best_server].stratum){ 298 if (slist[cserver].stratum <= slist[best_server].stratum) {
276 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); 299 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server));
277 /* does it have an equal or better dispersion? */ 300 /* does it have an equal or better dispersion? */
278 if(slist[cserver].rtdisp <= slist[best_server].rtdisp){ 301 if (slist[cserver].rtdisp <= slist[best_server].rtdisp) {
279 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); 302 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server));
280 /* does it have a better rtdelay? */ 303 /* does it have a better rtdelay? */
281 if(slist[cserver].rtdelay < slist[best_server].rtdelay){ 304 if (slist[cserver].rtdelay < slist[best_server].rtdelay) {
282 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); 305 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server));
283 best_server = cserver; 306 best_server = cserver;
284 DBG(printf("peer %d is now our best candidate\n", best_server)); 307 DBG(printf("peer %d is now our best candidate\n", best_server));
@@ -287,13 +310,12 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
287 } 310 }
288 } 311 }
289 312
290 if(best_server >= 0) { 313 if (best_server >= 0) {
291 DBG(printf("best server selected: peer %d\n", best_server)); 314 DBG(printf("best server selected: peer %d\n", best_server));
292 return best_server; 315 return best_server;
293 } else {
294 DBG(printf("no peers meeting synchronization criteria :(\n"));
295 return -1;
296 } 316 }
317 DBG(printf("no peers meeting synchronization criteria :(\n"));
318 return -1;
297} 319}
298 320
299/* do everything we need to get the total average offset 321/* do everything we need to get the total average offset
@@ -301,178 +323,209 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
301 * we don't waste time sitting around waiting for single packets. 323 * we don't waste time sitting around waiting for single packets.
302 * - we also "manually" handle resolving host names and connecting, because 324 * - we also "manually" handle resolving host names and connecting, because
303 * we have to do it in a way that our lazy macros don't handle currently :( */ 325 * we have to do it in a way that our lazy macros don't handle currently :( */
304double offset_request(const char *host, int *status){ 326double offset_request(const char *host, const char *port, mp_state_enum *status, int time_offset) {
305 int i=0, j=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0;
306 int servers_completed=0, one_read=0, servers_readable=0, best_index=-1;
307 time_t now_time=0, start_ts=0;
308 ntp_message *req=NULL;
309 double avg_offset=0.;
310 struct timeval recv_time;
311 struct addrinfo *ai=NULL, *ai_tmp=NULL, hints;
312 struct pollfd *ufds=NULL;
313 ntp_server_results *servers=NULL;
314
315 /* setup hints to only return results from getaddrinfo that we'd like */ 327 /* setup hints to only return results from getaddrinfo that we'd like */
328 struct addrinfo hints;
316 memset(&hints, 0, sizeof(struct addrinfo)); 329 memset(&hints, 0, sizeof(struct addrinfo));
317 hints.ai_family = address_family; 330 hints.ai_family = address_family;
318 hints.ai_protocol = IPPROTO_UDP; 331 hints.ai_protocol = IPPROTO_UDP;
319 hints.ai_socktype = SOCK_DGRAM; 332 hints.ai_socktype = SOCK_DGRAM;
320 333
321 /* 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 */
322 ga_result = getaddrinfo(host, port, &hints, &ai); 335 struct addrinfo *addresses = NULL;
323 if(ga_result!=0){ 336 int ga_result = getaddrinfo(host, port, &hints, &addresses);
324 die(STATE_UNKNOWN, "error getting address for %s: %s\n", 337 if (ga_result != 0) {
325 host, gai_strerror(ga_result)); 338 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
326 } 339 }
327 340
328 /* count the number of returned hosts, and allocate stuff accordingly */ 341 /* count the number of returned hosts, and allocate stuff accordingly */
329 for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; } 342 size_t num_hosts = 0;
330 req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts); 343 for (struct addrinfo *ai_tmp = addresses; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
331 if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array"); 344 num_hosts++;
332 socklist=(int*)malloc(sizeof(int)*num_hosts); 345 }
333 if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); 346
334 ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts); 347 ntp_message *req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts);
335 if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); 348
336 servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts); 349 if (req == NULL) {
337 if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array"); 350 die(STATE_UNKNOWN, "can not allocate ntp message array");
338 memset(servers, 0, sizeof(ntp_server_results)*num_hosts); 351 }
339 DBG(printf("Found %d peers to check\n", num_hosts)); 352 int *socklist = (int *)malloc(sizeof(int) * num_hosts);
353
354 if (socklist == NULL) {
355 die(STATE_UNKNOWN, "can not allocate socket array");
356 }
357
358 struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts);
359 if (ufds == NULL) {
360 die(STATE_UNKNOWN, "can not allocate socket array");
361 }
362
363 ntp_server_results *servers =
364 (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts);
365 if (servers == NULL) {
366 die(STATE_UNKNOWN, "can not allocate server array");
367 }
368 memset(servers, 0, sizeof(ntp_server_results) * num_hosts);
369 DBG(printf("Found %zu peers to check\n", num_hosts));
340 370
341 /* setup each socket for writing, and the corresponding struct pollfd */ 371 /* setup each socket for writing, and the corresponding struct pollfd */
342 ai_tmp=ai; 372 struct addrinfo *ai_tmp = addresses;
343 for(i=0;ai_tmp;i++){ 373 for (int i = 0; ai_tmp; i++) {
344 socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); 374 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
345 if(socklist[i] == -1) { 375 if (socklist[i] == -1) {
346 perror(NULL); 376 perror(NULL);
347 die(STATE_UNKNOWN, "can not create new socket"); 377 die(STATE_UNKNOWN, "can not create new socket");
348 } 378 }
349 if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){ 379 if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) {
350 /* don't die here, because it is enough if there is one server 380 /* don't die here, because it is enough if there is one server
351 answering in time. This also would break for dual ipv4/6 stacked 381 answering in time. This also would break for dual ipv4/6 stacked
352 ntp servers when the client only supports on of them. 382 ntp servers when the client only supports on of them.
353 */ 383 */
354 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); 384 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno)));
355 } else { 385 } else {
356 ufds[i].fd=socklist[i]; 386 ufds[i].fd = socklist[i];
357 ufds[i].events=POLLIN; 387 ufds[i].events = POLLIN;
358 ufds[i].revents=0; 388 ufds[i].revents = 0;
359 } 389 }
360 ai_tmp = ai_tmp->ai_next; 390 ai_tmp = ai_tmp->ai_next;
361 } 391 }
362 392
363 /* now do AVG_NUM checks to each host. We stop before timeout/2 seconds 393 /* now do AVG_NUM checks to each host. We stop before timeout/2 seconds
364 * have passed in order to ensure post-processing and jitter time. */ 394 * have passed in order to ensure post-processing and jitter time. */
365 now_time=start_ts=time(NULL); 395 time_t start_ts = 0;
366 while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){ 396 time_t now_time = 0;
397 now_time = start_ts = time(NULL);
398 size_t servers_completed = 0;
399 bool one_read = false;
400 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) {
367 /* loop through each server and find each one which hasn't 401 /* loop through each server and find each one which hasn't
368 * been touched in the past second or so and is still lacking 402 * been touched in the past second or so and is still lacking
369 * some responses. For each of these servers, send a new request, 403 * some responses. For each of these servers, send a new request,
370 * and update the "waiting" timestamp with the current time. */ 404 * and update the "waiting" timestamp with the current time. */
371 now_time=time(NULL); 405 now_time = time(NULL);
372 406
373 for(i=0; i<num_hosts; i++){ 407 for (size_t i = 0; i < num_hosts; i++) {
374 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) {
375 if(verbose && servers[i].waiting != 0) printf("re-"); 409 if (verbose && servers[i].waiting != 0) {
376 if(verbose) printf("sending request to peer %d\n", i); 410 printf("re-");
411 }
412 if (verbose) {
413 printf("sending request to peer %zu\n", i);
414 }
377 setup_request(&req[i]); 415 setup_request(&req[i]);
378 write(socklist[i], &req[i], sizeof(ntp_message)); 416 write(socklist[i], &req[i], sizeof(ntp_message));
379 servers[i].waiting=now_time; 417 servers[i].waiting = now_time;
380 break; 418 break;
381 } 419 }
382 } 420 }
383 421
384 /* quickly poll for any sockets with pending data */ 422 /* quickly poll for any sockets with pending data */
385 servers_readable=poll(ufds, num_hosts, 100); 423 int servers_readable = poll(ufds, num_hosts, 100);
386 if(servers_readable==-1){ 424 if (servers_readable == -1) {
387 perror("polling ntp sockets"); 425 perror("polling ntp sockets");
388 die(STATE_UNKNOWN, "communication errors"); 426 die(STATE_UNKNOWN, "communication errors");
389 } 427 }
390 428
391 /* read from any sockets with pending data */ 429 /* read from any sockets with pending data */
392 for(i=0; servers_readable && i<num_hosts; i++){ 430 for (size_t i = 0; servers_readable && i < num_hosts; i++) {
393 if(ufds[i].revents&POLLIN && servers[i].num_responses < AVG_NUM){ 431 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) {
394 if(verbose) { 432 if (verbose) {
395 printf("response from peer %d: ", i); 433 printf("response from peer %zu: ", i);
396 } 434 }
397 435
398 read(ufds[i].fd, &req[i], sizeof(ntp_message)); 436 read(ufds[i].fd, &req[i], sizeof(ntp_message));
437
438 struct timeval recv_time;
399 gettimeofday(&recv_time, NULL); 439 gettimeofday(&recv_time, NULL);
400 DBG(print_ntp_message(&req[i])); 440 DBG(print_ntp_message(&req[i]));
401 respnum=servers[i].num_responses++; 441 int respnum = servers[i].num_responses++;
402 servers[i].offset[respnum]=calc_offset(&req[i], &recv_time)+time_offset; 442 servers[i].offset[respnum] = calc_offset(&req[i], &recv_time) + time_offset;
403 if(verbose) { 443 if (verbose) {
404 printf("offset %.10g\n", servers[i].offset[respnum]); 444 printf("offset %.10g\n", servers[i].offset[respnum]);
405 } 445 }
406 servers[i].stratum=req[i].stratum; 446 servers[i].stratum = req[i].stratum;
407 servers[i].rtdisp=NTP32asDOUBLE(req[i].rtdisp); 447 servers[i].rtdisp = NTP32asDOUBLE(req[i].rtdisp);
408 servers[i].rtdelay=NTP32asDOUBLE(req[i].rtdelay); 448 servers[i].rtdelay = NTP32asDOUBLE(req[i].rtdelay);
409 servers[i].waiting=0; 449 servers[i].waiting = 0;
410 servers[i].flags=req[i].flags; 450 servers[i].flags = req[i].flags;
411 servers_readable--; 451 servers_readable--;
412 one_read = 1; 452 one_read = true;
413 if(servers[i].num_responses==AVG_NUM) servers_completed++; 453 if (servers[i].num_responses == AVG_NUM) {
454 servers_completed++;
455 }
414 } 456 }
415 } 457 }
416 /* lather, rinse, repeat. */ 458 /* lather, rinse, repeat. */
417 } 459 }
418 460
419 if (one_read == 0) { 461 if (!one_read) {
420 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 462 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
421 } 463 }
422 464
423 /* now, pick the best server from the list */ 465 /* now, pick the best server from the list */
424 best_index=best_offset_server(servers, num_hosts); 466 double avg_offset = 0.;
425 if(best_index < 0){ 467 int best_index = best_offset_server(servers, num_hosts);
426 *status=STATE_UNKNOWN; 468 if (best_index < 0) {
469 *status = STATE_UNKNOWN;
427 } else { 470 } else {
428 /* finally, calculate the average offset */ 471 /* finally, calculate the average offset */
429 for(i=0; i<servers[best_index].num_responses;i++){ 472 for (int i = 0; i < servers[best_index].num_responses; i++) {
430 avg_offset+=servers[best_index].offset[i]; 473 avg_offset += servers[best_index].offset[i];
431 } 474 }
432 avg_offset/=servers[best_index].num_responses; 475 avg_offset /= servers[best_index].num_responses;
433 } 476 }
434 477
435 /* cleanup */ 478 /* cleanup */
436 for(j=0; j<num_hosts; j++){ close(socklist[j]); } 479 for (size_t j = 0; j < num_hosts; j++) {
480 close(socklist[j]);
481 }
437 free(socklist); 482 free(socklist);
438 free(ufds); 483 free(ufds);
439 free(servers); 484 free(servers);
440 free(req); 485 free(req);
441 freeaddrinfo(ai); 486 freeaddrinfo(addresses);
442 487
443 if(verbose) printf("overall average offset: %.10g\n", avg_offset); 488 if (verbose) {
489 printf("overall average offset: %.10g\n", avg_offset);
490 }
444 return avg_offset; 491 return avg_offset;
445} 492}
446 493
447int process_arguments(int argc, char **argv){ 494check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
448 int c; 495 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
449 int option=0; 496 {"help", no_argument, 0, 'h'},
450 static struct option longopts[] = { 497 {"verbose", no_argument, 0, 'v'},
451 {"version", no_argument, 0, 'V'}, 498 {"use-ipv4", no_argument, 0, '4'},
452 {"help", no_argument, 0, 'h'}, 499 {"use-ipv6", no_argument, 0, '6'},
453 {"verbose", no_argument, 0, 'v'}, 500 {"quiet", no_argument, 0, 'q'},
454 {"use-ipv4", no_argument, 0, '4'}, 501 {"time-offset", optional_argument, 0, 'o'},
455 {"use-ipv6", no_argument, 0, '6'}, 502 {"warning", required_argument, 0, 'w'},
456 {"quiet", no_argument, 0, 'q'}, 503 {"critical", required_argument, 0, 'c'},
457 {"time-offset", optional_argument, 0, 'o'}, 504 {"timeout", required_argument, 0, 't'},
458 {"warning", required_argument, 0, 'w'}, 505 {"hostname", required_argument, 0, 'H'},
459 {"critical", required_argument, 0, 'c'}, 506 {"port", required_argument, 0, 'p'},
460 {"timeout", required_argument, 0, 't'}, 507 {0, 0, 0, 0}};
461 {"hostname", required_argument, 0, 'H'}, 508
462 {"port", required_argument, 0, 'p'}, 509 if (argc < 2) {
463 {0, 0, 0, 0} 510 usage("\n");
464 }; 511 }
465 512
513 check_ntp_time_config_wrapper result = {
514 .errorcode = OK,
515 .config = check_ntp_time_config_init(),
516 };
466 517
467 if (argc < 2) 518 char *owarn = "60";
468 usage ("\n"); 519 char *ocrit = "120";
469 520
470 while (1) { 521 while (true) {
471 c = getopt_long (argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); 522 int option = 0;
472 if (c == -1 || c == EOF || c == 1) 523 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option);
524 if (option_char == -1 || option_char == EOF || option_char == 1) {
473 break; 525 break;
526 }
474 527
475 switch (c) { 528 switch (option_char) {
476 case 'h': 529 case 'h':
477 print_help(); 530 print_help();
478 exit(STATE_UNKNOWN); 531 exit(STATE_UNKNOWN);
@@ -485,7 +538,7 @@ int process_arguments(int argc, char **argv){
485 verbose++; 538 verbose++;
486 break; 539 break;
487 case 'q': 540 case 'q':
488 quiet = true; 541 result.config.quiet = true;
489 break; 542 break;
490 case 'w': 543 case 'w':
491 owarn = optarg; 544 owarn = optarg;
@@ -494,19 +547,20 @@ int process_arguments(int argc, char **argv){
494 ocrit = optarg; 547 ocrit = optarg;
495 break; 548 break;
496 case 'H': 549 case 'H':
497 if(!is_host(optarg)) 550 if (!is_host(optarg)) {
498 usage2(_("Invalid hostname/address"), optarg); 551 usage2(_("Invalid hostname/address"), optarg);
499 server_address = strdup(optarg); 552 }
553 result.config.server_address = strdup(optarg);
500 break; 554 break;
501 case 'p': 555 case 'p':
502 port = strdup(optarg); 556 result.config.port = strdup(optarg);
503 break; 557 break;
504 case 't': 558 case 't':
505 socket_timeout=atoi(optarg); 559 socket_timeout = atoi(optarg);
506 break; 560 break;
507 case 'o': 561 case 'o':
508 time_offset=atoi(optarg); 562 result.config.time_offset = atoi(optarg);
509 break; 563 break;
510 case '4': 564 case '4':
511 address_family = AF_INET; 565 address_family = AF_INET;
512 break; 566 break;
@@ -514,114 +568,119 @@ int process_arguments(int argc, char **argv){
514#ifdef USE_IPV6 568#ifdef USE_IPV6
515 address_family = AF_INET6; 569 address_family = AF_INET6;
516#else 570#else
517 usage4 (_("IPv6 support not available")); 571 usage4(_("IPv6 support not available"));
518#endif 572#endif
519 break; 573 break;
520 case '?': 574 case '?':
521 /* print short usage statement if args not parsable */ 575 /* print short usage statement if args not parsable */
522 usage5 (); 576 usage5();
523 break; 577 break;
524 } 578 }
525 } 579 }
526 580
527 if(server_address == NULL){ 581 if (result.config.server_address == NULL) {
528 usage4(_("Hostname was not supplied")); 582 usage4(_("Hostname was not supplied"));
529 } 583 }
530 584
531 return 0; 585 set_thresholds(&result.config.offset_thresholds, owarn, ocrit);
532}
533 586
534char *perfd_offset (double offset) { 587 return result;
535 return fperfdata ("offset", offset, "s",
536 true, offset_thresholds->warning->end,
537 true, offset_thresholds->critical->end,
538 false, 0, false, 0);
539} 588}
540 589
541int main(int argc, char *argv[]){ 590char *perfd_offset(double offset, thresholds *offset_thresholds) {
542 int result, offset_result; 591 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
543 double offset=0; 592 offset_thresholds->critical->end, false, 0, false, 0);
544 char *result_line, *perfdata_line; 593}
545
546 setlocale (LC_ALL, "");
547 bindtextdomain (PACKAGE, LOCALEDIR);
548 textdomain (PACKAGE);
549 594
550 result = offset_result = STATE_OK; 595int main(int argc, char *argv[]) {
596 setlocale(LC_ALL, "");
597 bindtextdomain(PACKAGE, LOCALEDIR);
598 textdomain(PACKAGE);
551 599
552 /* Parse extra opts if any */ 600 /* Parse extra opts if any */
553 argv=np_extra_opts (&argc, argv, progname); 601 argv = np_extra_opts(&argc, argv, progname);
554 602
555 if (process_arguments (argc, argv) == ERROR) 603 check_ntp_time_config_wrapper tmp_config = process_arguments(argc, argv);
556 usage4 (_("Could not parse arguments"));
557 604
558 set_thresholds(&offset_thresholds, owarn, ocrit); 605 if (tmp_config.errorcode == ERROR) {
606 usage4(_("Could not parse arguments"));
607 }
608
609 const check_ntp_time_config config = tmp_config.config;
559 610
560 /* initialize alarm signal handling */ 611 /* initialize alarm signal handling */
561 signal (SIGALRM, socket_timeout_alarm_handler); 612 signal(SIGALRM, socket_timeout_alarm_handler);
562 613
563 /* set socket timeout */ 614 /* set socket timeout */
564 alarm (socket_timeout); 615 alarm(socket_timeout);
565 616
566 offset = offset_request(server_address, &offset_result); 617 mp_state_enum offset_result = STATE_OK;
618 mp_state_enum result = STATE_OK;
619 double offset =
620 offset_request(config.server_address, config.port, &offset_result, config.time_offset);
567 if (offset_result == STATE_UNKNOWN) { 621 if (offset_result == STATE_UNKNOWN) {
568 result = ( (!quiet) ? STATE_UNKNOWN : STATE_CRITICAL); 622 result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
569 } else { 623 } else {
570 result = get_status(fabs(offset), offset_thresholds); 624 result = get_status(fabs(offset), config.offset_thresholds);
571 } 625 }
572 626
627 char *result_line;
573 switch (result) { 628 switch (result) {
574 case STATE_CRITICAL : 629 case STATE_CRITICAL:
575 xasprintf(&result_line, _("NTP CRITICAL:")); 630 xasprintf(&result_line, _("NTP CRITICAL:"));
576 break; 631 break;
577 case STATE_WARNING : 632 case STATE_WARNING:
578 xasprintf(&result_line, _("NTP WARNING:")); 633 xasprintf(&result_line, _("NTP WARNING:"));
579 break; 634 break;
580 case STATE_OK : 635 case STATE_OK:
581 xasprintf(&result_line, _("NTP OK:")); 636 xasprintf(&result_line, _("NTP OK:"));
582 break; 637 break;
583 default : 638 default:
584 xasprintf(&result_line, _("NTP UNKNOWN:")); 639 xasprintf(&result_line, _("NTP UNKNOWN:"));
585 break; 640 break;
586 } 641 }
587 if(offset_result == STATE_UNKNOWN){ 642
643 char *perfdata_line;
644 if (offset_result == STATE_UNKNOWN) {
588 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 645 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
589 xasprintf(&perfdata_line, ""); 646 xasprintf(&perfdata_line, "");
590 } else { 647 } else {
591 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 648 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
592 xasprintf(&perfdata_line, "%s", perfd_offset(offset)); 649 xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds));
593 } 650 }
594 printf("%s|%s\n", result_line, perfdata_line); 651 printf("%s|%s\n", result_line, perfdata_line);
595 652
596 if(server_address!=NULL) free(server_address); 653 if (config.server_address != NULL) {
597 return result; 654 free(config.server_address);
655 }
656 exit(result);
598} 657}
599 658
600void print_help(void){ 659void print_help(void) {
601 print_revision(progname, NP_VERSION); 660 print_revision(progname, NP_VERSION);
602 661
603 printf ("Copyright (c) 2006 Sean Finney\n"); 662 printf("Copyright (c) 2006 Sean Finney\n");
604 printf (COPYRIGHT, copyright, email); 663 printf(COPYRIGHT, copyright, email);
605 664
606 printf ("%s\n", _("This plugin checks the clock offset with the ntp server")); 665 printf("%s\n", _("This plugin checks the clock offset with the ntp server"));
607 666
608 printf ("\n\n"); 667 printf("\n\n");
609 668
610 print_usage(); 669 print_usage();
611 printf (UT_HELP_VRSN); 670 printf(UT_HELP_VRSN);
612 printf (UT_EXTRA_OPTS); 671 printf(UT_EXTRA_OPTS);
613 printf (UT_IPv46); 672 printf(UT_IPv46);
614 printf (UT_HOST_PORT, 'p', "123"); 673 printf(UT_HOST_PORT, 'p', "123");
615 printf (" %s\n", "-q, --quiet"); 674 printf(" %s\n", "-q, --quiet");
616 printf (" %s\n", _("Returns UNKNOWN instead of CRITICAL if offset cannot be found")); 675 printf(" %s\n", _("Returns UNKNOWN instead of CRITICAL if offset cannot be found"));
617 printf (" %s\n", "-w, --warning=THRESHOLD"); 676 printf(" %s\n", "-w, --warning=THRESHOLD");
618 printf (" %s\n", _("Offset to result in warning status (seconds)")); 677 printf(" %s\n", _("Offset to result in warning status (seconds)"));
619 printf (" %s\n", "-c, --critical=THRESHOLD"); 678 printf(" %s\n", "-c, --critical=THRESHOLD");
620 printf (" %s\n", _("Offset to result in critical status (seconds)")); 679 printf(" %s\n", _("Offset to result in critical status (seconds)"));
621 printf (" %s\n", "-o, --time_offset=INTEGER"); 680 printf(" %s\n", "-o, --time_offset=INTEGER");
622 printf (" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); 681 printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)"));
623 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 682 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
624 printf (UT_VERBOSE); 683 printf(UT_VERBOSE);
625 684
626 printf("\n"); 685 printf("\n");
627 printf("%s\n", _("This plugin checks the clock offset between the local host and a")); 686 printf("%s\n", _("This plugin checks the clock offset between the local host and a"));
@@ -641,13 +700,11 @@ void print_help(void){
641 printf("%s\n", _("Examples:")); 700 printf("%s\n", _("Examples:"));
642 printf(" %s\n", ("./check_ntp_time -H ntpserv -w 0.5 -c 1")); 701 printf(" %s\n", ("./check_ntp_time -H ntpserv -w 0.5 -c 1"));
643 702
644 printf (UT_SUPPORT); 703 printf(UT_SUPPORT);
645} 704}
646 705
647void 706void print_usage(void) {
648print_usage(void) 707 printf("%s\n", _("Usage:"));
649{ 708 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n",
650 printf ("%s\n", _("Usage:")); 709 progname);
651 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n", progname);
652} 710}
653
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h
new file mode 100644
index 00000000..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 10c493b6..00000000
--- a/plugins/check_nwstat.c
+++ /dev/null
@@ -1,1740 +0,0 @@
1/*****************************************************************************
2*
3* Monitoring check_nwstat plugin
4*
5* License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team
7*
8* Description:
9*
10* This file contains the check_nwstat plugin
11*
12* This plugin attempts to contact the MRTGEXT NLM running on a
13* Novell server to gather the requested system information.
14*
15*
16* This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version.
20*
21* This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details.
25*
26* You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>.
28*
29*
30*****************************************************************************/
31
32const char *progname = "check_nwstat";
33const char *copyright = "2000-2007";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h"
37#include "netutils.h"
38#include "utils.h"
39
40enum checkvar {
41 NONE,
42 LOAD1, /* check 1 minute CPU load */
43 LOAD5, /* check 5 minute CPU load */
44 LOAD15, /* check 15 minute CPU load */
45 CONNS, /* check number of connections */
46 VPF, /* check % free space on volume */
47 VMF, /* check MB free space on volume */
48 VMU, /* check MB used space on volume */
49 VPU, /* check % used space on volume */
50 VMP, /* check MB purgeable space on volume */
51 VKF, /* check KB free space on volume */
52 LTCH, /* check long-term cache hit percentage */
53 CBUFF, /* check total cache buffers */
54 CDBUFF, /* check dirty cache buffers */
55 LRUM, /* check LRU sitting time in minutes */
56 DSDB, /* check to see if DS Database is open */
57 LOGINS, /* check to see if logins are enabled */
58 NRMH, /* check to see NRM Health Status */
59 PUPRB, /* check % of used packet receive buffers */
60 UPRB, /* check used packet receive buffers */
61 SAPENTRIES, /* check SAP entries */
62 OFILES, /* check number of open files */
63 VKP, /* check KB purgeable space on volume */
64 VPP, /* check % purgeable space on volume */
65 VKNP, /* check KB not yet purgeable space on volume */
66 VPNP, /* check % not yet purgeable space on volume */
67 ABENDS, /* check abended thread count */
68 CSPROCS, /* check number of current service processes */
69 TSYNC, /* check timesync status 0=no 1=yes in sync to the network */
70 LRUS, /* check LRU sitting time in seconds */
71 DCB, /* check dirty cache buffers as a percentage of the total */
72 TCB, /* check total cache buffers as a percentage of the original */
73 DSVER, /* check NDS version */
74 UPTIME, /* check server uptime */
75 NLM, /* check NLM loaded */
76 NRMP, /* check NRM Process Values */
77 NRMM, /* check NRM Memory Values */
78 NRMS, /* check NRM Values */
79 NSS1, /* check Statistics from _Admin:Manage_NSS\GeneralStats.xml */
80 NSS2, /* check Statistics from _Admin:Manage_NSS\BufferCache.xml */
81 NSS3, /* check statistics from _Admin:Manage_NSS\NameCache.xml */
82 NSS4, /* check statistics from _Admin:Manage_NSS\FileStats.xml */
83 NSS5, /* check statistics from _Admin:Manage_NSS\ObjectCache.xml */
84 NSS6, /* check statistics from _Admin:Manage_NSS\Thread.xml */
85 NSS7 /* check statistics from _Admin:Manage_NSS\AuthorizationCache.xml */
86};
87
88enum {
89 PORT = 9999
90};
91
92char *server_address=NULL;
93char *volume_name=NULL;
94char *nlm_name=NULL;
95char *nrmp_name=NULL;
96char *nrmm_name=NULL;
97char *nrms_name=NULL;
98char *nss1_name=NULL;
99char *nss2_name=NULL;
100char *nss3_name=NULL;
101char *nss4_name=NULL;
102char *nss5_name=NULL;
103char *nss6_name=NULL;
104char *nss7_name=NULL;
105int server_port=PORT;
106unsigned long warning_value=0L;
107unsigned long critical_value=0L;
108bool check_warning_value = false;
109bool check_critical_value = false;
110bool check_netware_version = false;
111enum checkvar vars_to_check = NONE;
112int sap_number=-1;
113
114int process_arguments(int, char **);
115void print_help(void);
116void print_usage(void);
117
118
119
120int
121main(int argc, char **argv) {
122 int result = STATE_UNKNOWN;
123 int sd;
124 char *send_buffer=NULL;
125 char recv_buffer[MAX_INPUT_BUFFER];
126 char *output_message=NULL;
127 char *temp_buffer=NULL;
128 char *netware_version=NULL;
129
130 int time_sync_status=0;
131 int nrm_health_status=0;
132 unsigned long total_cache_buffers=0;
133 unsigned long dirty_cache_buffers=0;
134 unsigned long open_files=0;
135 unsigned long abended_threads=0;
136 unsigned long max_service_processes=0;
137 unsigned long current_service_processes=0;
138 unsigned long free_disk_space=0L;
139 unsigned long nrmp_value=0L;
140 unsigned long nrmm_value=0L;
141 unsigned long nrms_value=0L;
142 unsigned long nss1_value=0L;
143 unsigned long nss2_value=0L;
144 unsigned long nss3_value=0L;
145 unsigned long nss4_value=0L;
146 unsigned long nss5_value=0L;
147 unsigned long nss6_value=0L;
148 unsigned long nss7_value=0L;
149 unsigned long total_disk_space=0L;
150 unsigned long used_disk_space=0L;
151 unsigned long percent_used_disk_space=0L;
152 unsigned long purgeable_disk_space=0L;
153 unsigned long non_purgeable_disk_space=0L;
154 unsigned long percent_free_space=0;
155 unsigned long percent_purgeable_space=0;
156 unsigned long percent_non_purgeable_space=0;
157 unsigned long current_connections=0L;
158 unsigned long utilization=0L;
159 unsigned long cache_hits=0;
160 unsigned long cache_buffers=0L;
161 unsigned long lru_time=0L;
162 unsigned long max_packet_receive_buffers=0;
163 unsigned long used_packet_receive_buffers=0;
164 unsigned long percent_used_packet_receive_buffers=0L;
165 unsigned long sap_entries=0;
166 char uptime[MAX_INPUT_BUFFER];
167
168 setlocale (LC_ALL, "");
169 bindtextdomain (PACKAGE, LOCALEDIR);
170 textdomain (PACKAGE);
171
172 /* Parse extra opts if any */
173 argv=np_extra_opts(&argc, argv, progname);
174
175 if (process_arguments(argc,argv) == ERROR)
176 usage4 (_("Could not parse arguments"));
177
178 /* initialize alarm signal handling */
179 signal(SIGALRM,socket_timeout_alarm_handler);
180
181 /* set socket timeout */
182 alarm(socket_timeout);
183
184 /* open connection */
185 my_tcp_connect (server_address, server_port, &sd);
186
187 /* get OS version string */
188 if (check_netware_version) {
189 send_buffer = strdup ("S19\r\n");
190 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
191 if (result!=STATE_OK)
192 return result;
193 if (!strcmp(recv_buffer,"-1\n"))
194 netware_version = strdup("");
195 else {
196 recv_buffer[strlen(recv_buffer)-1]=0;
197 xasprintf (&netware_version,_("NetWare %s: "),recv_buffer);
198 }
199 } else
200 netware_version = strdup("");
201
202
203 /* check CPU load */
204 if (vars_to_check==LOAD1 || vars_to_check==LOAD5 || vars_to_check==LOAD15) {
205
206 switch(vars_to_check) {
207 case LOAD1:
208 temp_buffer = strdup ("1");
209 break;
210 case LOAD5:
211 temp_buffer = strdup ("5");
212 break;
213 default:
214 temp_buffer = strdup ("15");
215 break;
216 }
217
218 close(sd);
219 my_tcp_connect (server_address, server_port, &sd);
220
221 xasprintf (&send_buffer,"UTIL%s\r\n",temp_buffer);
222 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
223 if (result!=STATE_OK)
224 return result;
225 utilization=strtoul(recv_buffer,NULL,10);
226
227 close(sd);
228 my_tcp_connect (server_address, server_port, &sd);
229
230 send_buffer = strdup ("UPTIME\r\n");
231 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
232 if (result!=STATE_OK)
233 return result;
234 recv_buffer[strlen(recv_buffer)-1]=0;
235 sprintf(uptime,_("Up %s,"),recv_buffer);
236
237 if (check_critical_value && utilization >= critical_value)
238 result=STATE_CRITICAL;
239 else if (check_warning_value && utilization >= warning_value)
240 result=STATE_WARNING;
241
242 xasprintf (&output_message,
243 _("Load %s - %s %s-min load average = %lu%%|load%s=%lu;%lu;%lu;0;100"),
244 state_text(result),
245 uptime,
246 temp_buffer,
247 utilization,
248 temp_buffer,
249 utilization,
250 warning_value,
251 critical_value);
252
253 /* check number of user connections */
254 } else if (vars_to_check==CONNS) {
255
256 close(sd);
257 my_tcp_connect (server_address, server_port, &sd);
258
259 send_buffer = strdup ("CONNECT\r\n");
260 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
261 if (result!=STATE_OK)
262 return result;
263 current_connections=strtoul(recv_buffer,NULL,10);
264
265 if (check_critical_value && current_connections >= critical_value)
266 result=STATE_CRITICAL;
267 else if (check_warning_value && current_connections >= warning_value)
268 result=STATE_WARNING;
269
270 xasprintf (&output_message,
271 _("Conns %s - %lu current connections|Conns=%lu;%lu;%lu;;"),
272 state_text(result),
273 current_connections,
274 current_connections,
275 warning_value,
276 critical_value);
277
278 /* check % long term cache hits */
279 } else if (vars_to_check==LTCH) {
280
281 close(sd);
282 my_tcp_connect (server_address, server_port, &sd);
283
284 send_buffer = strdup ("S1\r\n");
285 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
286 if (result!=STATE_OK)
287 return result;
288 cache_hits=atoi(recv_buffer);
289
290 if (check_critical_value && cache_hits <= critical_value)
291 result=STATE_CRITICAL;
292 else if (check_warning_value && cache_hits <= warning_value)
293 result=STATE_WARNING;
294
295 xasprintf (&output_message,
296 _("%s: Long term cache hits = %lu%%"),
297 state_text(result),
298 cache_hits);
299
300 /* check cache buffers */
301 } else if (vars_to_check==CBUFF) {
302
303 close(sd);
304 my_tcp_connect (server_address, server_port, &sd);
305
306 send_buffer = strdup ("S2\r\n");
307 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
308 if (result!=STATE_OK)
309 return result;
310 cache_buffers=strtoul(recv_buffer,NULL,10);
311
312 if (check_critical_value && cache_buffers <= critical_value)
313 result=STATE_CRITICAL;
314 else if (check_warning_value && cache_buffers <= warning_value)
315 result=STATE_WARNING;
316
317 xasprintf (&output_message,
318 _("%s: Total cache buffers = %lu|Cachebuffers=%lu;%lu;%lu;;"),
319 state_text(result),
320 cache_buffers,
321 cache_buffers,
322 warning_value,
323 critical_value);
324
325 /* check dirty cache buffers */
326 } else if (vars_to_check==CDBUFF) {
327
328 close(sd);
329 my_tcp_connect (server_address, server_port, &sd);
330
331 send_buffer = strdup ("S3\r\n");
332 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
333 if (result!=STATE_OK)
334 return result;
335 cache_buffers=strtoul(recv_buffer,NULL,10);
336
337 if (check_critical_value && cache_buffers >= critical_value)
338 result=STATE_CRITICAL;
339 else if (check_warning_value && cache_buffers >= warning_value)
340 result=STATE_WARNING;
341
342 xasprintf (&output_message,
343 _("%s: Dirty cache buffers = %lu|Dirty-Cache-Buffers=%lu;%lu;%lu;;"),
344 state_text(result),
345 cache_buffers,
346 cache_buffers,
347 warning_value,
348 critical_value);
349
350 /* check LRU sitting time in minutes */
351 } else if (vars_to_check==LRUM) {
352
353 close(sd);
354 my_tcp_connect (server_address, server_port, &sd);
355
356 send_buffer = strdup ("S5\r\n");
357 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
358 if (result!=STATE_OK)
359 return result;
360 lru_time=strtoul(recv_buffer,NULL,10);
361
362 if (check_critical_value && lru_time <= critical_value)
363 result=STATE_CRITICAL;
364 else if (check_warning_value && lru_time <= warning_value)
365 result=STATE_WARNING;
366
367 xasprintf (&output_message,
368 _("%s: LRU sitting time = %lu minutes"),
369 state_text(result),
370 lru_time);
371
372
373 /* check KB free space on volume */
374 } else if (vars_to_check==VKF) {
375
376 close(sd);
377 my_tcp_connect (server_address, server_port, &sd);
378
379 xasprintf (&send_buffer,"VKF%s\r\n",volume_name);
380 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
381 if (result!=STATE_OK)
382 return result;
383
384 if (!strcmp(recv_buffer,"-1\n")) {
385 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
386 result=STATE_CRITICAL;
387 } else {
388 free_disk_space=strtoul(recv_buffer,NULL,10);
389 if (check_critical_value && free_disk_space <= critical_value)
390 result=STATE_CRITICAL;
391 else if (check_warning_value && free_disk_space <= warning_value)
392 result=STATE_WARNING;
393 xasprintf (&output_message,
394 _("%s%lu KB free on volume %s|KBFree%s=%lu;%lu;%lu;;"),
395 (result==STATE_OK)?"":_("Only "),
396 free_disk_space,
397 volume_name,
398 volume_name,
399 free_disk_space,
400 warning_value,
401 critical_value);
402 }
403
404 /* check MB free space on volume */
405 } else if (vars_to_check==VMF) {
406
407 xasprintf (&send_buffer,"VMF%s\r\n",volume_name);
408 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
409 if (result!=STATE_OK)
410 return result;
411
412 if (!strcmp(recv_buffer,"-1\n")) {
413 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
414 result=STATE_CRITICAL;
415 } else {
416 free_disk_space=strtoul(recv_buffer,NULL,10);
417 if (check_critical_value && free_disk_space <= critical_value)
418 result=STATE_CRITICAL;
419 else if (check_warning_value && free_disk_space <= warning_value)
420 result=STATE_WARNING;
421 xasprintf (&output_message,
422 _("%s%lu MB free on volume %s|MBFree%s=%lu;%lu;%lu;;"),
423 (result==STATE_OK)?"":_("Only "),
424 free_disk_space,
425 volume_name,
426 volume_name,
427 free_disk_space,
428 warning_value,
429 critical_value);
430 }
431 /* check MB used space on volume */
432 } else if (vars_to_check==VMU) {
433
434 xasprintf (&send_buffer,"VMU%s\r\n",volume_name);
435 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
436 if (result!=STATE_OK)
437 return result;
438
439 if (!strcmp(recv_buffer,"-1\n")) {
440 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
441 result=STATE_CRITICAL;
442 } else {
443 free_disk_space=strtoul(recv_buffer,NULL,10);
444 if (check_critical_value && free_disk_space <= critical_value)
445 result=STATE_CRITICAL;
446 else if (check_warning_value && free_disk_space <= warning_value)
447 result=STATE_WARNING;
448 xasprintf (&output_message,
449 _("%s%lu MB used on volume %s|MBUsed%s=%lu;%lu;%lu;;"),
450 (result==STATE_OK)?"":_("Only "),
451 free_disk_space,
452 volume_name,
453 volume_name,
454 free_disk_space,
455 warning_value,
456 critical_value);
457 }
458 /* check % used space on volume */
459 } else if (vars_to_check==VPU) {
460 close(sd);
461 my_tcp_connect (server_address, server_port, &sd);
462
463 asprintf (&send_buffer,"VMU%s\r\n",volume_name);
464 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
465
466 if (result!=STATE_OK)
467 return result;
468
469 if (!strcmp(recv_buffer,"-1\n")) {
470 asprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
471 result=STATE_CRITICAL;
472
473 } else {
474 used_disk_space=strtoul(recv_buffer,NULL,10);
475 close(sd);
476 my_tcp_connect (server_address, server_port, &sd);
477 /* get total volume in MB */
478 asprintf (&send_buffer,"VMS%s\r\n",volume_name);
479 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
480 if (result!=STATE_OK)
481 return result;
482 total_disk_space=strtoul(recv_buffer,NULL,10);
483 /* calculate percent used on volume */
484 percent_used_disk_space=(unsigned long)(((double)used_disk_space/(double)total_disk_space)*100.0);
485
486 if (check_critical_value && percent_used_disk_space >= critical_value)
487 result=STATE_CRITICAL;
488 else if (check_warning_value && percent_used_disk_space >= warning_value)
489 result=STATE_WARNING;
490
491 asprintf (&output_message,_("%lu MB (%lu%%) used on volume %s - total %lu MB|Used space in percent on %s=%lu;%lu;%lu;0;100"),
492 used_disk_space,
493 percent_used_disk_space,
494 volume_name,
495 total_disk_space,
496 volume_name,
497 percent_used_disk_space,
498 warning_value,
499 critical_value
500 );
501 }
502
503 /* check % free space on volume */
504 } else if (vars_to_check==VPF) {
505
506 close(sd);
507 my_tcp_connect (server_address, server_port, &sd);
508
509 xasprintf (&send_buffer,"VKF%s\r\n",volume_name);
510 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
511 if (result!=STATE_OK)
512 return result;
513
514 if (!strcmp(recv_buffer,"-1\n")) {
515
516 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
517 result=STATE_CRITICAL;
518
519 } else {
520
521 free_disk_space=strtoul(recv_buffer,NULL,10);
522
523 close(sd);
524 my_tcp_connect (server_address, server_port, &sd);
525
526 xasprintf (&send_buffer,"VKS%s\r\n",volume_name);
527 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
528 if (result!=STATE_OK)
529 return result;
530 total_disk_space=strtoul(recv_buffer,NULL,10);
531
532 percent_free_space=(unsigned long)(((double)free_disk_space/(double)total_disk_space)*100.0);
533
534 if (check_critical_value && percent_free_space <= critical_value)
535 result=STATE_CRITICAL;
536 else if (check_warning_value && percent_free_space <= warning_value)
537 result=STATE_WARNING;
538 free_disk_space/=1024;
539 total_disk_space/=1024;
540 xasprintf (&output_message,_("%lu MB (%lu%%) free on volume %s - total %lu MB|FreeMB%s=%lu;%lu;%lu;0;100"),
541 free_disk_space,
542 percent_free_space,
543 volume_name,
544 total_disk_space,
545 volume_name,
546 percent_free_space,
547 warning_value,
548 critical_value
549 );
550 }
551
552 /* check to see if DS Database is open or closed */
553 } else if (vars_to_check==DSDB) {
554
555 close(sd);
556 my_tcp_connect (server_address, server_port, &sd);
557
558 send_buffer = strdup ("S11\r\n");
559 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
560 if (result!=STATE_OK)
561 return result;
562 if (atoi(recv_buffer)==1)
563 result=STATE_OK;
564 else
565 result=STATE_WARNING;
566
567 close(sd);
568 my_tcp_connect (server_address, server_port, &sd);
569
570 send_buffer = strdup ("S13\r\n");
571 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
572 temp_buffer=strtok(recv_buffer,"\r\n");
573
574 xasprintf (&output_message,_("Directory Services Database is %s (DS version %s)"),(result==STATE_OK)?"open":"closed",temp_buffer);
575
576 /* check to see if logins are enabled */
577 } else if (vars_to_check==LOGINS) {
578
579 close(sd);
580 my_tcp_connect (server_address, server_port, &sd);
581
582 send_buffer = strdup ("S12\r\n");
583 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
584 if (result!=STATE_OK)
585 return result;
586 if (atoi(recv_buffer)==1)
587 result=STATE_OK;
588 else
589 result=STATE_WARNING;
590
591 xasprintf (&output_message,_("Logins are %s"),(result==STATE_OK)?_("enabled"):_("disabled"));
592
593
594 /* check NRM Health Status Summary*/
595 } else if (vars_to_check==NRMH) {
596
597 xasprintf (&send_buffer,"NRMH\r\n");
598 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
599 if (result!=STATE_OK)
600 return result;
601
602 nrm_health_status=atoi(recv_buffer);
603
604 if (nrm_health_status==2) {
605 result=STATE_OK;
606 xasprintf (&output_message,_("CRITICAL - NRM Status is bad!"));
607 }
608 else {
609 if (nrm_health_status==1) {
610 result=STATE_WARNING;
611 xasprintf (&output_message,_("Warning - NRM Status is suspect!"));
612 }
613
614 xasprintf (&output_message,_("OK - NRM Status is good!"));
615 }
616
617
618
619 /* check packet receive buffers */
620 } else if (vars_to_check==UPRB || vars_to_check==PUPRB) {
621
622 close(sd);
623 my_tcp_connect (server_address, server_port, &sd);
624
625 xasprintf (&send_buffer,"S15\r\n");
626 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
627 if (result!=STATE_OK)
628 return result;
629
630 used_packet_receive_buffers=atoi(recv_buffer);
631
632 close(sd);
633 my_tcp_connect (server_address, server_port, &sd);
634
635 xasprintf (&send_buffer,"S16\r\n");
636 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
637 if (result!=STATE_OK)
638 return result;
639
640 max_packet_receive_buffers=atoi(recv_buffer);
641
642 percent_used_packet_receive_buffers=(unsigned long)(((double)used_packet_receive_buffers/(double)max_packet_receive_buffers)*100.0);
643
644 if (vars_to_check==UPRB) {
645 if (check_critical_value && used_packet_receive_buffers >= critical_value)
646 result=STATE_CRITICAL;
647 else if (check_warning_value && used_packet_receive_buffers >= warning_value)
648 result=STATE_WARNING;
649 } else {
650 if (check_critical_value && percent_used_packet_receive_buffers >= critical_value)
651 result=STATE_CRITICAL;
652 else if (check_warning_value && percent_used_packet_receive_buffers >= warning_value)
653 result=STATE_WARNING;
654 }
655
656 xasprintf (&output_message,_("%lu of %lu (%lu%%) packet receive buffers used"),used_packet_receive_buffers,max_packet_receive_buffers,percent_used_packet_receive_buffers);
657
658 /* check SAP table entries */
659 } else if (vars_to_check==SAPENTRIES) {
660
661 close(sd);
662 my_tcp_connect (server_address, server_port, &sd);
663
664 if (sap_number==-1)
665 xasprintf (&send_buffer,"S9\r\n");
666 else
667 xasprintf (&send_buffer,"S9.%d\r\n",sap_number);
668 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
669 if (result!=STATE_OK)
670 return result;
671
672 sap_entries=atoi(recv_buffer);
673
674 if (check_critical_value && sap_entries >= critical_value)
675 result=STATE_CRITICAL;
676 else if (check_warning_value && sap_entries >= warning_value)
677 result=STATE_WARNING;
678
679 if (sap_number==-1)
680 xasprintf (&output_message,_("%lu entries in SAP table"),sap_entries);
681 else
682 xasprintf (&output_message,_("%lu entries in SAP table for SAP type %d"),sap_entries,sap_number);
683
684 /* check KB purgeable space on volume */
685 } else if (vars_to_check==VKP) {
686
687 close(sd);
688 my_tcp_connect (server_address, server_port, &sd);
689
690 xasprintf (&send_buffer,"VKP%s\r\n",volume_name);
691 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
692 if (result!=STATE_OK)
693 return result;
694
695 if (!strcmp(recv_buffer,"-1\n")) {
696 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
697 result=STATE_CRITICAL;
698 } else {
699 purgeable_disk_space=strtoul(recv_buffer,NULL,10);
700 if (check_critical_value && purgeable_disk_space >= critical_value)
701 result=STATE_CRITICAL;
702 else if (check_warning_value && purgeable_disk_space >= warning_value)
703 result=STATE_WARNING;
704 xasprintf (&output_message,_("%s%lu KB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"),
705 (result==STATE_OK)?"":_("Only "),
706 purgeable_disk_space,
707 volume_name,
708 volume_name,
709 purgeable_disk_space,
710 warning_value,
711 critical_value);
712 }
713 /* check MB purgeable space on volume */
714 } else if (vars_to_check==VMP) {
715
716 xasprintf (&send_buffer,"VMP%s\r\n",volume_name);
717 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
718 if (result!=STATE_OK)
719 return result;
720
721 if (!strcmp(recv_buffer,"-1\n")) {
722 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
723 result=STATE_CRITICAL;
724 } else {
725 purgeable_disk_space=strtoul(recv_buffer,NULL,10);
726 if (check_critical_value && purgeable_disk_space >= critical_value)
727 result=STATE_CRITICAL;
728 else if (check_warning_value && purgeable_disk_space >= warning_value)
729 result=STATE_WARNING;
730 xasprintf (&output_message,_("%s%lu MB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"),
731 (result==STATE_OK)?"":_("Only "),
732 purgeable_disk_space,
733 volume_name,
734 volume_name,
735 purgeable_disk_space,
736 warning_value,
737 critical_value);
738 }
739
740 /* check % purgeable space on volume */
741 } else if (vars_to_check==VPP) {
742
743 close(sd);
744 my_tcp_connect (server_address, server_port, &sd);
745
746 xasprintf (&send_buffer,"VKP%s\r\n",volume_name);
747 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
748 if (result!=STATE_OK)
749 return result;
750
751 if (!strcmp(recv_buffer,"-1\n")) {
752
753 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
754 result=STATE_CRITICAL;
755
756 } else {
757
758 purgeable_disk_space=strtoul(recv_buffer,NULL,10);
759
760 close(sd);
761 my_tcp_connect (server_address, server_port, &sd);
762
763 xasprintf (&send_buffer,"VKS%s\r\n",volume_name);
764 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
765 if (result!=STATE_OK)
766 return result;
767 total_disk_space=strtoul(recv_buffer,NULL,10);
768
769 percent_purgeable_space=(unsigned long)(((double)purgeable_disk_space/(double)total_disk_space)*100.0);
770
771 if (check_critical_value && percent_purgeable_space >= critical_value)
772 result=STATE_CRITICAL;
773 else if (check_warning_value && percent_purgeable_space >= warning_value)
774 result=STATE_WARNING;
775 purgeable_disk_space/=1024;
776 xasprintf (&output_message,_("%lu MB (%lu%%) purgeable on volume %s|Purgeable%s=%lu;%lu;%lu;0;100"),
777 purgeable_disk_space,
778 percent_purgeable_space,
779 volume_name,
780 volume_name,
781 percent_purgeable_space,
782 warning_value,
783 critical_value
784 );
785 }
786
787 /* check KB not yet purgeable space on volume */
788 } else if (vars_to_check==VKNP) {
789
790 close(sd);
791 my_tcp_connect (server_address, server_port, &sd);
792
793 xasprintf (&send_buffer,"VKNP%s\r\n",volume_name);
794 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
795 if (result!=STATE_OK)
796 return result;
797
798 if (!strcmp(recv_buffer,"-1\n")) {
799 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
800 result=STATE_CRITICAL;
801 } else {
802 non_purgeable_disk_space=strtoul(recv_buffer,NULL,10);
803 if (check_critical_value && non_purgeable_disk_space >= critical_value)
804 result=STATE_CRITICAL;
805 else if (check_warning_value && non_purgeable_disk_space >= warning_value)
806 result=STATE_WARNING;
807 xasprintf (&output_message,_("%s%lu KB not yet purgeable on volume %s"),(result==STATE_OK)?"":_("Only "),non_purgeable_disk_space,volume_name);
808 }
809
810 /* check % not yet purgeable space on volume */
811 } else if (vars_to_check==VPNP) {
812
813 close(sd);
814 my_tcp_connect (server_address, server_port, &sd);
815
816 xasprintf (&send_buffer,"VKNP%s\r\n",volume_name);
817 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
818 if (result!=STATE_OK)
819 return result;
820
821 if (!strcmp(recv_buffer,"-1\n")) {
822
823 xasprintf (&output_message,_("CRITICAL - Volume '%s' does not exist!"),volume_name);
824 result=STATE_CRITICAL;
825
826 } else {
827
828 non_purgeable_disk_space=strtoul(recv_buffer,NULL,10);
829
830 close(sd);
831 my_tcp_connect (server_address, server_port, &sd);
832
833 xasprintf (&send_buffer,"VKS%s\r\n",volume_name);
834 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
835 if (result!=STATE_OK)
836 return result;
837 total_disk_space=strtoul(recv_buffer,NULL,10);
838
839 percent_non_purgeable_space=(unsigned long)(((double)non_purgeable_disk_space/(double)total_disk_space)*100.0);
840
841 if (check_critical_value && percent_non_purgeable_space >= critical_value)
842 result=STATE_CRITICAL;
843 else if (check_warning_value && percent_non_purgeable_space >= warning_value)
844 result=STATE_WARNING;
845 purgeable_disk_space/=1024;
846 xasprintf (&output_message,_("%lu MB (%lu%%) not yet purgeable on volume %s"),non_purgeable_disk_space,percent_non_purgeable_space,volume_name);
847 }
848
849 /* check # of open files */
850 } else if (vars_to_check==OFILES) {
851
852 close(sd);
853 my_tcp_connect (server_address, server_port, &sd);
854
855 xasprintf (&send_buffer,"S18\r\n");
856 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
857 if (result!=STATE_OK)
858 return result;
859
860 open_files=atoi(recv_buffer);
861
862 if (check_critical_value && open_files >= critical_value)
863 result=STATE_CRITICAL;
864 else if (check_warning_value && open_files >= warning_value)
865 result=STATE_WARNING;
866
867 xasprintf (&output_message,_("%lu open files|Openfiles=%lu;%lu;%lu;0,0"),
868 open_files,
869 open_files,
870 warning_value,
871 critical_value);
872
873
874 /* check # of abended threads (Netware > 5.x only) */
875 } else if (vars_to_check==ABENDS) {
876
877 close(sd);
878 my_tcp_connect (server_address, server_port, &sd);
879
880 xasprintf (&send_buffer,"S17\r\n");
881 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
882 if (result!=STATE_OK)
883 return result;
884
885 abended_threads=atoi(recv_buffer);
886
887 if (check_critical_value && abended_threads >= critical_value)
888 result=STATE_CRITICAL;
889 else if (check_warning_value && abended_threads >= warning_value)
890 result=STATE_WARNING;
891
892 xasprintf (&output_message,_("%lu abended threads|Abends=%lu;%lu;%lu;;"),
893 abended_threads,
894 abended_threads,
895 warning_value,
896 critical_value);
897
898 /* check # of current service processes (Netware 5.x only) */
899 } else if (vars_to_check==CSPROCS) {
900
901 close(sd);
902 my_tcp_connect (server_address, server_port, &sd);
903
904 xasprintf (&send_buffer,"S20\r\n");
905 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
906 if (result!=STATE_OK)
907 return result;
908
909 max_service_processes=atoi(recv_buffer);
910
911 close(sd);
912 my_tcp_connect (server_address, server_port, &sd);
913
914 xasprintf (&send_buffer,"S21\r\n");
915 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
916 if (result!=STATE_OK)
917 return result;
918
919 current_service_processes=atoi(recv_buffer);
920
921 if (check_critical_value && current_service_processes >= critical_value)
922 result=STATE_CRITICAL;
923 else if (check_warning_value && current_service_processes >= warning_value)
924 result=STATE_WARNING;
925
926 xasprintf (&output_message,
927 _("%lu current service processes (%lu max)|Processes=%lu;%lu;%lu;0;%lu"),
928 current_service_processes,
929 max_service_processes,
930 current_service_processes,
931 warning_value,
932 critical_value,
933 max_service_processes);
934
935 /* check # Timesync Status */
936 } else if (vars_to_check==TSYNC) {
937
938 close(sd);
939 my_tcp_connect (server_address, server_port, &sd);
940
941 xasprintf (&send_buffer,"S22\r\n");
942 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
943 if (result!=STATE_OK)
944 return result;
945
946 time_sync_status=atoi(recv_buffer);
947
948 if (time_sync_status==0) {
949 result=STATE_CRITICAL;
950 xasprintf (&output_message,_("CRITICAL - Time not in sync with network!"));
951 }
952 else {
953 xasprintf (&output_message,_("OK - Time in sync with network!"));
954 }
955
956
957
958
959
960 /* check LRU sitting time in secondss */
961 } else if (vars_to_check==LRUS) {
962
963 close(sd);
964 my_tcp_connect (server_address, server_port, &sd);
965
966 send_buffer = strdup ("S4\r\n");
967 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
968 if (result!=STATE_OK)
969 return result;
970 lru_time=strtoul(recv_buffer,NULL,10);
971
972 if (check_critical_value && lru_time <= critical_value)
973 result=STATE_CRITICAL;
974 else if (check_warning_value && lru_time <= warning_value)
975 result=STATE_WARNING;
976 xasprintf (&output_message,_("LRU sitting time = %lu seconds"),lru_time);
977
978
979 /* check % dirty cacheobuffers as a percentage of the total*/
980 } else if (vars_to_check==DCB) {
981
982 close(sd);
983 my_tcp_connect (server_address, server_port, &sd);
984
985 send_buffer = strdup ("S6\r\n");
986 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
987 if (result!=STATE_OK)
988 return result;
989 dirty_cache_buffers=atoi(recv_buffer);
990
991 if (check_critical_value && dirty_cache_buffers <= critical_value)
992 result=STATE_CRITICAL;
993 else if (check_warning_value && dirty_cache_buffers <= warning_value)
994 result=STATE_WARNING;
995 xasprintf (&output_message,_("Dirty cache buffers = %lu%% of the total|DCB=%lu;%lu;%lu;0;100"),
996 dirty_cache_buffers,
997 dirty_cache_buffers,
998 warning_value,
999 critical_value);
1000
1001 /* check % total cache buffers as a percentage of the original*/
1002 } else if (vars_to_check==TCB) {
1003
1004 close(sd);
1005 my_tcp_connect (server_address, server_port, &sd);
1006
1007 send_buffer = strdup ("S7\r\n");
1008 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1009 if (result!=STATE_OK)
1010 return result;
1011 total_cache_buffers=atoi(recv_buffer);
1012
1013 if (check_critical_value && total_cache_buffers <= critical_value)
1014 result=STATE_CRITICAL;
1015 else if (check_warning_value && total_cache_buffers <= warning_value)
1016 result=STATE_WARNING;
1017 xasprintf (&output_message,_("Total cache buffers = %lu%% of the original|TCB=%lu;%lu;%lu;0;100"),
1018 total_cache_buffers,
1019 total_cache_buffers,
1020 warning_value,
1021 critical_value);
1022
1023 } else if (vars_to_check==DSVER) {
1024
1025 close(sd);
1026 my_tcp_connect (server_address, server_port, &sd);
1027
1028 xasprintf (&send_buffer,"S13\r\n");
1029 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1030 if (result!=STATE_OK)
1031 return result;
1032
1033 recv_buffer[strlen(recv_buffer)-1]=0;
1034
1035 xasprintf (&output_message,_("NDS Version %s"),recv_buffer);
1036
1037 } else if (vars_to_check==UPTIME) {
1038
1039 close(sd);
1040 my_tcp_connect (server_address, server_port, &sd);
1041
1042 xasprintf (&send_buffer,"UPTIME\r\n");
1043 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1044 if (result!=STATE_OK)
1045 return result;
1046
1047
1048 recv_buffer[sizeof(recv_buffer)-1]=0;
1049 recv_buffer[strlen(recv_buffer)-1]=0;
1050
1051 xasprintf (&output_message,_("Up %s"),recv_buffer);
1052
1053 } else if (vars_to_check==NLM) {
1054
1055 close(sd);
1056 my_tcp_connect (server_address, server_port, &sd);
1057
1058 xasprintf (&send_buffer,"S24:%s\r\n",nlm_name);
1059 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1060 if (result!=STATE_OK)
1061 return result;
1062
1063 recv_buffer[strlen(recv_buffer)-1]=0;
1064 if (strcmp(recv_buffer,"-1")) {
1065 xasprintf (&output_message,_("Module %s version %s is loaded"),nlm_name,recv_buffer);
1066 } else {
1067 result=STATE_CRITICAL;
1068 xasprintf (&output_message,_("Module %s is not loaded"),nlm_name);
1069
1070 }
1071 } else if (vars_to_check==NRMP) {
1072
1073 xasprintf (&send_buffer,"NRMP:%s\r\n",nrmp_name);
1074 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1075 if (result!=STATE_OK)
1076 return result;
1077
1078 if (!strcmp(recv_buffer,"-1\n")) {
1079 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nrmp_name);
1080 result=STATE_CRITICAL;
1081 } else {
1082 nrmp_value=strtoul(recv_buffer,NULL,10);
1083 if (check_critical_value && nrmp_value <= critical_value)
1084 result=STATE_CRITICAL;
1085 else if (check_warning_value && nrmp_value <= warning_value)
1086 result=STATE_WARNING;
1087 xasprintf (&output_message,
1088 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1089 nrmp_name,
1090 nrmp_value,
1091 nrmp_name,
1092 nrmp_value,
1093 warning_value,
1094 critical_value);
1095 }
1096
1097 } else if (vars_to_check==NRMM) {
1098
1099 xasprintf (&send_buffer,"NRMM:%s\r\n",nrmm_name);
1100 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1101 if (result!=STATE_OK)
1102 return result;
1103
1104 if (!strcmp(recv_buffer,"-1\n")) {
1105 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nrmm_name);
1106 result=STATE_CRITICAL;
1107 } else {
1108 nrmm_value=strtoul(recv_buffer,NULL,10);
1109 if (check_critical_value && nrmm_value <= critical_value)
1110 result=STATE_CRITICAL;
1111 else if (check_warning_value && nrmm_value <= warning_value)
1112 result=STATE_WARNING;
1113 xasprintf (&output_message,
1114 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1115 nrmm_name,
1116 nrmm_value,
1117 nrmm_name,
1118 nrmm_value,
1119 warning_value,
1120 critical_value);
1121 }
1122
1123 } else if (vars_to_check==NRMS) {
1124
1125 xasprintf (&send_buffer,"NRMS:%s\r\n",nrms_name);
1126 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1127 if (result!=STATE_OK)
1128 return result;
1129
1130 if (!strcmp(recv_buffer,"-1\n")) {
1131 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nrms_name);
1132 result=STATE_CRITICAL;
1133 } else {
1134 nrms_value=strtoul(recv_buffer,NULL,10);
1135 if (check_critical_value && nrms_value >= critical_value)
1136 result=STATE_CRITICAL;
1137 else if (check_warning_value && nrms_value >= warning_value)
1138 result=STATE_WARNING;
1139 xasprintf (&output_message,
1140 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1141 nrms_name,
1142 nrms_value,
1143 nrms_name,
1144 nrms_value,
1145 warning_value,
1146 critical_value);
1147 }
1148
1149 } else if (vars_to_check==NSS1) {
1150
1151 xasprintf (&send_buffer,"NSS1:%s\r\n",nss1_name);
1152 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1153 if (result!=STATE_OK)
1154 return result;
1155
1156 if (!strcmp(recv_buffer,"-1\n")) {
1157 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss1_name);
1158 result=STATE_CRITICAL;
1159 } else {
1160 nss1_value=strtoul(recv_buffer,NULL,10);
1161 if (check_critical_value && nss1_value >= critical_value)
1162 result=STATE_CRITICAL;
1163 else if (check_warning_value && nss1_value >= warning_value)
1164 result=STATE_WARNING;
1165 xasprintf (&output_message,
1166 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1167 nss1_name,
1168 nss1_value,
1169 nss1_name,
1170 nss1_value,
1171 warning_value,
1172 critical_value);
1173 }
1174
1175 } else if (vars_to_check==NSS2) {
1176
1177 xasprintf (&send_buffer,"NSS2:%s\r\n",nss2_name);
1178 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1179 if (result!=STATE_OK)
1180 return result;
1181
1182 if (!strcmp(recv_buffer,"-1\n")) {
1183 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss2_name);
1184 result=STATE_CRITICAL;
1185 } else {
1186 nss2_value=strtoul(recv_buffer,NULL,10);
1187 if (check_critical_value && nss2_value >= critical_value)
1188 result=STATE_CRITICAL;
1189 else if (check_warning_value && nss2_value >= warning_value)
1190 result=STATE_WARNING;
1191 xasprintf (&output_message,
1192 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1193 nss2_name,
1194 nss2_value,
1195 nss2_name,
1196 nss2_value,
1197 warning_value,
1198 critical_value);
1199 }
1200
1201 } else if (vars_to_check==NSS3) {
1202
1203 xasprintf (&send_buffer,"NSS3:%s\r\n",nss3_name);
1204 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1205 if (result!=STATE_OK)
1206 return result;
1207
1208 if (!strcmp(recv_buffer,"-1\n")) {
1209 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss3_name);
1210 result=STATE_CRITICAL;
1211 } else {
1212 nss3_value=strtoul(recv_buffer,NULL,10);
1213 if (check_critical_value && nss3_value >= critical_value)
1214 result=STATE_CRITICAL;
1215 else if (check_warning_value && nss3_value >= warning_value)
1216 result=STATE_WARNING;
1217 xasprintf (&output_message,
1218 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1219 nss3_name,
1220 nss3_value,
1221 nss3_name,
1222 nss3_value,
1223 warning_value,
1224 critical_value);
1225 }
1226
1227 } else if (vars_to_check==NSS4) {
1228
1229 xasprintf (&send_buffer,"NSS4:%s\r\n",nss4_name);
1230 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1231 if (result!=STATE_OK)
1232 return result;
1233
1234 if (!strcmp(recv_buffer,"-1\n")) {
1235 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss4_name);
1236 result=STATE_CRITICAL;
1237 } else {
1238 nss4_value=strtoul(recv_buffer,NULL,10);
1239 if (check_critical_value && nss4_value >= critical_value)
1240 result=STATE_CRITICAL;
1241 else if (check_warning_value && nss4_value >= warning_value)
1242 result=STATE_WARNING;
1243 xasprintf (&output_message,
1244 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1245 nss4_name,
1246 nss4_value,
1247 nss4_name,
1248 nss4_value,
1249 warning_value,
1250 critical_value);
1251 }
1252
1253 } else if (vars_to_check==NSS5) {
1254
1255 xasprintf (&send_buffer,"NSS5:%s\r\n",nss5_name);
1256 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1257 if (result!=STATE_OK)
1258 return result;
1259
1260 if (!strcmp(recv_buffer,"-1\n")) {
1261 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss5_name);
1262 result=STATE_CRITICAL;
1263 } else {
1264 nss5_value=strtoul(recv_buffer,NULL,10);
1265 if (check_critical_value && nss5_value >= critical_value)
1266 result=STATE_CRITICAL;
1267 else if (check_warning_value && nss5_value >= warning_value)
1268 result=STATE_WARNING;
1269 xasprintf (&output_message,
1270 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1271 nss5_name,
1272 nss5_value,
1273 nss5_name,
1274 nss5_value,
1275 warning_value,
1276 critical_value);
1277 }
1278
1279 } else if (vars_to_check==NSS6) {
1280
1281 xasprintf (&send_buffer,"NSS6:%s\r\n",nss6_name);
1282 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1283 if (result!=STATE_OK)
1284 return result;
1285
1286 if (!strcmp(recv_buffer,"-1\n")) {
1287 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss6_name);
1288 result=STATE_CRITICAL;
1289 } else {
1290 nss6_value=strtoul(recv_buffer,NULL,10);
1291 if (check_critical_value && nss6_value >= critical_value)
1292 result=STATE_CRITICAL;
1293 else if (check_warning_value && nss6_value >= warning_value)
1294 result=STATE_WARNING;
1295 xasprintf (&output_message,
1296 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1297 nss6_name,
1298 nss6_value,
1299 nss6_name,
1300 nss6_value,
1301 warning_value,
1302 critical_value);
1303 }
1304
1305 } else if (vars_to_check==NSS7) {
1306
1307 xasprintf (&send_buffer,"NSS7:%s\r\n",nss7_name);
1308 result=send_tcp_request(sd,send_buffer,recv_buffer,sizeof(recv_buffer));
1309 if (result!=STATE_OK)
1310 return result;
1311
1312 if (!strcmp(recv_buffer,"-1\n")) {
1313 xasprintf (&output_message,_("CRITICAL - Value '%s' does not exist!"),nss7_name);
1314 result=STATE_CRITICAL;
1315 } else {
1316 nss7_value=strtoul(recv_buffer,NULL,10);
1317 if (check_critical_value && nss7_value >= critical_value)
1318 result=STATE_CRITICAL;
1319 else if (check_warning_value && nss7_value >= warning_value)
1320 result=STATE_WARNING;
1321 xasprintf (&output_message,
1322 _("%s is %lu|%s=%lu;%lu;%lu;;"),
1323 nss7_name,
1324 nss7_value,
1325 nss7_name,
1326 nss7_value,
1327 warning_value,
1328 critical_value);
1329 }
1330
1331
1332}
1333 else {
1334
1335 output_message = strdup (_("Nothing to check!\n"));
1336 result=STATE_UNKNOWN;
1337
1338 }
1339
1340 close (sd);
1341
1342 /* reset timeout */
1343 alarm(0);
1344
1345 printf("%s%s\n",netware_version,output_message);
1346
1347 return result;
1348}
1349
1350
1351
1352/* process command-line arguments */
1353int process_arguments(int argc, char **argv) {
1354 int c;
1355
1356 int option = 0;
1357 static struct option longopts[] =
1358 {
1359 {"port", required_argument,0,'p'},
1360 {"timeout", required_argument,0,'t'},
1361 {"critical", required_argument,0,'c'},
1362 {"warning", required_argument,0,'w'},
1363 {"variable", required_argument,0,'v'},
1364 {"hostname", required_argument,0,'H'},
1365 {"osversion",no_argument, 0,'o'},
1366 {"version", no_argument, 0,'V'},
1367 {"help", no_argument, 0,'h'},
1368 {0,0,0,0}
1369 };
1370
1371 /* no options were supplied */
1372 if (argc<2) return ERROR;
1373
1374 /* backwards compatibility */
1375 if (! is_option(argv[1])) {
1376 server_address=argv[1];
1377 argv[1]=argv[0];
1378 argv=&argv[1];
1379 argc--;
1380 }
1381
1382 for (c=1;c<argc;c++) {
1383 if (strcmp("-to",argv[c])==0)
1384 strcpy(argv[c],"-t");
1385 else if (strcmp("-wv",argv[c])==0)
1386 strcpy(argv[c],"-w");
1387 else if (strcmp("-cv",argv[c])==0)
1388 strcpy(argv[c],"-c");
1389 }
1390
1391 while (1) {
1392 c = getopt_long(argc,argv,"+hoVH:t:c:w:p:v:",longopts,&option);
1393
1394 if (c==-1||c==EOF||c==1)
1395 break;
1396
1397 switch (c)
1398 {
1399 case '?': /* print short usage statement if args not parsable */
1400 usage5 ();
1401 case 'h': /* help */
1402 print_help();
1403 exit(STATE_UNKNOWN);
1404 case 'V': /* version */
1405 print_revision(progname, NP_VERSION);
1406 exit(STATE_UNKNOWN);
1407 case 'H': /* hostname */
1408 server_address=optarg;
1409 break;
1410 case 'o': /* display nos version */
1411 check_netware_version = true;
1412 break;
1413 case 'p': /* port */
1414 if (is_intnonneg(optarg))
1415 server_port=atoi(optarg);
1416 else
1417 die(STATE_UNKNOWN,_("Server port an integer\n"));
1418 break;
1419 case 'v':
1420 if (strlen(optarg)<3)
1421 return ERROR;
1422 if (!strcmp(optarg,"LOAD1"))
1423 vars_to_check=LOAD1;
1424 else if (!strcmp(optarg,"LOAD5"))
1425 vars_to_check=LOAD5;
1426 else if (!strcmp(optarg,"LOAD15"))
1427 vars_to_check=LOAD15;
1428 else if (!strcmp(optarg,"CONNS"))
1429 vars_to_check=CONNS;
1430 else if (!strcmp(optarg,"LTCH"))
1431 vars_to_check=LTCH;
1432 else if (!strcmp(optarg,"DCB"))
1433 vars_to_check=DCB;
1434 else if (!strcmp(optarg,"TCB"))
1435 vars_to_check=TCB;
1436 else if (!strcmp(optarg,"CBUFF"))
1437 vars_to_check=CBUFF;
1438 else if (!strcmp(optarg,"CDBUFF"))
1439 vars_to_check=CDBUFF;
1440 else if (!strcmp(optarg,"LRUM"))
1441 vars_to_check=LRUM;
1442 else if (!strcmp(optarg,"LRUS"))
1443 vars_to_check=LRUS;
1444 else if (strncmp(optarg,"VPF",3)==0) {
1445 vars_to_check=VPF;
1446 volume_name = strdup (optarg+3);
1447 if (!strcmp(volume_name,""))
1448 volume_name = strdup ("SYS");
1449 }
1450 else if (strncmp(optarg,"VKF",3)==0) {
1451 vars_to_check=VKF;
1452 volume_name = strdup (optarg+3);
1453 if (!strcmp(volume_name,""))
1454 volume_name = strdup ("SYS");
1455 }
1456 else if (strncmp(optarg,"VMF",3)==0) {
1457 vars_to_check=VMF;
1458 volume_name = strdup (optarg+3);
1459 if (!strcmp(volume_name,""))
1460 volume_name = strdup ("SYS");
1461 }
1462 else if (!strcmp(optarg,"DSDB"))
1463 vars_to_check=DSDB;
1464 else if (!strcmp(optarg,"LOGINS"))
1465 vars_to_check=LOGINS;
1466 else if (!strcmp(optarg,"NRMH"))
1467 vars_to_check=NRMH;
1468 else if (!strcmp(optarg,"UPRB"))
1469 vars_to_check=UPRB;
1470 else if (!strcmp(optarg,"PUPRB"))
1471 vars_to_check=PUPRB;
1472 else if (!strncmp(optarg,"SAPENTRIES",10)) {
1473 vars_to_check=SAPENTRIES;
1474 if (strlen(optarg)>10)
1475 sap_number=atoi(optarg+10);
1476 else
1477 sap_number=-1;
1478 }
1479 else if (!strcmp(optarg,"OFILES"))
1480 vars_to_check=OFILES;
1481 else if (strncmp(optarg,"VKP",3)==0) {
1482 vars_to_check=VKP;
1483 volume_name = strdup (optarg+3);
1484 if (!strcmp(volume_name,""))
1485 volume_name = strdup ("SYS");
1486 }
1487 else if (strncmp(optarg,"VMP",3)==0) {
1488 vars_to_check=VMP;
1489 volume_name = strdup (optarg+3);
1490 if (!strcmp(volume_name,""))
1491 volume_name = strdup ("SYS");
1492 }
1493 else if (strncmp(optarg,"VMU",3)==0) {
1494 vars_to_check=VMU;
1495 volume_name = strdup (optarg+3);
1496 if (!strcmp(volume_name,""))
1497 volume_name = strdup ("SYS");
1498 }
1499 else if (strncmp(optarg,"VPU",3)==0) {
1500 vars_to_check=VPU;
1501 volume_name = strdup (optarg+3);
1502 if (!strcmp(volume_name,""))
1503 volume_name = strdup ("SYS");
1504 }
1505 else if (strncmp(optarg,"VPP",3)==0) {
1506 vars_to_check=VPP;
1507 volume_name = strdup (optarg+3);
1508 if (!strcmp(volume_name,""))
1509 volume_name = strdup ("SYS");
1510 }
1511 else if (strncmp(optarg,"VKNP",4)==0) {
1512 vars_to_check=VKNP;
1513 volume_name = strdup (optarg+4);
1514 if (!strcmp(volume_name,""))
1515 volume_name = strdup ("SYS");
1516 }
1517 else if (strncmp(optarg,"VPNP",4)==0) {
1518 vars_to_check=VPNP;
1519 volume_name = strdup (optarg+4);
1520 if (!strcmp(volume_name,""))
1521 volume_name = strdup("SYS");
1522 }
1523 else if (!strcmp(optarg,"ABENDS"))
1524 vars_to_check=ABENDS;
1525 else if (!strcmp(optarg,"CSPROCS"))
1526 vars_to_check=CSPROCS;
1527 else if (!strcmp(optarg,"TSYNC"))
1528 vars_to_check=TSYNC;
1529 else if (!strcmp(optarg,"DSVER"))
1530 vars_to_check=DSVER;
1531 else if (!strcmp(optarg,"UPTIME")) {
1532 vars_to_check=UPTIME;
1533 }
1534 else if (strncmp(optarg,"NLM:",4)==0) {
1535 vars_to_check=NLM;
1536 nlm_name=strdup (optarg+4);
1537 }
1538 else if (strncmp(optarg,"NRMP",4)==0) {
1539 vars_to_check=NRMP;
1540 nrmp_name = strdup (optarg+4);
1541 if (!strcmp(nrmp_name,""))
1542 nrmp_name = strdup ("AVAILABLE_MEMORY");
1543 }
1544 else if (strncmp(optarg,"NRMM",4)==0) {
1545 vars_to_check=NRMM;
1546 nrmm_name = strdup (optarg+4);
1547 if (!strcmp(nrmm_name,""))
1548 nrmm_name = strdup ("AVAILABLE_CACHE_MEMORY");
1549
1550 }
1551
1552 else if (strncmp(optarg,"NRMS",4)==0) {
1553 vars_to_check=NRMS;
1554 nrms_name = strdup (optarg+4);
1555 if (!strcmp(nrms_name,""))
1556 nrms_name = strdup ("USED_SWAP_SPACE");
1557
1558 }
1559
1560 else if (strncmp(optarg,"NSS1",4)==0) {
1561 vars_to_check=NSS1;
1562 nss1_name = strdup (optarg+4);
1563 if (!strcmp(nss1_name,""))
1564 nss1_name = strdup ("CURRENTBUFFERCACHESIZE");
1565
1566 }
1567
1568 else if (strncmp(optarg,"NSS2",4)==0) {
1569 vars_to_check=NSS2;
1570 nss2_name = strdup (optarg+4);
1571 if (!strcmp(nss2_name,""))
1572 nss2_name = strdup ("CACHEHITS");
1573
1574 }
1575
1576 else if (strncmp(optarg,"NSS3",4)==0) {
1577 vars_to_check=NSS3;
1578 nss3_name = strdup (optarg+4);
1579 if (!strcmp(nss3_name,""))
1580 nss3_name = strdup ("CACHEGITPERCENT");
1581
1582 }
1583
1584 else if (strncmp(optarg,"NSS4",4)==0) {
1585 vars_to_check=NSS4;
1586 nss4_name = strdup (optarg+4);
1587 if (!strcmp(nss4_name,""))
1588 nss4_name = strdup ("CURRENTOPENCOUNT");
1589
1590 }
1591
1592 else if (strncmp(optarg,"NSS5",4)==0) {
1593 vars_to_check=NSS5;
1594 nss5_name = strdup (optarg+4);
1595 if (!strcmp(nss5_name,""))
1596 nss5_name = strdup ("CACHEMISSES");
1597
1598 }
1599
1600
1601 else if (strncmp(optarg,"NSS6",4)==0) {
1602 vars_to_check=NSS6;
1603 nss6_name = strdup (optarg+4);
1604 if (!strcmp(nss6_name,""))
1605 nss6_name = strdup ("PENDINGWORKSCOUNT");
1606
1607 }
1608
1609
1610 else if (strncmp(optarg,"NSS7",4)==0) {
1611 vars_to_check=NSS7;
1612 nss7_name = strdup (optarg+4);
1613 if (!strcmp(nss7_name,""))
1614 nss7_name = strdup ("CACHESIZE");
1615
1616 }
1617
1618
1619 else
1620 return ERROR;
1621 break;
1622 case 'w': /* warning threshold */
1623 warning_value=strtoul(optarg,NULL,10);
1624 check_warning_value = true;
1625 break;
1626 case 'c': /* critical threshold */
1627 critical_value=strtoul(optarg,NULL,10);
1628 check_critical_value = true;
1629 break;
1630 case 't': /* timeout */
1631 socket_timeout=atoi(optarg);
1632 if (socket_timeout<=0)
1633 return ERROR;
1634 }
1635
1636 }
1637
1638 return OK;
1639}
1640
1641
1642
1643void print_help(void)
1644{
1645 char *myport;
1646 xasprintf (&myport, "%d", PORT);
1647
1648 print_revision (progname, NP_VERSION);
1649
1650 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1651 printf (COPYRIGHT, copyright, email);
1652
1653 printf ("%s\n", _("This plugin attempts to contact the MRTGEXT NLM running on a"));
1654 printf ("%s\n", _("Novell server to gather the requested system information."));
1655
1656 printf ("\n\n");
1657
1658 print_usage();
1659
1660 printf (UT_HELP_VRSN);
1661 printf (UT_EXTRA_OPTS);
1662
1663 printf (UT_HOST_PORT, 'p', myport);
1664
1665 printf (" %s\n", "-v, --variable=STRING");
1666 printf (" %s\n", _("Variable to check. Valid variables include:"));
1667 printf (" %s\n", _("LOAD1 = 1 minute average CPU load"));
1668 printf (" %s\n", _("LOAD5 = 5 minute average CPU load"));
1669 printf (" %s\n", _("LOAD15 = 15 minute average CPU load"));
1670 printf (" %s\n", _("CSPROCS = number of current service processes (NW 5.x only)"));
1671 printf (" %s\n", _("ABENDS = number of abended threads (NW 5.x only)"));
1672 printf (" %s\n", _("UPTIME = server uptime"));
1673 printf (" %s\n", _("LTCH = percent long term cache hits"));
1674 printf (" %s\n", _("CBUFF = current number of cache buffers"));
1675 printf (" %s\n", _("CDBUFF = current number of dirty cache buffers"));
1676 printf (" %s\n", _("DCB = dirty cache buffers as a percentage of the total"));
1677 printf (" %s\n", _("TCB = dirty cache buffers as a percentage of the original"));
1678 printf (" %s\n", _("OFILES = number of open files"));
1679 printf (" %s\n", _(" VMF<vol> = MB of free space on Volume <vol>"));
1680 printf (" %s\n", _(" VMU<vol> = MB used space on Volume <vol>"));
1681 printf (" %s\n", _(" VPU<vol> = percent used space on Volume <vol>"));
1682 printf (" %s\n", _(" VMP<vol> = MB of purgeable space on Volume <vol>"));
1683 printf (" %s\n", _(" VPF<vol> = percent free space on volume <vol>"));
1684 printf (" %s\n", _(" VKF<vol> = KB of free space on volume <vol>"));
1685 printf (" %s\n", _(" VPP<vol> = percent purgeable space on volume <vol>"));
1686 printf (" %s\n", _(" VKP<vol> = KB of purgeable space on volume <vol>"));
1687 printf (" %s\n", _(" VPNP<vol> = percent not yet purgeable space on volume <vol>"));
1688 printf (" %s\n", _(" VKNP<vol> = KB of not yet purgeable space on volume <vol>"));
1689 printf (" %s\n", _(" LRUM = LRU sitting time in minutes"));
1690 printf (" %s\n", _(" LRUS = LRU sitting time in seconds"));
1691 printf (" %s\n", _(" DSDB = check to see if DS Database is open"));
1692 printf (" %s\n", _(" DSVER = NDS version"));
1693 printf (" %s\n", _(" UPRB = used packet receive buffers"));
1694 printf (" %s\n", _(" PUPRB = percent (of max) used packet receive buffers"));
1695 printf (" %s\n", _(" SAPENTRIES = number of entries in the SAP table"));
1696 printf (" %s\n", _(" SAPENTRIES<n> = number of entries in the SAP table for SAP type <n>"));
1697 printf (" %s\n", _(" TSYNC = timesync status"));
1698 printf (" %s\n", _(" LOGINS = check to see if logins are enabled"));
1699 printf (" %s\n", _(" CONNS = number of currently licensed connections"));
1700 printf (" %s\n", _(" NRMH = NRM Summary Status"));
1701 printf (" %s\n", _(" NRMP<stat> = Returns the current value for a NRM health item"));
1702 printf (" %s\n", _(" NRMM<stat> = Returns the current memory stats from NRM"));
1703 printf (" %s\n", _(" NRMS<stat> = Returns the current Swapfile stats from NRM"));
1704 printf (" %s\n", _(" NSS1<stat> = Statistics from _Admin:Manage_NSS\\GeneralStats.xml"));
1705 printf (" %s\n", _(" NSS3<stat> = Statistics from _Admin:Manage_NSS\\NameCache.xml"));
1706 printf (" %s\n", _(" NSS4<stat> = Statistics from _Admin:Manage_NSS\\FileStats.xml"));
1707 printf (" %s\n", _(" NSS5<stat> = Statistics from _Admin:Manage_NSS\\ObjectCache.xml"));
1708 printf (" %s\n", _(" NSS6<stat> = Statistics from _Admin:Manage_NSS\\Thread.xml"));
1709 printf (" %s\n", _(" NSS7<stat> = Statistics from _Admin:Manage_NSS\\AuthorizationCache.xml"));
1710 printf (" %s\n", _(" NLM:<nlm> = check if NLM is loaded and report version"));
1711 printf (" %s\n", _(" (e.g. NLM:TSANDS.NLM)"));
1712 printf ("\n");
1713 printf (" %s\n", "-w, --warning=INTEGER");
1714 printf (" %s\n", _("Threshold which will result in a warning status"));
1715 printf (" %s\n", "-c, --critical=INTEGER");
1716 printf (" %s\n", _("Threshold which will result in a critical status"));
1717 printf (" %s\n", "-o, --osversion");
1718 printf (" %s\n", _("Include server version string in results"));
1719
1720 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1721
1722 printf ("\n");
1723 printf ("%s\n", _("Notes:"));
1724 printf (" %s\n", _("- This plugin requires that the MRTGEXT.NLM file from James Drews' MRTG"));
1725 printf (" %s\n", _(" extension for NetWare be loaded on the Novell servers you wish to check."));
1726 printf (" %s\n", _(" (available from http://www.engr.wisc.edu/~drews/mrtg/)"));
1727 printf (" %s\n", _("- Values for critical thresholds should be lower than warning thresholds"));
1728 printf (" %s\n", _(" when the following variables are checked: VPF, VKF, LTCH, CBUFF, DCB, "));
1729 printf (" %s\n", _(" TCB, LRUS and LRUM."));
1730
1731 printf (UT_SUPPORT);
1732}
1733
1734
1735
1736void print_usage(void)
1737{
1738 printf ("%s\n", _("Usage:"));
1739 printf ("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n",progname);
1740}
diff --git a/plugins/check_overcr.c b/plugins/check_overcr.c
deleted file mode 100644
index 5165c828..00000000
--- a/plugins/check_overcr.c
+++ /dev/null
@@ -1,469 +0,0 @@
1/*****************************************************************************
2*
3* Monitoring check_overcr plugin
4*
5* License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team
7*
8* Description:
9*
10* This file contains the check_overcr plugin
11*
12* This plugin attempts to contact the Over-CR collector daemon running on the
13* remote UNIX server in order to gather the requested system information.
14*
15*
16* This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version.
20*
21* This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details.
25*
26* You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>.
28*
29*
30*****************************************************************************/
31
32const char *progname = "check_overcr";
33const char *copyright = "2000-2007";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h"
37#include "netutils.h"
38#include "utils.h"
39
40enum checkvar {
41 NONE,
42 LOAD1,
43 LOAD5,
44 LOAD15,
45 DPU,
46 PROCS,
47 NETSTAT,
48 UPTIME
49};
50
51enum {
52 PORT = 2000
53};
54
55char *server_address = NULL;
56int server_port = PORT;
57double warning_value = 0L;
58double critical_value = 0L;
59bool check_warning_value = false;
60bool check_critical_value = false;
61enum checkvar vars_to_check = NONE;
62int cmd_timeout = 1;
63
64int netstat_port = 0;
65char *disk_name = NULL;
66char *process_name = NULL;
67char send_buffer[MAX_INPUT_BUFFER];
68
69int process_arguments (int, char **);
70void print_usage (void);
71void print_help (void);
72
73int
74main (int argc, char **argv)
75{
76 int result = STATE_UNKNOWN;
77 char recv_buffer[MAX_INPUT_BUFFER];
78 char temp_buffer[MAX_INPUT_BUFFER];
79 char *temp_ptr = NULL;
80 bool found_disk = false;
81 unsigned long percent_used_disk_space = 100;
82 double load;
83 double load_1min;
84 double load_5min;
85 double load_15min;
86 int port_connections = 0;
87 int processes = 0;
88 double uptime_raw_hours;
89 int uptime_raw_minutes = 0;
90 int uptime_days = 0;
91 int uptime_hours = 0;
92 int uptime_minutes = 0;
93
94 setlocale (LC_ALL, "");
95 bindtextdomain (PACKAGE, LOCALEDIR);
96 textdomain (PACKAGE);
97
98 /* Parse extra opts if any */
99 argv=np_extra_opts (&argc, argv, progname);
100
101 if (process_arguments (argc, argv) == ERROR)
102 usage4 (_("Could not parse arguments"));
103
104 /* initialize alarm signal handling */
105 signal (SIGALRM, socket_timeout_alarm_handler);
106
107 /* set socket timeout */
108 alarm (socket_timeout);
109
110 result = process_tcp_request2 (server_address,
111 server_port,
112 send_buffer,
113 recv_buffer,
114 sizeof (recv_buffer));
115
116 switch (vars_to_check) {
117
118 case LOAD1:
119 case LOAD5:
120 case LOAD15:
121
122 if (result != STATE_OK)
123 die (result, _("Unknown error fetching load data\n"));
124
125 temp_ptr = (char *) strtok (recv_buffer, "\r\n");
126 if (temp_ptr == NULL)
127 die (STATE_CRITICAL, _("Invalid response from server - no load information\n"));
128 else
129 load_1min = strtod (temp_ptr, NULL);
130
131 temp_ptr = (char *) strtok (NULL, "\r\n");
132 if (temp_ptr == NULL)
133 die (STATE_CRITICAL, _("Invalid response from server after load 1\n"));
134 else
135 load_5min = strtod (temp_ptr, NULL);
136
137 temp_ptr = (char *) strtok (NULL, "\r\n");
138 if (temp_ptr == NULL)
139 die (STATE_CRITICAL, _("Invalid response from server after load 5\n"));
140 else
141 load_15min = strtod (temp_ptr, NULL);
142
143 switch (vars_to_check) {
144 case LOAD1:
145 strcpy (temp_buffer, "1");
146 load = load_1min;
147 break;
148 case LOAD5:
149 strcpy (temp_buffer, "5");
150 load = load_5min;
151 break;
152 default:
153 strcpy (temp_buffer, "15");
154 load = load_15min;
155 break;
156 }
157
158 if (check_critical_value && (load >= critical_value))
159 result = STATE_CRITICAL;
160 else if (check_warning_value && (load >= warning_value))
161 result = STATE_WARNING;
162
163 die (result,
164 _("Load %s - %s-min load average = %0.2f"),
165 state_text(result),
166 temp_buffer,
167 load);
168
169 break;
170
171 case DPU:
172
173 if (result != STATE_OK)
174 die (result, _("Unknown error fetching disk data\n"));
175
176 for (temp_ptr = (char *) strtok (recv_buffer, " ");
177 temp_ptr != NULL;
178 temp_ptr = (char *) strtok (NULL, " ")) {
179
180 if (!strcmp (temp_ptr, disk_name)) {
181 found_disk = true;
182 temp_ptr = (char *) strtok (NULL, "%");
183 if (temp_ptr == NULL)
184 die (STATE_CRITICAL, _("Invalid response from server\n"));
185 else
186 percent_used_disk_space = strtoul (temp_ptr, NULL, 10);
187 break;
188 }
189
190 temp_ptr = (char *) strtok (NULL, "\r\n");
191 }
192
193 /* error if we couldn't find the info for the disk */
194 if (!found_disk)
195 die (STATE_CRITICAL,
196 "CRITICAL - Disk '%s' non-existent or not mounted",
197 disk_name);
198
199 if (check_critical_value && (percent_used_disk_space >= critical_value))
200 result = STATE_CRITICAL;
201 else if (check_warning_value && (percent_used_disk_space >= warning_value))
202 result = STATE_WARNING;
203
204 die (result, "Disk %s - %lu%% used on %s", state_text(result), percent_used_disk_space, disk_name);
205
206 break;
207
208 case NETSTAT:
209
210 if (result != STATE_OK)
211 die (result, _("Unknown error fetching network status\n"));
212 else
213 port_connections = strtod (recv_buffer, NULL);
214
215 if (check_critical_value && (port_connections >= critical_value))
216 result = STATE_CRITICAL;
217 else if (check_warning_value && (port_connections >= warning_value))
218 result = STATE_WARNING;
219
220 die (result,
221 _("Net %s - %d connection%s on port %d"),
222 state_text(result),
223 port_connections,
224 (port_connections == 1) ? "" : "s",
225 netstat_port);
226
227 break;
228
229 case PROCS:
230
231 if (result != STATE_OK)
232 die (result, _("Unknown error fetching process status\n"));
233
234 temp_ptr = (char *) strtok (recv_buffer, "(");
235 if (temp_ptr == NULL)
236 die (STATE_CRITICAL, _("Invalid response from server\n"));
237
238 temp_ptr = (char *) strtok (NULL, ")");
239 if (temp_ptr == NULL)
240 die (STATE_CRITICAL, _("Invalid response from server\n"));
241 else
242 processes = strtod (temp_ptr, NULL);
243
244 if (check_critical_value && (processes >= critical_value))
245 result = STATE_CRITICAL;
246 else if (check_warning_value && (processes >= warning_value))
247 result = STATE_WARNING;
248
249 die (result,
250 _("Process %s - %d instance%s of %s running"),
251 state_text(result),
252 processes,
253 (processes == 1) ? "" : "s",
254 process_name);
255 break;
256
257 case UPTIME:
258
259 if (result != STATE_OK)
260 return result;
261
262 uptime_raw_hours = strtod (recv_buffer, NULL);
263 uptime_raw_minutes = (unsigned long) (uptime_raw_hours * 60.0);
264
265 if (check_critical_value && (uptime_raw_minutes <= critical_value))
266 result = STATE_CRITICAL;
267 else if (check_warning_value && (uptime_raw_minutes <= warning_value))
268 result = STATE_WARNING;
269
270 uptime_days = uptime_raw_minutes / 1440;
271 uptime_raw_minutes %= 1440;
272 uptime_hours = uptime_raw_minutes / 60;
273 uptime_raw_minutes %= 60;
274 uptime_minutes = uptime_raw_minutes;
275
276 die (result,
277 _("Uptime %s - Up %d days %d hours %d minutes"),
278 state_text(result),
279 uptime_days,
280 uptime_hours,
281 uptime_minutes);
282 break;
283
284 default:
285 die (STATE_UNKNOWN, _("Nothing to check!\n"));
286 break;
287 }
288}
289
290
291/* process command-line arguments */
292int
293process_arguments (int argc, char **argv)
294{
295 int c;
296
297 int option = 0;
298 static struct option longopts[] = {
299 {"port", required_argument, 0, 'p'},
300 {"timeout", required_argument, 0, 't'},
301 {"critical", required_argument, 0, 'c'},
302 {"warning", required_argument, 0, 'w'},
303 {"variable", required_argument, 0, 'v'},
304 {"hostname", required_argument, 0, 'H'},
305 {"version", no_argument, 0, 'V'},
306 {"help", no_argument, 0, 'h'},
307 {0, 0, 0, 0}
308 };
309
310 /* no options were supplied */
311 if (argc < 2)
312 return ERROR;
313
314 /* backwards compatibility */
315 if (!is_option (argv[1])) {
316 server_address = argv[1];
317 argv[1] = argv[0];
318 argv = &argv[1];
319 argc--;
320 }
321
322 for (c = 1; c < argc; c++) {
323 if (strcmp ("-to", argv[c]) == 0)
324 strcpy (argv[c], "-t");
325 else if (strcmp ("-wv", argv[c]) == 0)
326 strcpy (argv[c], "-w");
327 else if (strcmp ("-cv", argv[c]) == 0)
328 strcpy (argv[c], "-c");
329 }
330
331 while (1) {
332 c = getopt_long (argc, argv, "+hVH:t:c:w:p:v:", longopts,
333 &option);
334
335 if (c == -1 || c == EOF || c == 1)
336 break;
337
338 switch (c) {
339 case '?': /* print short usage statement if args not parsable */
340 usage5 ();
341 case 'h': /* help */
342 print_help ();
343 exit (STATE_UNKNOWN);
344 case 'V': /* version */
345 print_revision (progname, NP_VERSION);
346 exit (STATE_UNKNOWN);
347 case 'H': /* hostname */
348 server_address = optarg;
349 break;
350 case 'p': /* port */
351 if (is_intnonneg (optarg))
352 server_port = atoi (optarg);
353 else
354 die (STATE_UNKNOWN,
355 _("Server port an integer\n"));
356 break;
357 case 'v': /* variable */
358 if (strcmp (optarg, "LOAD") == 0) {
359 strcpy (send_buffer, "LOAD\r\nQUIT\r\n");
360 if (strcmp (optarg, "LOAD1") == 0)
361 vars_to_check = LOAD1;
362 else if (strcmp (optarg, "LOAD5") == 0)
363 vars_to_check = LOAD5;
364 else if (strcmp (optarg, "LOAD15") == 0)
365 vars_to_check = LOAD15;
366 }
367 else if (strcmp (optarg, "UPTIME") == 0) {
368 vars_to_check = UPTIME;
369 strcpy (send_buffer, "UPTIME\r\n");
370 }
371 else if (strstr (optarg, "PROC") == optarg) {
372 vars_to_check = PROCS;
373 process_name = strscpy (process_name, optarg + 4);
374 sprintf (send_buffer, "PROCESS %s\r\n", process_name);
375 }
376 else if (strstr (optarg, "NET") == optarg) {
377 vars_to_check = NETSTAT;
378 netstat_port = atoi (optarg + 3);
379 sprintf (send_buffer, "NETSTAT %d\r\n", netstat_port);
380 }
381 else if (strstr (optarg, "DPU") == optarg) {
382 vars_to_check = DPU;
383 strcpy (send_buffer, "DISKSPACE\r\n");
384 disk_name = strscpy (disk_name, optarg + 3);
385 }
386 else
387 return ERROR;
388 break;
389 case 'w': /* warning threshold */
390 warning_value = strtoul (optarg, NULL, 10);
391 check_warning_value = true;
392 break;
393 case 'c': /* critical threshold */
394 critical_value = strtoul (optarg, NULL, 10);
395 check_critical_value = true;
396 break;
397 case 't': /* timeout */
398 socket_timeout = atoi (optarg);
399 if (socket_timeout <= 0)
400 return ERROR;
401 }
402
403 }
404 return OK;
405}
406
407
408void
409print_help (void)
410{
411 char *myport;
412 xasprintf (&myport, "%d", PORT);
413
414 print_revision (progname, NP_VERSION);
415
416 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
417 printf (COPYRIGHT, copyright, email);
418
419 printf ("%s\n", _("This plugin attempts to contact the Over-CR collector daemon running on the"));
420 printf ("%s\n", _("remote UNIX server in order to gather the requested system information."));
421
422 printf ("\n\n");
423
424 print_usage ();
425
426 printf (UT_HELP_VRSN);
427 printf (UT_EXTRA_OPTS);
428
429 printf (UT_HOST_PORT, 'p', myport);
430
431 printf (" %s\n", "-w, --warning=INTEGER");
432 printf (" %s\n", _("Threshold which will result in a warning status"));
433 printf (" %s\n", "-c, --critical=INTEGER");
434 printf (" %s\n", _("Threshold which will result in a critical status"));
435 printf (" %s\n", "-v, --variable=STRING");
436 printf (" %s\n", _("Variable to check. Valid variables include:"));
437 printf (" %s\n", _("LOAD1 = 1 minute average CPU load"));
438 printf (" %s\n", _("LOAD5 = 5 minute average CPU load"));
439 printf (" %s\n", _("LOAD15 = 15 minute average CPU load"));
440 printf (" %s\n", _("DPU<filesys> = percent used disk space on filesystem <filesys>"));
441 printf (" %s\n", _("PROC<process> = number of running processes with name <process>"));
442 printf (" %s\n", _("NET<port> = number of active connections on TCP port <port>"));
443 printf (" %s\n", _("UPTIME = system uptime in seconds"));
444
445 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
446
447 printf (UT_VERBOSE);
448
449 printf ("\n");
450 printf ("%s\n", _("This plugin requires that Eric Molitors' Over-CR collector daemon be"));
451 printf ("%s\n", _("running on the remote server."));
452 printf ("%s\n", _("Over-CR can be downloaded from http://www.molitor.org/overcr"));
453 printf ("%s\n", _("This plugin was tested with version 0.99.53 of the Over-CR collector"));
454
455 printf ("\n");
456 printf ("%s\n", _("Notes:"));
457 printf (" %s\n", _("For the available options, the critical threshold value should always be"));
458 printf (" %s\n", _("higher than the warning threshold value, EXCEPT with the uptime variable"));
459
460 printf (UT_SUPPORT);
461}
462
463
464void
465print_usage (void)
466{
467 printf ("%s\n", _("Usage:"));
468 printf ("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname);
469}
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index 94d589e1..793a686f 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -1,95 +1,77 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_pgsql plugin 3 * Monitoring check_pgsql plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2011 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_pgsql plugin 10 * This file contains the check_pgsql plugin
11* 11 *
12* Test whether a PostgreSQL Database is accepting connections. 12 * Test whether a PostgreSQL Database is accepting connections.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
31const char *progname = "check_pgsql"; 32const char *progname = "check_pgsql";
32const char *copyright = "1999-2011"; 33const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
34 35
35#include "common.h" 36#include "common.h"
36#include "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, \ 50 (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \
49 (server_version) / 100 - (int)((server_version) / 10000) * 100, \ 51 (server_version) - (int)((server_version) / 100) * 100
50 (server_version) - (int)((server_version) / 100) * 100
51/* return true if the given host is a UNIX domain socket */ 52/* return true if the given host is a UNIX domain socket */
52#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) \ 53#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
53 ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
54/* return a 3-tuple identifying a host/port independent of the socket type */ 54/* return a 3-tuple identifying a host/port independent of the socket type */
55#define PSQL_SOCKET3(host, port) \ 55#define PSQL_SOCKET3(host, port) \
56 ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \ 56 ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \
57 PSQL_IS_UNIX_DOMAIN_SOCKET (host) ? "/.s.PGSQL." : ":", \ 57 PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port
58 port 58
59 59typedef struct {
60enum { 60 int errorcode;
61 DEFAULT_PORT = 5432, 61 check_pgsql_config config;
62 DEFAULT_WARN = 2, 62} check_pgsql_config_wrapper;
63 DEFAULT_CRIT = 8 63static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
64}; 64
65 65static void print_help(void);
66 66static bool is_pg_logname(char * /*username*/);
67 67static mp_state_enum do_query(PGconn * /*conn*/, char * /*query*/, const char /*pgqueryname*/[],
68int process_arguments (int, char **); 68 thresholds * /*qthresholds*/, char * /*query_warning*/,
69int validate_arguments (void); 69 char * /*query_critical*/);
70void print_usage (void); 70void print_usage(void);
71void print_help (void); 71
72bool is_pg_logname (char *); 72static int verbose = 0;
73int do_query (PGconn *, char *); 73
74
75char *pghost = NULL; /* host name of the backend server */
76char *pgport = NULL; /* port of the backend server */
77int default_port = DEFAULT_PORT;
78char *pgoptions = NULL;
79char *pgtty = NULL;
80char dbName[NAMEDATALEN] = DEFAULT_DB;
81char *pguser = NULL;
82char *pgpasswd = NULL;
83char *pgparams = NULL;
84double twarn = (double)DEFAULT_WARN;
85double tcrit = (double)DEFAULT_CRIT;
86char *pgquery = NULL;
87#define OPTID_QUERYNAME -1000 74#define OPTID_QUERYNAME -1000
88char *pgqueryname = NULL;
89char *query_warning = NULL;
90char *query_critical = NULL;
91thresholds *qthresholds = NULL;
92int verbose = 0;
93 75
94/****************************************************************************** 76/******************************************************************************
95 77
@@ -141,237 +123,239 @@ Please note that all tags must be lowercase to use the DocBook XML DTD.
141-@@ 123-@@
142******************************************************************************/ 124******************************************************************************/
143 125
126int main(int argc, char **argv) {
127 setlocale(LC_ALL, "");
128 bindtextdomain(PACKAGE, LOCALEDIR);
129 textdomain(PACKAGE);
144 130
131 /* Parse extra opts if any */
132 argv = np_extra_opts(&argc, argv, progname);
145 133
146int 134 check_pgsql_config_wrapper tmp_config = process_arguments(argc, argv);
147main (int argc, char **argv) 135 if (tmp_config.errorcode == ERROR) {
148{ 136 usage4(_("Could not parse arguments"));
149 PGconn *conn; 137 }
150 char *conninfo = NULL;
151
152 struct timeval start_timeval;
153 struct timeval end_timeval;
154 double elapsed_time;
155 int status = STATE_UNKNOWN;
156 int query_status = STATE_UNKNOWN;
157
158 /* begin, by setting the parameters for a backend connection if the
159 * parameters are null, then the system will try to use reasonable
160 * defaults by looking up environment variables or, failing that,
161 * using hardwired constants */
162
163 pgoptions = NULL; /* special options to start up the backend server */
164 pgtty = NULL; /* debugging tty for the backend server */
165 138
166 setlocale (LC_ALL, ""); 139 const check_pgsql_config config = tmp_config.config;
167 bindtextdomain (PACKAGE, LOCALEDIR);
168 textdomain (PACKAGE);
169 140
170 /* Parse extra opts if any */ 141 if (verbose > 2) {
171 argv=np_extra_opts (&argc, argv, progname);
172
173 if (process_arguments (argc, argv) == ERROR)
174 usage4 (_("Could not parse arguments"));
175 if (verbose > 2)
176 printf("Arguments initialized\n"); 142 printf("Arguments initialized\n");
143 }
177 144
178 /* Set signal handling and alarm */ 145 /* Set signal handling and alarm */
179 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 146 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
180 usage4 (_("Cannot catch SIGALRM")); 147 usage4(_("Cannot catch SIGALRM"));
148 }
149 alarm(timeout_interval);
150
151 char *conninfo = NULL;
152 if (config.pgparams) {
153 asprintf(&conninfo, "%s ", config.pgparams);
154 }
155
156 asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", config.dbName);
157 if (config.pghost) {
158 asprintf(&conninfo, "%s host = '%s'", conninfo, config.pghost);
159 }
160 if (config.pgport) {
161 asprintf(&conninfo, "%s port = '%s'", conninfo, config.pgport);
162 }
163 if (config.pgoptions) {
164 asprintf(&conninfo, "%s options = '%s'", conninfo, config.pgoptions);
181 } 165 }
182 alarm (timeout_interval);
183
184 if (pgparams)
185 asprintf (&conninfo, "%s ", pgparams);
186
187 asprintf (&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", dbName);
188 if (pghost)
189 asprintf (&conninfo, "%s host = '%s'", conninfo, pghost);
190 if (pgport)
191 asprintf (&conninfo, "%s port = '%s'", conninfo, pgport);
192 if (pgoptions)
193 asprintf (&conninfo, "%s options = '%s'", conninfo, pgoptions);
194 /* if (pgtty) -- ignored by PQconnectdb */ 166 /* if (pgtty) -- ignored by PQconnectdb */
195 if (pguser) 167 if (config.pguser) {
196 asprintf (&conninfo, "%s user = '%s'", conninfo, pguser); 168 asprintf(&conninfo, "%s user = '%s'", conninfo, config.pguser);
169 }
197 170
198 if (verbose) /* do not include password (see right below) in output */ 171 if (verbose) { /* do not include password (see right below) in output */
199 printf ("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo, 172 printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo,
200 pgpasswd ? " password = <hidden>" : ""); 173 config.pgpasswd ? " password = <hidden>" : "");
174 }
201 175
202 if (pgpasswd) 176 if (config.pgpasswd) {
203 asprintf (&conninfo, "%s password = '%s'", conninfo, pgpasswd); 177 asprintf(&conninfo, "%s password = '%s'", conninfo, config.pgpasswd);
178 }
204 179
205 /* make a connection to the database */ 180 /* make a connection to the database */
206 gettimeofday (&start_timeval, NULL); 181 struct timeval start_timeval;
207 conn = PQconnectdb (conninfo); 182 gettimeofday(&start_timeval, NULL);
208 gettimeofday (&end_timeval, NULL); 183 PGconn *conn = PQconnectdb(conninfo);
184 struct timeval end_timeval;
185 gettimeofday(&end_timeval, NULL);
209 186
210 while (start_timeval.tv_usec > end_timeval.tv_usec) { 187 while (start_timeval.tv_usec > end_timeval.tv_usec) {
211 --end_timeval.tv_sec; 188 --end_timeval.tv_sec;
212 end_timeval.tv_usec += 1000000; 189 end_timeval.tv_usec += 1000000;
213 } 190 }
214 elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) 191 double elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) +
215 + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0; 192 ((double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0);
216 193
217 if (verbose) 194 if (verbose) {
218 printf("Time elapsed: %f\n", elapsed_time); 195 printf("Time elapsed: %f\n", elapsed_time);
196 }
219 197
220 /* check to see that the backend connection was successfully made */ 198 /* check to see that the backend connection was successfully made */
221 if (verbose) 199 if (verbose) {
222 printf("Verifying connection\n"); 200 printf("Verifying connection\n");
223 if (PQstatus (conn) == CONNECTION_BAD) { 201 }
224 printf (_("CRITICAL - no connection to '%s' (%s).\n"), 202 if (PQstatus(conn) == CONNECTION_BAD) {
225 dbName, PQerrorMessage (conn)); 203 printf(_("CRITICAL - no connection to '%s' (%s).\n"), config.dbName, PQerrorMessage(conn));
226 PQfinish (conn); 204 PQfinish(conn);
227 return STATE_CRITICAL; 205 return STATE_CRITICAL;
228 } 206 }
229 else if (elapsed_time > tcrit) { 207
208 mp_state_enum status = STATE_UNKNOWN;
209 if (elapsed_time > config.tcrit) {
230 status = STATE_CRITICAL; 210 status = STATE_CRITICAL;
231 } 211 } else if (elapsed_time > config.twarn) {
232 else if (elapsed_time > twarn) {
233 status = STATE_WARNING; 212 status = STATE_WARNING;
234 } 213 } else {
235 else {
236 status = STATE_OK; 214 status = STATE_OK;
237 } 215 }
238 216
239 if (verbose) { 217 if (verbose) {
240 char *server_host = PQhost (conn); 218 char *server_host = PQhost(conn);
241 int server_version = PQserverVersion (conn); 219 int server_version = PQserverVersion(conn);
242 220
243 printf ("Successfully connected to database %s (user %s) " 221 printf("Successfully connected to database %s (user %s) "
244 "at server %s%s%s (server version: %d.%d.%d, " 222 "at server %s%s%s (server version: %d.%d.%d, "
245 "protocol version: %d, pid: %d)\n", 223 "protocol version: %d, pid: %d)\n",
246 PQdb (conn), PQuser (conn), 224 PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)),
247 PSQL_SOCKET3 (server_host, PQport (conn)), 225 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn));
248 PSQL_SERVER_VERSION3 (server_version),
249 PQprotocolVersion (conn), PQbackendPID (conn));
250 } 226 }
251 227
252 printf (_(" %s - database %s (%f sec.)|%s\n"), 228 printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), config.dbName, elapsed_time,
253 state_text(status), dbName, elapsed_time, 229 fperfdata("time", elapsed_time, "s", (config.twarn > 0.0), config.twarn,
254 fperfdata("time", elapsed_time, "s", 230 (config.tcrit > 0.0), config.tcrit, true, 0, false, 0));
255 !!(twarn > 0.0), twarn, !!(tcrit > 0.0), tcrit, true, 0, false,0));
256 231
257 if (pgquery) 232 mp_state_enum query_status = STATE_UNKNOWN;
258 query_status = do_query (conn, pgquery); 233 if (config.pgquery) {
234 query_status = do_query(conn, config.pgquery, config.pgqueryname, config.qthresholds,
235 config.query_warning, config.query_critical);
236 }
259 237
260 if (verbose) 238 if (verbose) {
261 printf("Closing connection\n"); 239 printf("Closing connection\n");
262 PQfinish (conn); 240 }
263 return (pgquery && query_status > status) ? query_status : status; 241 PQfinish(conn);
242 return (config.pgquery && query_status > status) ? query_status : status;
264} 243}
265 244
266
267
268/* process command-line arguments */ 245/* process command-line arguments */
269int 246check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
270process_arguments (int argc, char **argv) 247 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
271{ 248 {"version", no_argument, 0, 'V'},
272 int c; 249 {"timeout", required_argument, 0, 't'},
273 250 {"critical", required_argument, 0, 'c'},
274 int option = 0; 251 {"warning", required_argument, 0, 'w'},
275 static struct option longopts[] = { 252 {"hostname", required_argument, 0, 'H'},
276 {"help", no_argument, 0, 'h'}, 253 {"logname", required_argument, 0, 'l'},
277 {"version", no_argument, 0, 'V'}, 254 {"password", required_argument, 0, 'p'},
278 {"timeout", required_argument, 0, 't'}, 255 {"authorization", required_argument, 0, 'a'},
279 {"critical", required_argument, 0, 'c'}, 256 {"port", required_argument, 0, 'P'},
280 {"warning", required_argument, 0, 'w'}, 257 {"database", required_argument, 0, 'd'},
281 {"hostname", required_argument, 0, 'H'}, 258 {"option", required_argument, 0, 'o'},
282 {"logname", required_argument, 0, 'l'}, 259 {"query", required_argument, 0, 'q'},
283 {"password", required_argument, 0, 'p'}, 260 {"queryname", required_argument, 0, OPTID_QUERYNAME},
284 {"authorization", required_argument, 0, 'a'}, 261 {"query_critical", required_argument, 0, 'C'},
285 {"port", required_argument, 0, 'P'}, 262 {"query_warning", required_argument, 0, 'W'},
286 {"database", required_argument, 0, 'd'}, 263 {"verbose", no_argument, 0, 'v'},
287 {"option", required_argument, 0, 'o'}, 264 {0, 0, 0, 0}};
288 {"query", required_argument, 0, 'q'}, 265
289 {"queryname", required_argument, 0, OPTID_QUERYNAME}, 266 check_pgsql_config_wrapper result = {
290 {"query_critical", required_argument, 0, 'C'}, 267 .errorcode = OK,
291 {"query_warning", required_argument, 0, 'W'}, 268 .config = check_pgsql_config_init(),
292 {"verbose", no_argument, 0, 'v'},
293 {0, 0, 0, 0}
294 }; 269 };
295 270
296 while (1) { 271 while (true) {
297 c = getopt_long (argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", 272 int option = 0;
298 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);
299 275
300 if (c == EOF) 276 if (option_char == EOF) {
301 break; 277 break;
278 }
302 279
303 switch (c) { 280 switch (option_char) {
304 case '?': /* usage */ 281 case '?': /* usage */
305 usage5 (); 282 usage5();
306 case 'h': /* help */ 283 case 'h': /* help */
307 print_help (); 284 print_help();
308 exit (STATE_UNKNOWN); 285 exit(STATE_UNKNOWN);
309 case 'V': /* version */ 286 case 'V': /* version */
310 print_revision (progname, NP_VERSION); 287 print_revision(progname, NP_VERSION);
311 exit (STATE_UNKNOWN); 288 exit(STATE_UNKNOWN);
312 case 't': /* timeout period */ 289 case 't': /* timeout period */
313 if (!is_integer (optarg)) 290 if (!is_integer(optarg)) {
314 usage2 (_("Timeout interval must be a positive integer"), optarg); 291 usage2(_("Timeout interval must be a positive integer"), optarg);
315 else 292 } else {
316 timeout_interval = atoi (optarg); 293 timeout_interval = atoi(optarg);
294 }
317 break; 295 break;
318 case 'c': /* critical time threshold */ 296 case 'c': /* critical time threshold */
319 if (!is_nonnegative (optarg)) 297 if (!is_nonnegative(optarg)) {
320 usage2 (_("Critical threshold must be a positive integer"), optarg); 298 usage2(_("Critical threshold must be a positive integer"), optarg);
321 else 299 } else {
322 tcrit = strtod (optarg, NULL); 300 result.config.tcrit = strtod(optarg, NULL);
301 }
323 break; 302 break;
324 case 'w': /* warning time threshold */ 303 case 'w': /* warning time threshold */
325 if (!is_nonnegative (optarg)) 304 if (!is_nonnegative(optarg)) {
326 usage2 (_("Warning threshold must be a positive integer"), optarg); 305 usage2(_("Warning threshold must be a positive integer"), optarg);
327 else 306 } else {
328 twarn = strtod (optarg, NULL); 307 result.config.twarn = strtod(optarg, NULL);
308 }
329 break; 309 break;
330 case 'C': /* critical query threshold */ 310 case 'C': /* critical query threshold */
331 query_critical = optarg; 311 result.config.query_critical = optarg;
332 break; 312 break;
333 case 'W': /* warning query threshold */ 313 case 'W': /* warning query threshold */
334 query_warning = optarg; 314 result.config.query_warning = optarg;
335 break; 315 break;
336 case 'H': /* host */ 316 case 'H': /* host */
337 if ((*optarg != '/') && (!is_host (optarg))) 317 if ((*optarg != '/') && (!is_host(optarg))) {
338 usage2 (_("Invalid hostname/address"), optarg); 318 usage2(_("Invalid hostname/address"), optarg);
339 else 319 } else {
340 pghost = optarg; 320 result.config.pghost = optarg;
321 }
341 break; 322 break;
342 case 'P': /* port */ 323 case 'P': /* port */
343 if (!is_integer (optarg)) 324 if (!is_integer(optarg)) {
344 usage2 (_("Port must be a positive integer"), optarg); 325 usage2(_("Port must be a positive integer"), optarg);
345 else 326 } else {
346 pgport = optarg; 327 result.config.pgport = optarg;
328 }
347 break; 329 break;
348 case 'd': /* database name */ 330 case 'd': /* database name */
349 if (strlen(optarg) >= NAMEDATALEN) { 331 if (strlen(optarg) >= NAMEDATALEN) {
350 usage2 (_("Database name exceeds the maximum length"), optarg); 332 usage2(_("Database name exceeds the maximum length"), optarg);
351 } 333 }
352 snprintf(dbName, NAMEDATALEN, "%s", optarg); 334 snprintf(result.config.dbName, NAMEDATALEN, "%s", optarg);
353 break; 335 break;
354 case 'l': /* login name */ 336 case 'l': /* login name */
355 if (!is_pg_logname (optarg)) 337 if (!is_pg_logname(optarg)) {
356 usage2 (_("User name is not valid"), optarg); 338 usage2(_("User name is not valid"), optarg);
357 else 339 } else {
358 pguser = optarg; 340 result.config.pguser = optarg;
341 }
359 break; 342 break;
360 case 'p': /* authentication password */ 343 case 'p': /* authentication password */
361 case 'a': 344 case 'a':
362 pgpasswd = optarg; 345 result.config.pgpasswd = optarg;
363 break; 346 break;
364 case 'o': 347 case 'o':
365 if (pgparams) 348 if (result.config.pgparams) {
366 asprintf (&pgparams, "%s %s", pgparams, optarg); 349 asprintf(&result.config.pgparams, "%s %s", result.config.pgparams, optarg);
367 else 350 } else {
368 asprintf (&pgparams, "%s", optarg); 351 asprintf(&result.config.pgparams, "%s", optarg);
352 }
369 break; 353 break;
370 case 'q': 354 case 'q':
371 pgquery = optarg; 355 result.config.pgquery = optarg;
372 break; 356 break;
373 case OPTID_QUERYNAME: 357 case OPTID_QUERYNAME:
374 pgqueryname = optarg; 358 result.config.pgqueryname = optarg;
375 break; 359 break;
376 case 'v': 360 case 'v':
377 verbose++; 361 verbose++;
@@ -379,38 +363,10 @@ process_arguments (int argc, char **argv)
379 } 363 }
380 } 364 }
381 365
382 set_thresholds (&qthresholds, query_warning, query_critical); 366 set_thresholds(&result.config.qthresholds, result.config.query_warning,
367 result.config.query_critical);
383 368
384 return validate_arguments (); 369 return result;
385}
386
387
388/******************************************************************************
389
390@@-
391<sect3>
392<title>validate_arguments</title>
393
394<para>&PROTO_validate_arguments;</para>
395
396<para>Given a database name, this function returns true if the string
397is a valid PostgreSQL database name, and returns false if it is
398not.</para>
399
400<para>Valid PostgreSQL database names are less than &NAMEDATALEN;
401characters long and consist of letters, numbers, and underscores. The
402first character cannot be a number, however.</para>
403
404</sect3>
405-@@
406******************************************************************************/
407
408
409
410int
411validate_arguments ()
412{
413 return OK;
414} 370}
415 371
416/** 372/**
@@ -437,11 +393,10 @@ should be added.</para>
437-@@ 393-@@
438******************************************************************************/ 394******************************************************************************/
439 395
440 396bool is_pg_logname(char *username) {
441 397 if (strlen(username) > NAMEDATALEN - 1) {
442bool is_pg_logname (char *username) {
443 if (strlen (username) > NAMEDATALEN - 1)
444 return (false); 398 return (false);
399 }
445 return (true); 400 return (true);
446} 401}
447 402
@@ -453,182 +408,171 @@ bool is_pg_logname (char *username) {
453-@@ 408-@@
454******************************************************************************/ 409******************************************************************************/
455 410
456 411void print_help(void) {
457
458void
459print_help (void)
460{
461 char *myport; 412 char *myport;
462 413
463 xasprintf (&myport, "%d", DEFAULT_PORT); 414 xasprintf(&myport, "%d", 5432);
464 415
465 print_revision (progname, NP_VERSION); 416 print_revision(progname, NP_VERSION);
466 417
467 printf (COPYRIGHT, copyright, email); 418 printf(COPYRIGHT, copyright, email);
468 419
469 printf (_("Test whether a PostgreSQL Database is accepting connections.")); 420 printf(_("Test whether a PostgreSQL Database is accepting connections."));
470 421
471 printf ("\n\n"); 422 printf("\n\n");
472 423
473 print_usage (); 424 print_usage();
474 425
475 printf (UT_HELP_VRSN); 426 printf(UT_HELP_VRSN);
476 printf (UT_EXTRA_OPTS); 427 printf(UT_EXTRA_OPTS);
477 428
478 printf (UT_HOST_PORT, 'P', myport); 429 printf(UT_HOST_PORT, 'P', myport);
479 430
480 printf (" %s\n", "-d, --database=STRING"); 431 printf(" %s\n", "-d, --database=STRING");
481 printf (" %s", _("Database to check ")); 432 printf(" %s", _("Database to check "));
482 printf (_("(default: %s)\n"), DEFAULT_DB); 433 printf(_("(default: %s)\n"), DEFAULT_DB);
483 printf (" %s\n", "-l, --logname = STRING"); 434 printf(" %s\n", "-l, --logname = STRING");
484 printf (" %s\n", _("Login name of user")); 435 printf(" %s\n", _("Login name of user"));
485 printf (" %s\n", "-p, --password = STRING"); 436 printf(" %s\n", "-p, --password = STRING");
486 printf (" %s\n", _("Password (BIG SECURITY ISSUE)")); 437 printf(" %s\n", _("Password (BIG SECURITY ISSUE)"));
487 printf (" %s\n", "-o, --option = STRING"); 438 printf(" %s\n", "-o, --option = STRING");
488 printf (" %s\n", _("Connection parameters (keyword = value), see below")); 439 printf(" %s\n", _("Connection parameters (keyword = value), see below"));
489 440
490 printf (UT_WARN_CRIT); 441 printf(UT_WARN_CRIT);
491 442
492 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 443 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
493 444
494 printf (" %s\n", "-q, --query=STRING"); 445 printf(" %s\n", "-q, --query=STRING");
495 printf (" %s\n", _("SQL query to run. Only first column in first row will be read")); 446 printf(" %s\n", _("SQL query to run. Only first column in first row will be read"));
496 printf (" %s\n", "--queryname=STRING"); 447 printf(" %s\n", "--queryname=STRING");
497 printf (" %s\n", _("A name for the query, this string is used instead of the query")); 448 printf(" %s\n", _("A name for the query, this string is used instead of the query"));
498 printf (" %s\n", _("in the long output of the plugin")); 449 printf(" %s\n", _("in the long output of the plugin"));
499 printf (" %s\n", "-W, --query-warning=RANGE"); 450 printf(" %s\n", "-W, --query-warning=RANGE");
500 printf (" %s\n", _("SQL query value to result in warning status (double)")); 451 printf(" %s\n", _("SQL query value to result in warning status (double)"));
501 printf (" %s\n", "-C, --query-critical=RANGE"); 452 printf(" %s\n", "-C, --query-critical=RANGE");
502 printf (" %s\n", _("SQL query value to result in critical status (double)")); 453 printf(" %s\n", _("SQL query value to result in critical status (double)"));
503 454
504 printf (UT_VERBOSE); 455 printf(UT_VERBOSE);
505 456
506 printf ("\n"); 457 printf("\n");
507 printf (" %s\n", _("All parameters are optional.")); 458 printf(" %s\n", _("All parameters are optional."));
508 printf (" %s\n", _("This plugin tests a PostgreSQL DBMS to determine whether it is active and")); 459 printf(" %s\n", _("This plugin tests a PostgreSQL DBMS to determine whether it is active and"));
509 printf (" %s\n", _("accepting queries. In its current operation, it simply connects to the")); 460 printf(" %s\n", _("accepting queries. In its current operation, it simply connects to the"));
510 printf (" %s\n", _("specified database, and then disconnects. If no database is specified, it")); 461 printf(" %s\n", _("specified database, and then disconnects. If no database is specified, it"));
511 printf (" %s\n", _("connects to the template1 database, which is present in every functioning")); 462 printf(" %s\n", _("connects to the template1 database, which is present in every functioning"));
512 printf (" %s\n\n", _("PostgreSQL DBMS.")); 463 printf(" %s\n\n", _("PostgreSQL DBMS."));
513 464
514 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"));
515 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."));
516 printf (" %s\n", _("Multiple SQL commands, separated by semicolon, are allowed but the result ")); 467 printf(" %s\n",
517 printf (" %s\n", _("of the last command is taken into account only. The value of the first")); 468 _("Multiple SQL commands, separated by semicolon, are allowed but the result "));
518 printf (" %s\n", _("column in the first row is used as the check result. If a second column is")); 469 printf(" %s\n", _("of the last command is taken into account only. The value of the first"));
519 printf (" %s\n", _("present in the result set, this is added to the plugin output with a")); 470 printf(" %s\n",
520 printf (" %s\n", _("prefix of \"Extra Info:\". This information can be displayed in the system")); 471 _("column in the first row is used as the check result. If a second column is"));
521 printf (" %s\n\n", _("executing the plugin.")); 472 printf(" %s\n", _("present in the result set, this is added to the plugin output with a"));
522 473 printf(" %s\n",
523 printf (" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual")); 474 _("prefix of \"Extra Info:\". This information can be displayed in the system"));
524 printf (" %s\n\n", _("for details about how to access internal statistics of the database server.")); 475 printf(" %s\n\n", _("executing the plugin."));
525 476
526 printf (" %s\n", _("For a list of available connection parameters which may be used with the -o")); 477 printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual"));
527 printf (" %s\n", _("command line option, see the documentation for PQconnectdb() in the chapter")); 478 printf(" %s\n\n",
528 printf (" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be")); 479 _("for details about how to access internal statistics of the database server."));
529 printf (" %s\n", _("used to specify a service name in pg_service.conf to be used for additional")); 480
530 printf (" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:")); 481 printf(" %s\n",
531 printf (" %s\n\n", _("-o 'sslmode=require'.")); 482 _("For a list of available connection parameters which may be used with the -o"));
532 483 printf(" %s\n",
533 printf (" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To")); 484 _("command line option, see the documentation for PQconnectdb() in the chapter"));
534 printf (" %s\n", _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP")); 485 printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be"));
535 printf (" %s\n\n", _("connections (start the postmaster with the -i option).")); 486 printf(" %s\n",
536 487 _("used to specify a service name in pg_service.conf to be used for additional"));
537 printf (" %s\n", _("Typically, the monitoring user (unless the --logname option is used) should be")); 488 printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:"));
538 printf (" %s\n", _("able to connect to the database without a password. The plugin can also send")); 489 printf(" %s\n\n", _("-o 'sslmode=require'."));
539 printf (" %s\n", _("a password, but no effort is made to obscure or encrypt the password.")); 490
540 491 printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To"));
541 printf (UT_SUPPORT); 492 printf(" %s\n",
493 _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP"));
494 printf(" %s\n\n", _("connections (start the postmaster with the -i option)."));
495
496 printf(" %s\n",
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"));
500 printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password."));
501
502 printf(UT_SUPPORT);
542} 503}
543 504
544 505void print_usage(void) {
545 506 printf("%s\n", _("Usage:"));
546void 507 printf("%s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n", progname);
547print_usage (void) 508 printf(" [-t <timeout>] [-d <database>] [-l <logname>] [-p <password>]\n"
548{ 509 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
549 printf ("%s\n", _("Usage:"));
550 printf ("%s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n", progname);
551 printf (" [-t <timeout>] [-d <database>] [-l <logname>] [-p <password>]\n"
552 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
553} 510}
554 511
555int 512mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thresholds *qthresholds,
556do_query (PGconn *conn, char *query) 513 char *query_warning, char *query_critical) {
557{ 514 if (verbose) {
558 PGresult *res; 515 printf("Executing SQL query \"%s\".\n", query);
559 516 }
560 char *val_str; 517 PGresult *res = PQexec(conn, query);
561 char *extra_info;
562 double value;
563
564 char *endptr = NULL;
565
566 int my_status = STATE_UNKNOWN;
567
568 if (verbose)
569 printf ("Executing SQL query \"%s\".\n", query);
570 res = PQexec (conn, query);
571 518
572 if (PGRES_TUPLES_OK != PQresultStatus (res)) { 519 if (PGRES_TUPLES_OK != PQresultStatus(res)) {
573 printf (_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), 520 printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"),
574 PQerrorMessage (conn)); 521 PQerrorMessage(conn));
575 return STATE_CRITICAL; 522 return STATE_CRITICAL;
576 } 523 }
577 524
578 if (PQntuples (res) < 1) { 525 if (PQntuples(res) < 1) {
579 printf ("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); 526 printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned"));
580 return STATE_WARNING; 527 return STATE_WARNING;
581 } 528 }
582 529
583 if (PQnfields (res) < 1) { 530 if (PQnfields(res) < 1) {
584 printf ("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); 531 printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned"));
585 return STATE_WARNING; 532 return STATE_WARNING;
586 } 533 }
587 534
588 val_str = PQgetvalue (res, 0, 0); 535 char *val_str = PQgetvalue(res, 0, 0);
589 if (! val_str) { 536 if (!val_str) {
590 printf ("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); 537 printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned"));
591 return STATE_CRITICAL; 538 return STATE_CRITICAL;
592 } 539 }
593 540
594 value = strtod (val_str, &endptr); 541 char *endptr = NULL;
595 if (verbose) 542 double value = strtod(val_str, &endptr);
596 printf ("Query result: %f\n", value); 543 if (verbose) {
544 printf("Query result: %f\n", value);
545 }
597 546
598 if (endptr == val_str) { 547 if (endptr == val_str) {
599 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);
600 return STATE_CRITICAL; 549 return STATE_CRITICAL;
601 } 550 }
602 else if ((endptr != NULL) && (*endptr != '\0')) {
603 if (verbose)
604 printf ("Garbage after value: %s.\n", endptr);
605 }
606 551
607 my_status = get_status (value, qthresholds); 552 if ((endptr != NULL) && (*endptr != '\0')) {
608 printf ("QUERY %s - ", 553 if (verbose) {
609 (my_status == STATE_OK) 554 printf("Garbage after value: %s.\n", endptr);
610 ? _("OK") 555 }
611 : (my_status == STATE_WARNING)
612 ? _("WARNING")
613 : (my_status == STATE_CRITICAL)
614 ? _("CRITICAL")
615 : _("UNKNOWN"));
616 if(pgqueryname) {
617 printf (_("%s returned %f"), pgqueryname, value);
618 } 556 }
619 else { 557
620 printf (_("'%s' returned %f"), query, value); 558 mp_state_enum my_status = get_status(value, qthresholds);
559 printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK")
560 : (my_status == STATE_WARNING) ? _("WARNING")
561 : (my_status == STATE_CRITICAL) ? _("CRITICAL")
562 : _("UNKNOWN"));
563 if (pgqueryname) {
564 printf(_("%s returned %f"), pgqueryname, value);
565 } else {
566 printf(_("'%s' returned %f"), query, value);
621 } 567 }
622 568
623 printf ("|query=%f;%s;%s;;\n", value, 569 printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "",
624 query_warning ? query_warning : "", 570 query_critical ? query_critical : "");
625 query_critical ? query_critical : ""); 571 if (PQnfields(res) > 1) {
626 if (PQnfields (res) > 1) { 572 char *extra_info = PQgetvalue(res, 0, 1);
627 extra_info = PQgetvalue (res, 0, 1);
628 if (extra_info != NULL) { 573 if (extra_info != NULL) {
629 printf ("Extra Info: %s\n", extra_info); 574 printf("Extra Info: %s\n", extra_info);
630 } 575 }
631 } 576 }
632 return my_status; 577 return my_status;
633} 578}
634
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 6e162e6a..61feb958 100644
--- a/plugins/check_ping.c
+++ b/plugins/check_ping.c
@@ -1,620 +1,673 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ping plugin 3 * Monitoring check_ping plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_ping plugin 10 * This file contains the check_ping plugin
11* 11 *
12* Use the ping program to check connection statistics for a remote host. 12 * Use the ping program to check connection statistics for a remote host.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_ping"; 31const char *progname = "check_ping";
32const char *copyright = "2000-2007"; 32const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "common.h"
36#include "netutils.h" 36#include "netutils.h"
37#include "popen.h" 37#include "popen.h"
38#include "utils.h" 38#include "utils.h"
39#include "check_ping.d/config.h"
40#include "../lib/states.h"
39 41
40#include <signal.h> 42#include <signal.h>
41 43
42#define WARN_DUPLICATES "DUPLICATES FOUND! " 44#define WARN_DUPLICATES "DUPLICATES FOUND! "
43#define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
44
45enum {
46 UNKNOWN_PACKET_LOSS = 200, /* 200% */
47 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */
48};
49
50int process_arguments (int, char **);
51int get_threshold (char *, float *, int *);
52int validate_arguments (void);
53int run_ping (const char *cmd, const char *addr);
54int error_scan (char buf[MAX_INPUT_BUFFER], const char *addr);
55void print_usage (void);
56void print_help (void);
57
58bool display_html = false;
59int wpl = UNKNOWN_PACKET_LOSS;
60int cpl = UNKNOWN_PACKET_LOSS;
61float wrta = UNKNOWN_TRIP_TIME;
62float crta = UNKNOWN_TRIP_TIME;
63char **addresses = NULL;
64int n_addresses = 0;
65int max_addr = 1;
66int max_packets = -1;
67int verbose = 0;
68
69float rta = UNKNOWN_TRIP_TIME;
70int pl = UNKNOWN_PACKET_LOSS;
71
72char *warn_text;
73
74
75
76int
77main (int argc, char **argv)
78{
79 char *cmd = NULL;
80 char *rawcmd = NULL;
81 int result = STATE_UNKNOWN;
82 int this_result = STATE_UNKNOWN;
83 int i;
84 45
85 setlocale (LC_ALL, ""); 46typedef struct {
86 setlocale (LC_NUMERIC, "C"); 47 int errorcode;
87 bindtextdomain (PACKAGE, LOCALEDIR); 48 check_ping_config config;
88 textdomain (PACKAGE); 49} check_ping_config_wrapper;
50static check_ping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
51static check_ping_config_wrapper validate_arguments(check_ping_config_wrapper /*config_wrapper*/);
52
53static int get_threshold(char * /*arg*/, double * /*trta*/, int * /*tpl*/);
54
55typedef struct {
56 mp_state_enum state;
57 double round_trip_average;
58 int packet_loss;
59} ping_result;
60static ping_result run_ping(const char *cmd, const char *addr, double /*crta*/);
61
62static mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr);
63static void print_help(void);
64void print_usage(void);
65
66static int verbose = 0;
89 67
90 addresses = malloc (sizeof(char*) * max_addr); 68static char *warn_text;
91 addresses[0] = NULL; 69
70int main(int argc, char **argv) {
71 setlocale(LC_ALL, "");
72 setlocale(LC_NUMERIC, "C");
73 bindtextdomain(PACKAGE, LOCALEDIR);
74 textdomain(PACKAGE);
92 75
93 /* Parse extra opts if any */ 76 /* Parse extra opts if any */
94 argv=np_extra_opts (&argc, argv, progname); 77 argv = np_extra_opts(&argc, argv, progname);
78
79 check_ping_config_wrapper tmp_config = process_arguments(argc, argv);
80 if (tmp_config.errorcode == ERROR) {
81 usage4(_("Could not parse arguments"));
82 }
95 83
96 if (process_arguments (argc, argv) == ERROR) 84 const check_ping_config config = tmp_config.config;
97 usage4 (_("Could not parse arguments"));
98 85
99 /* Set signal handling and alarm */ 86 /* Set signal handling and alarm */
100 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) { 87 if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
101 usage4 (_("Cannot catch SIGALRM")); 88 usage4(_("Cannot catch SIGALRM"));
102 } 89 }
103 90
104 /* If ./configure finds ping has timeout values, set plugin alarm slightly 91 /* If ./configure finds ping has timeout values, set plugin alarm slightly
105 * higher so that we can use response from command line ping */ 92 * higher so that we can use response from command line ping */
106#if defined(PING_PACKETS_FIRST) && defined(PING_HAS_TIMEOUT) 93#if defined(PING_PACKETS_FIRST) && defined(PING_HAS_TIMEOUT)
107 alarm (timeout_interval + 1); 94 alarm(timeout_interval + 1);
108#else 95#else
109 alarm (timeout_interval); 96 alarm(timeout_interval);
110#endif 97#endif
111 98
112 for (i = 0 ; i < n_addresses ; i++) { 99 int result = STATE_UNKNOWN;
113 100 char *rawcmd = NULL;
101 for (size_t i = 0; i < config.n_addresses; i++) {
114#ifdef PING6_COMMAND 102#ifdef PING6_COMMAND
115 if (address_family != AF_INET && is_inet6_addr(addresses[i])) 103 if (address_family != AF_INET && is_inet6_addr(config.addresses[i])) {
116 rawcmd = strdup(PING6_COMMAND); 104 rawcmd = strdup(PING6_COMMAND);
117 else 105 } else {
118 rawcmd = strdup(PING_COMMAND); 106 rawcmd = strdup(PING_COMMAND);
107 }
119#else 108#else
120 rawcmd = strdup(PING_COMMAND); 109 rawcmd = strdup(PING_COMMAND);
121#endif 110#endif
122 111
112 char *cmd = NULL;
113
123 /* does the host address of number of packets argument come first? */ 114 /* does the host address of number of packets argument come first? */
124#ifdef PING_PACKETS_FIRST 115#ifdef PING_PACKETS_FIRST
125# ifdef PING_HAS_TIMEOUT 116# ifdef PING_HAS_TIMEOUT
126 xasprintf (&cmd, rawcmd, timeout_interval, max_packets, addresses[i]); 117 xasprintf(&cmd, rawcmd, timeout_interval, config.max_packets, config.addresses[i]);
127# else 118# else
128 xasprintf (&cmd, rawcmd, max_packets, addresses[i]); 119 xasprintf(&cmd, rawcmd, config.max_packets, config.addresses[i]);
129# endif 120# endif
130#else 121#else
131 xasprintf (&cmd, rawcmd, addresses[i], max_packets); 122 xasprintf(&cmd, rawcmd, config.addresses[i], config.max_packets);
132#endif 123#endif
133 124
134 if (verbose >= 2) 125 if (verbose >= 2) {
135 printf ("CMD: %s\n", cmd); 126 printf("CMD: %s\n", cmd);
127 }
136 128
137 /* run the command */ 129 /* run the command */
138 this_result = run_ping (cmd, addresses[i]);
139 130
140 if (pl == UNKNOWN_PACKET_LOSS || rta < 0.0) { 131 ping_result pinged = run_ping(cmd, config.addresses[i], config.crta);
141 printf ("%s\n", cmd); 132
142 die (STATE_UNKNOWN, 133 if (pinged.packet_loss == UNKNOWN_PACKET_LOSS || pinged.round_trip_average < 0.0) {
143 _("CRITICAL - Could not interpret output from ping command\n")); 134 printf("%s\n", cmd);
135 die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n"));
136 }
137
138 if (pinged.packet_loss >= config.cpl || pinged.round_trip_average >= config.crta ||
139 pinged.round_trip_average < 0) {
140 pinged.state = STATE_CRITICAL;
141 } else if (pinged.packet_loss >= config.wpl || pinged.round_trip_average >= config.wrta) {
142 pinged.state = STATE_WARNING;
143 } else if (pinged.packet_loss >= 0 && pinged.round_trip_average >= 0) {
144 pinged.state = max_state(STATE_OK, pinged.state);
145 }
146
147 if (config.n_addresses > 1 && pinged.state != STATE_UNKNOWN) {
148 die(STATE_OK, "%s is alive\n", config.addresses[i]);
144 } 149 }
145 150
146 if (pl >= cpl || rta >= crta || rta < 0) 151 if (config.display_html) {
147 this_result = STATE_CRITICAL; 152 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, config.addresses[i]);
148 else if (pl >= wpl || rta >= wrta) 153 }
149 this_result = STATE_WARNING; 154 if (pinged.packet_loss == 100) {
150 else if (pl >= 0 && rta >= 0) 155 printf(_("PING %s - %sPacket loss = %d%%"), state_text(pinged.state), warn_text,
151 this_result = max_state (STATE_OK, this_result); 156 pinged.packet_loss);
152 157 } else {
153 if (n_addresses > 1 && this_result != STATE_UNKNOWN) 158 printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(pinged.state),
154 die (STATE_OK, "%s is alive\n", addresses[i]); 159 warn_text, pinged.packet_loss, pinged.round_trip_average);
155 160 }
156 if (display_html == true) 161 if (config.display_html) {
157 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]); 162 printf("</A>");
158 if (pl == 100) 163 }
159 printf (_("PING %s - %sPacket loss = %d%%"), state_text (this_result), warn_text,
160 pl);
161 else
162 printf (_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"),
163 state_text (this_result), warn_text, pl, rta);
164 if (display_html == true)
165 printf ("</A>");
166 164
167 /* Print performance data */ 165 /* Print performance data */
168 if (pl != 100) { 166 if (pinged.packet_loss != 100) {
169 printf("|%s", fperfdata ("rta", (double) rta, "ms", 167 printf("|%s",
170 wrta>0?true:false, wrta, 168 fperfdata("rta", pinged.round_trip_average, "ms", (bool)(config.wrta > 0),
171 crta>0?true:false, crta, 169 config.wrta, (bool)(config.crta > 0), config.crta, true, 0, false, 0));
172 true, 0, false, 0));
173 } else { 170 } else {
174 printf("| rta=U;%f;%f;;", wrta, crta); 171 printf("| rta=U;%f;%f;;", config.wrta, config.crta);
175 } 172 }
176 printf(" %s\n", perfdata ("pl", (long) pl, "%",
177 wpl>0?true:false, wpl,
178 cpl>0?true:false, cpl,
179 true, 0, false, 0));
180 173
181 if (verbose >= 2) 174 printf(" %s\n",
182 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl); 175 perfdata("pl", (long)pinged.packet_loss, "%", (bool)(config.wpl > 0), config.wpl,
176 (bool)(config.cpl > 0), config.cpl, true, 0, false, 0));
177
178 if (verbose >= 2) {
179 printf("%f:%d%% %f:%d%%\n", config.wrta, config.wpl, config.crta, config.cpl);
180 }
183 181
184 result = max_state (result, this_result); 182 result = max_state(result, pinged.state);
185 free (rawcmd); 183 free(rawcmd);
186 free (cmd); 184 free(cmd);
187 } 185 }
188 186
189 return result; 187 return result;
190} 188}
191 189
192
193
194/* process command-line arguments */ 190/* process command-line arguments */
195int 191check_ping_config_wrapper process_arguments(int argc, char **argv) {
196process_arguments (int argc, char **argv) 192 static struct option longopts[] = {STD_LONG_OPTS,
197{ 193 {"packets", required_argument, 0, 'p'},
198 int c = 1; 194 {"nohtml", no_argument, 0, 'n'},
199 char *ptr; 195 {"link", no_argument, 0, 'L'},
200 196 {"use-ipv4", no_argument, 0, '4'},
201 int option = 0; 197 {"use-ipv6", no_argument, 0, '6'},
202 static struct option longopts[] = { 198 {0, 0, 0, 0}};
203 STD_LONG_OPTS, 199
204 {"packets", required_argument, 0, 'p'}, 200 check_ping_config_wrapper result = {
205 {"nohtml", no_argument, 0, 'n'}, 201 .errorcode = OK,
206 {"link", no_argument, 0, 'L'}, 202 .config = check_ping_config_init(),
207 {"use-ipv4", no_argument, 0, '4'},
208 {"use-ipv6", no_argument, 0, '6'},
209 {0, 0, 0, 0}
210 }; 203 };
211 204
212 if (argc < 2) 205 if (argc < 2) {
213 return ERROR; 206 result.errorcode = ERROR;
207 return result;
208 }
214 209
215 for (c = 1; c < argc; c++) { 210 for (int index = 1; index < argc; index++) {
216 if (strcmp ("-to", argv[c]) == 0) 211 if (strcmp("-to", argv[index]) == 0) {
217 strcpy (argv[c], "-t"); 212 strcpy(argv[index], "-t");
218 if (strcmp ("-nohtml", argv[c]) == 0) 213 }
219 strcpy (argv[c], "-n"); 214 if (strcmp("-nohtml", argv[index]) == 0) {
215 strcpy(argv[index], "-n");
216 }
220 } 217 }
221 218
222 while (1) { 219 int option = 0;
223 c = getopt_long (argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option); 220 size_t max_addr = MAX_ADDR_START;
221 while (true) {
222 int option_index = getopt_long(argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option);
224 223
225 if (c == -1 || c == EOF) 224 if (option_index == -1 || option_index == EOF) {
226 break; 225 break;
226 }
227 227
228 switch (c) { 228 switch (option_index) {
229 case '?': /* usage */ 229 case '?': /* usage */
230 usage5 (); 230 usage5();
231 case 'h': /* help */ 231 case 'h': /* help */
232 print_help (); 232 print_help();
233 exit (STATE_UNKNOWN); 233 exit(STATE_UNKNOWN);
234 break; 234 break;
235 case 'V': /* version */ 235 case 'V': /* version */
236 print_revision (progname, NP_VERSION); 236 print_revision(progname, NP_VERSION);
237 exit (STATE_UNKNOWN); 237 exit(STATE_UNKNOWN);
238 break; 238 break;
239 case 't': /* timeout period */ 239 case 't': /* timeout period */
240 timeout_interval = atoi (optarg); 240 timeout_interval = atoi(optarg);
241 break; 241 break;
242 case 'v': /* verbose mode */ 242 case 'v': /* verbose mode */
243 verbose++; 243 verbose++;
244 break; 244 break;
245 case '4': /* IPv4 only */ 245 case '4': /* IPv4 only */
246 address_family = AF_INET; 246 address_family = AF_INET;
247 break; 247 break;
248 case '6': /* IPv6 only */ 248 case '6': /* IPv6 only */
249#ifdef USE_IPV6 249#ifdef USE_IPV6
250 address_family = AF_INET6; 250 address_family = AF_INET6;
251#else 251#else
252 usage (_("IPv6 support not available\n")); 252 usage(_("IPv6 support not available\n"));
253#endif 253#endif
254 break; 254 break;
255 case 'H': /* hostname */ 255 case 'H': /* hostname */ {
256 ptr=optarg; 256 char *ptr = optarg;
257 while (1) { 257 while (true) {
258 n_addresses++; 258 result.config.n_addresses++;
259 if (n_addresses > max_addr) { 259 if (result.config.n_addresses > max_addr) {
260 max_addr *= 2; 260 max_addr *= 2;
261 addresses = realloc (addresses, sizeof(char*) * max_addr); 261 result.config.addresses =
262 if (addresses == NULL) 262 realloc(result.config.addresses, sizeof(char *) * max_addr);
263 die (STATE_UNKNOWN, _("Could not realloc() addresses\n")); 263 if (result.config.addresses == NULL) {
264 die(STATE_UNKNOWN, _("Could not realloc() addresses\n"));
265 }
264 } 266 }
265 addresses[n_addresses-1] = ptr; 267 result.config.addresses[result.config.n_addresses - 1] = ptr;
266 if ((ptr = index (ptr, ','))) { 268 if ((ptr = index(ptr, ','))) {
267 strcpy (ptr, ""); 269 strcpy(ptr, "");
268 ptr += sizeof(char); 270 ptr += sizeof(char);
269 } else { 271 } else {
270 break; 272 break;
271 } 273 }
272 } 274 }
275 } break;
276 case 'p': /* number of packets to send */
277 if (is_intnonneg(optarg)) {
278 result.config.max_packets = atoi(optarg);
279 } else {
280 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg);
281 }
273 break; 282 break;
274 case 'p': /* number of packets to send */ 283 case 'n': /* no HTML */
275 if (is_intnonneg (optarg)) 284 result.config.display_html = false;
276 max_packets = atoi (optarg);
277 else
278 usage2 (_("<max_packets> (%s) must be a non-negative number\n"), optarg);
279 break;
280 case 'n': /* no HTML */
281 display_html = false;
282 break; 285 break;
283 case 'L': /* show HTML */ 286 case 'L': /* show HTML */
284 display_html = true; 287 result.config.display_html = true;
285 break; 288 break;
286 case 'c': 289 case 'c':
287 get_threshold (optarg, &crta, &cpl); 290 get_threshold(optarg, &result.config.crta, &result.config.cpl);
288 break; 291 break;
289 case 'w': 292 case 'w':
290 get_threshold (optarg, &wrta, &wpl); 293 get_threshold(optarg, &result.config.wrta, &result.config.wpl);
291 break; 294 break;
292 } 295 }
293 } 296 }
294 297
295 c = optind; 298 int arg_counter = optind;
296 if (c == argc) 299 if (arg_counter == argc) {
297 return validate_arguments (); 300 return validate_arguments(result);
301 }
298 302
299 if (addresses[0] == NULL) { 303 if (result.config.addresses[0] == NULL) {
300 if (!is_host (argv[c])) { 304 if (!is_host(argv[arg_counter])) {
301 usage2 (_("Invalid hostname/address"), argv[c]); 305 usage2(_("Invalid hostname/address"), argv[arg_counter]);
302 } else { 306 } else {
303 addresses[0] = argv[c++]; 307 result.config.addresses[0] = argv[arg_counter++];
304 n_addresses++; 308 result.config.n_addresses++;
305 if (c == argc) 309 if (arg_counter == argc) {
306 return validate_arguments (); 310 return validate_arguments(result);
311 }
307 } 312 }
308 } 313 }
309 314
310 if (wpl == UNKNOWN_PACKET_LOSS) { 315 if (result.config.wpl == UNKNOWN_PACKET_LOSS) {
311 if (!is_intpercent (argv[c])) { 316 if (!is_intpercent(argv[arg_counter])) {
312 printf (_("<wpl> (%s) must be an integer percentage\n"), argv[c]); 317 printf(_("<wpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
313 return ERROR; 318 result.errorcode = ERROR;
314 } else { 319 return result;
315 wpl = atoi (argv[c++]); 320 }
316 if (c == argc) 321 result.config.wpl = atoi(argv[arg_counter++]);
317 return validate_arguments (); 322 if (arg_counter == argc) {
323 return validate_arguments(result);
318 } 324 }
319 } 325 }
320 326
321 if (cpl == UNKNOWN_PACKET_LOSS) { 327 if (result.config.cpl == UNKNOWN_PACKET_LOSS) {
322 if (!is_intpercent (argv[c])) { 328 if (!is_intpercent(argv[arg_counter])) {
323 printf (_("<cpl> (%s) must be an integer percentage\n"), argv[c]); 329 printf(_("<cpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
324 return ERROR; 330 result.errorcode = ERROR;
325 } else { 331 return result;
326 cpl = atoi (argv[c++]); 332 }
327 if (c == argc) 333 result.config.cpl = atoi(argv[arg_counter++]);
328 return validate_arguments (); 334 if (arg_counter == argc) {
335 return validate_arguments(result);
329 } 336 }
330 } 337 }
331 338
332 if (wrta < 0.0) { 339 if (result.config.wrta < 0.0) {
333 if (is_negative (argv[c])) { 340 if (is_negative(argv[arg_counter])) {
334 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]);
335 return ERROR; 342 result.errorcode = ERROR;
336 } else { 343 return result;
337 wrta = atof (argv[c++]); 344 }
338 if (c == argc) 345 result.config.wrta = atof(argv[arg_counter++]);
339 return validate_arguments (); 346 if (arg_counter == argc) {
347 return validate_arguments(result);
340 } 348 }
341 } 349 }
342 350
343 if (crta < 0.0) { 351 if (result.config.crta < 0.0) {
344 if (is_negative (argv[c])) { 352 if (is_negative(argv[arg_counter])) {
345 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]);
346 return ERROR; 354 result.errorcode = ERROR;
347 } else { 355 return result;
348 crta = atof (argv[c++]); 356 }
349 if (c == argc) 357 result.config.crta = atof(argv[arg_counter++]);
350 return validate_arguments (); 358 if (arg_counter == argc) {
359 return validate_arguments(result);
351 } 360 }
352 } 361 }
353 362
354 if (max_packets == -1) { 363 if (result.config.max_packets == -1) {
355 if (is_intnonneg (argv[c])) { 364 if (is_intnonneg(argv[arg_counter])) {
356 max_packets = atoi (argv[c++]); 365 result.config.max_packets = atoi(argv[arg_counter++]);
357 } else { 366 } else {
358 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]);
359 return ERROR; 368 result.errorcode = ERROR;
369 return result;
360 } 370 }
361 } 371 }
362 372
363 return validate_arguments (); 373 return validate_arguments(result);
364} 374}
365 375
366 376int get_threshold(char *arg, double *trta, int *tpl) {
367 377 if (is_intnonneg(arg) && sscanf(arg, "%lf", trta) == 1) {
368int
369get_threshold (char *arg, float *trta, int *tpl)
370{
371 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
372 return OK; 378 return OK;
373 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) {
374 return OK; 382 return OK;
375 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 383 }
384
385 if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) {
376 return OK; 386 return OK;
387 }
377 388
378 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);
379 return STATE_UNKNOWN; 390 return STATE_UNKNOWN;
380} 391}
381 392
382 393check_ping_config_wrapper validate_arguments(check_ping_config_wrapper config_wrapper) {
383 394 if (config_wrapper.config.wrta < 0.0) {
384int 395 printf(_("<wrta> was not set\n"));
385validate_arguments () 396 config_wrapper.errorcode = ERROR;
386{ 397 return config_wrapper;
387 float max_seconds;
388 int i;
389
390 if (wrta < 0.0) {
391 printf (_("<wrta> was not set\n"));
392 return ERROR;
393 } 398 }
394 else if (crta < 0.0) { 399
395 printf (_("<crta> was not set\n")); 400 if (config_wrapper.config.crta < 0.0) {
396 return ERROR; 401 printf(_("<crta> was not set\n"));
402 config_wrapper.errorcode = ERROR;
403 return config_wrapper;
397 } 404 }
398 else if (wpl == UNKNOWN_PACKET_LOSS) { 405
399 printf (_("<wpl> was not set\n")); 406 if (config_wrapper.config.wpl == UNKNOWN_PACKET_LOSS) {
400 return ERROR; 407 printf(_("<wpl> was not set\n"));
408 config_wrapper.errorcode = ERROR;
409 return config_wrapper;
401 } 410 }
402 else if (cpl == UNKNOWN_PACKET_LOSS) { 411
403 printf (_("<cpl> was not set\n")); 412 if (config_wrapper.config.cpl == UNKNOWN_PACKET_LOSS) {
404 return ERROR; 413 printf(_("<cpl> was not set\n"));
414 config_wrapper.errorcode = ERROR;
415 return config_wrapper;
405 } 416 }
406 else if (wrta > crta) { 417
407 printf (_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta); 418 if (config_wrapper.config.wrta > config_wrapper.config.crta) {
408 return ERROR; 419 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), config_wrapper.config.wrta,
420 config_wrapper.config.crta);
421 config_wrapper.errorcode = ERROR;
422 return config_wrapper;
409 } 423 }
410 else if (wpl > cpl) { 424
411 printf (_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl); 425 if (config_wrapper.config.wpl > config_wrapper.config.cpl) {
412 return ERROR; 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;
413 } 430 }
414 431
415 if (max_packets == -1) 432 if (config_wrapper.config.max_packets == -1) {
416 max_packets = DEFAULT_MAX_PACKETS; 433 config_wrapper.config.max_packets = DEFAULT_MAX_PACKETS;
434 }
417 435
418 max_seconds = crta / 1000.0 * max_packets + max_packets; 436 double max_seconds = (config_wrapper.config.crta / 1000.0 * config_wrapper.config.max_packets) +
419 if (max_seconds > timeout_interval) 437 config_wrapper.config.max_packets;
420 timeout_interval = (int)max_seconds; 438 if (max_seconds > timeout_interval) {
439 timeout_interval = (unsigned int)max_seconds;
440 }
421 441
422 for (i=0; i<n_addresses; i++) { 442 for (size_t i = 0; i < config_wrapper.config.n_addresses; i++) {
423 if (!is_host(addresses[i])) 443 if (!is_host(config_wrapper.config.addresses[i])) {
424 usage2 (_("Invalid hostname/address"), addresses[i]); 444 usage2(_("Invalid hostname/address"), config_wrapper.config.addresses[i]);
445 }
425 } 446 }
426 447
427 if (n_addresses == 0) { 448 if (config_wrapper.config.n_addresses == 0) {
428 usage (_("You must specify a server address or host name")); 449 usage(_("You must specify a server address or host name"));
429 } 450 }
430 451
431 return OK; 452 return config_wrapper;
432} 453}
433 454
455ping_result run_ping(const char *cmd, const char *addr, double crta) {
456 if ((child_process = spopen(cmd)) == NULL) {
457 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
458 }
434 459
460 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
461 if (child_stderr == NULL) {
462 printf(_("Cannot open stderr for %s\n"), cmd);
463 }
435 464
436int
437run_ping (const char *cmd, const char *addr)
438{
439 char buf[MAX_INPUT_BUFFER]; 465 char buf[MAX_INPUT_BUFFER];
440 int result = STATE_UNKNOWN; 466 ping_result result = {
441 int match; 467 .state = STATE_UNKNOWN,
442 468 .packet_loss = UNKNOWN_PACKET_LOSS,
443 if ((child_process = spopen (cmd)) == NULL) 469 .round_trip_average = UNKNOWN_TRIP_TIME,
444 die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 470 };
445
446 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
447 if (child_stderr == NULL)
448 printf (_("Cannot open stderr for %s\n"), cmd);
449
450 while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) {
451 471
452 if (verbose >= 3) 472 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) {
473 if (verbose >= 3) {
453 printf("Output: %s", buf); 474 printf("Output: %s", buf);
475 }
454 476
455 result = max_state (result, error_scan (buf, addr)); 477 result.state = max_state(result.state, error_scan(buf, addr));
456 478
457 /* get the percent loss statistics */ 479 /* get the percent loss statistics */
458 match = 0; 480 int match = 0;
459 if((sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n",&pl,&match) && match) || 481 if ((sscanf(
460 (sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss%n",&pl,&match) && match) || 482 buf,
461 (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",
462 (sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% packet loss%n",&pl,&match) && match) || 484 &result.packet_loss, &match) == 1 &&
463 (sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% loss, time%n",&pl,&match) && match) || 485 match) ||
464 (sscanf(buf,"%*d packets transmitted, %*d received, %d%% loss, time%n",&pl,&match) && match) || 486 (sscanf(buf,
465 (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 "
466 (sscanf(buf,"%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n",&pl,&match) && match) || 488 "loss%n",
467 (sscanf(buf,"%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n",&pl,&match) && match) || 489 &result.packet_loss, &match) == 1 &&
468 (sscanf(buf,"%*[^(](%d%% %*[^)])%n",&pl,&match) && match) 490 match) ||
469 ) 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)) {
470 continue; 514 continue;
515 }
471 516
472 /* get the round trip average */ 517 /* get the round trip average */
473 else 518 if ((sscanf(buf, "round-trip min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average,
474 if((sscanf(buf,"round-trip min/avg/max = %*f/%f/%*f%n",&rta,&match) && match) || 519 &match) == 1 &&
475 (sscanf(buf,"round-trip min/avg/max/mdev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 520 match) ||
476 (sscanf(buf,"round-trip min/avg/max/sdev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 521 (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%lf/%*f/%*f%n",
477 (sscanf(buf,"round-trip min/avg/max/stddev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 522 &result.round_trip_average, &match) == 1 &&
478 (sscanf(buf,"round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 523 match) ||
479 (sscanf(buf,"round-trip (ms) min/avg/max = %*f/%f/%*f%n",&rta,&match) && match) || 524 (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%lf/%*f/%*f%n",
480 (sscanf(buf,"round-trip (ms) min/avg/max/stddev = %*f/%f/%*f/%*f%n",&rta,&match) && match) || 525 &result.round_trip_average, &match) == 1 &&
481 (sscanf(buf,"rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms%n",&rta,&match) && match) || 526 match) ||
482 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %fms%n", &rta, &match) && match) 527 (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%lf/%*f/%*f%n",
483 ) 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)) {
484 continue; 545 continue;
546 }
485 } 547 }
486 548
487 /* 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 */
488 if (pl == 100) 550 if (result.packet_loss == 100) {
489 rta = crta; 551 result.round_trip_average = crta;
552 }
490 553
491 /* check stderr, setting at least WARNING if there is output here */ 554 /* check stderr, setting at least WARNING if there is output here */
492 /* Add warning into warn_text */ 555 /* Add warning into warn_text */
493 while (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr)) { 556 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
494 if ( 557 if (!strstr(buf, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") &&
495 ! strstr(buf,"WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") 558 !strstr(buf, "Warning: time of day goes back")
496 && ! strstr(buf,"Warning: time of day goes back")
497 559
498 ) { 560 ) {
499 if (verbose >= 3) { 561 if (verbose >= 3) {
500 printf("Got stderr: %s", buf); 562 printf("Got stderr: %s", buf);
501 } 563 }
502 if ((result=error_scan(buf, addr)) == STATE_OK) { 564 if ((result.state = error_scan(buf, addr)) == STATE_OK) {
503 result = STATE_WARNING; 565 result.state = STATE_WARNING;
504 if (warn_text == NULL) { 566 if (warn_text == NULL) {
505 warn_text = strdup(_("System call sent warnings to stderr ")); 567 warn_text = strdup(_("System call sent warnings to stderr "));
506 } else { 568 } else {
507 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 "));
508 } 571 }
509 } 572 }
510 } 573 }
511 } 574 }
512 575
513 (void) fclose (child_stderr); 576 (void)fclose(child_stderr);
514 577
578 spclose(child_process);
515 579
516 spclose (child_process); 580 if (warn_text == NULL) {
517
518 if (warn_text == NULL)
519 warn_text = strdup(""); 581 warn_text = strdup("");
582 }
520 583
521 return result; 584 return result;
522} 585}
523 586
587mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) {
588 if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") ||
589 strstr(buf, "No route")) {
590 die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr);
591 } else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) {
592 die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr);
593 } else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) {
594 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr);
595 } else if (strstr(buf, "Destination Protocol Unreachable")) {
596 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr);
597 } else if (strstr(buf, "Destination Net Prohibited")) {
598 die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr);
599 } else if (strstr(buf, "Destination Host Prohibited")) {
600 die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr);
601 } else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) {
602 die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr);
603 } else if (strstr(buf, "unknown host")) {
604 die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr);
605 } else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) {
606 die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr);
607 } else if (strstr(buf, "Destination unreachable: ")) {
608 die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr);
609 }
524 610
611 if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) {
612 if (warn_text == NULL) {
613 warn_text = strdup(_(WARN_DUPLICATES));
614 } else if (!strstr(warn_text, _(WARN_DUPLICATES)) &&
615 xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) {
616 die(STATE_UNKNOWN, _("Unable to realloc warn_text\n"));
617 }
618 return STATE_WARNING;
619 }
525 620
526int 621 return STATE_OK;
527error_scan (char buf[MAX_INPUT_BUFFER], const char *addr)
528{
529 if (strstr (buf, "Network is unreachable") ||
530 strstr (buf, "Destination Net Unreachable") ||
531 strstr (buf, "No route")
532 )
533 die (STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr);
534 else if (strstr (buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable"))
535 die (STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr);
536 else if (strstr (buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable"))
537 die (STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr);
538 else if (strstr (buf, "Destination Protocol Unreachable"))
539 die (STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr);
540 else if (strstr (buf, "Destination Net Prohibited"))
541 die (STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr);
542 else if (strstr (buf, "Destination Host Prohibited"))
543 die (STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr);
544 else if (strstr (buf, "Packet filtered") || strstr(buf, "Administratively prohibited"))
545 die (STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr);
546 else if (strstr (buf, "unknown host" ))
547 die (STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr);
548 else if (strstr (buf, "Time to live exceeded") || strstr(buf, "Time exceeded"))
549 die (STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr);
550 else if (strstr (buf, "Destination unreachable: "))
551 die (STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr);
552
553 if (strstr (buf, "(DUP!)") || strstr (buf, "DUPLICATES FOUND")) {
554 if (warn_text == NULL)
555 warn_text = strdup (_(WARN_DUPLICATES));
556 else if (! strstr (warn_text, _(WARN_DUPLICATES)) &&
557 xasprintf (&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1)
558 die (STATE_UNKNOWN, _("Unable to realloc warn_text\n"));
559 return (STATE_WARNING);
560 }
561
562 return (STATE_OK);
563} 622}
564 623
624void print_help(void) {
625 print_revision(progname, NP_VERSION);
565 626
627 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
628 printf(COPYRIGHT, copyright, email);
566 629
567void 630 printf(_("Use ping to check connection statistics for a remote host."));
568print_help (void)
569{
570 print_revision (progname, NP_VERSION);
571
572 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
573 printf (COPYRIGHT, copyright, email);
574
575 printf (_("Use ping to check connection statistics for a remote host."));
576 631
577 printf ("\n\n"); 632 printf("\n\n");
578 633
579 print_usage (); 634 print_usage();
580 635
581 printf (UT_HELP_VRSN); 636 printf(UT_HELP_VRSN);
582 printf (UT_EXTRA_OPTS); 637 printf(UT_EXTRA_OPTS);
583 638
584 printf (UT_IPv46); 639 printf(UT_IPv46);
585 640
586 printf (" %s\n", "-H, --hostname=HOST"); 641 printf(" %s\n", "-H, --hostname=HOST");
587 printf (" %s\n", _("host to ping")); 642 printf(" %s\n", _("host to ping"));
588 printf (" %s\n", "-w, --warning=THRESHOLD"); 643 printf(" %s\n", "-w, --warning=THRESHOLD");
589 printf (" %s\n", _("warning threshold pair")); 644 printf(" %s\n", _("warning threshold pair"));
590 printf (" %s\n", "-c, --critical=THRESHOLD"); 645 printf(" %s\n", "-c, --critical=THRESHOLD");
591 printf (" %s\n", _("critical threshold pair")); 646 printf(" %s\n", _("critical threshold pair"));
592 printf (" %s\n", "-p, --packets=INTEGER"); 647 printf(" %s\n", "-p, --packets=INTEGER");
593 printf (" %s ", _("number of ICMP ECHO packets to send")); 648 printf(" %s ", _("number of ICMP ECHO packets to send"));
594 printf (_("(Default: %d)\n"), DEFAULT_MAX_PACKETS); 649 printf(_("(Default: %d)\n"), DEFAULT_MAX_PACKETS);
595 printf (" %s\n", "-L, --link"); 650 printf(" %s\n", "-L, --link");
596 printf (" %s\n", _("show HTML in the plugin output (obsoleted by urlize)")); 651 printf(" %s\n", _("show HTML in the plugin output (obsoleted by urlize)"));
597 652
598 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 653 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
599 654
600 printf ("\n"); 655 printf("\n");
601 printf ("%s\n", _("THRESHOLD is <rta>,<pl>% where <rta> is the round trip average travel")); 656 printf("%s\n", _("THRESHOLD is <rta>,<pl>% where <rta> is the round trip average travel"));
602 printf ("%s\n", _("time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the")); 657 printf("%s\n", _("time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the"));
603 printf ("%s\n", _("percentage of packet loss to trigger an alarm state.")); 658 printf("%s\n", _("percentage of packet loss to trigger an alarm state."));
604 659
605 printf ("\n"); 660 printf("\n");
606 printf ("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss")); 661 printf("%s\n",
607 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"));
608 printf ("%s\n", _("linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in")); 663 printf("%s\n",
609 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."));
610 665
611 printf (UT_SUPPORT); 666 printf(UT_SUPPORT);
612} 667}
613 668
614void 669void print_usage(void) {
615print_usage (void) 670 printf("%s\n", _("Usage:"));
616{ 671 printf("%s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n", progname);
617 printf ("%s\n", _("Usage:")); 672 printf(" [-p packets] [-t timeout] [-4|-6]\n");
618 printf ("%s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n", progname);
619 printf (" [-p packets] [-t timeout] [-4|-6]\n");
620} 673}
diff --git a/plugins/check_ping.d/config.h b/plugins/check_ping.d/config.h
new file mode 100644
index 00000000..eb2735a7
--- /dev/null
+++ b/plugins/check_ping.d/config.h
@@ -0,0 +1,46 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7enum {
8 UNKNOWN_PACKET_LOSS = 200, /* 200% */
9 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */
10};
11
12#define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
13
14#define MAX_ADDR_START 1
15
16typedef struct {
17 bool display_html;
18 int max_packets;
19
20 char **addresses;
21 size_t n_addresses;
22
23 int wpl;
24 int cpl;
25 double wrta;
26 double crta;
27} check_ping_config;
28
29check_ping_config check_ping_config_init() {
30 check_ping_config tmp = {
31 .display_html = false,
32 .max_packets = -1,
33
34 .addresses = NULL,
35 .n_addresses = 0,
36
37 .wpl = UNKNOWN_PACKET_LOSS,
38 .cpl = UNKNOWN_PACKET_LOSS,
39 .wrta = UNKNOWN_TRIP_TIME,
40 .crta = UNKNOWN_TRIP_TIME,
41 };
42
43 tmp.addresses = calloc(MAX_ADDR_START, sizeof(char *));
44 tmp.addresses[0] = NULL;
45 return tmp;
46}
diff --git a/plugins/check_procs.c b/plugins/check_procs.c
index 1fcbd981..ae6e9c23 100644
--- a/plugins/check_procs.c
+++ b/plugins/check_procs.c
@@ -1,357 +1,339 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_procs plugin 3 * Monitoring check_procs plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_procs plugin 10 * This file contains the check_procs plugin
11* 11 *
12* Checks all processes and generates WARNING or CRITICAL states if the 12 * Checks all processes and generates WARNING or CRITICAL states if the
13* specified metric is outside the required threshold ranges. The metric 13 * specified metric is outside the required threshold ranges. The metric
14* defaults to number of processes. Search filters can be applied to limit 14 * defaults to number of processes. Search filters can be applied to limit
15* the processes to check. 15 * the processes to check.
16* 16 *
17* The parent process, check_procs itself and any child process of 17 * The parent process, check_procs itself and any child process of
18* check_procs (ps) are excluded from any checks to prevent false positives. 18 * check_procs (ps) are excluded from any checks to prevent false positives.
19* 19 *
20* 20 *
21* This program is free software: you can redistribute it and/or modify 21 * This program is free software: you can redistribute it and/or modify
22* it under the terms of the GNU General Public License as published by 22 * it under the terms of the GNU General Public License as published by
23* the Free Software Foundation, either version 3 of the License, or 23 * the Free Software Foundation, either version 3 of the License, or
24* (at your option) any later version. 24 * (at your option) any later version.
25* 25 *
26* This program is distributed in the hope that it will be useful, 26 * This program is distributed in the hope that it will be useful,
27* but WITHOUT ANY WARRANTY; without even the implied warranty of 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29* GNU General Public License for more details. 29 * GNU General Public License for more details.
30* 30 *
31* You should have received a copy of the GNU General Public License 31 * You should have received a copy of the GNU General Public License
32* along with this program. If not, see <http://www.gnu.org/licenses/>. 32 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33* 33 *
34* 34 *
35*****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_procs"; 37const char *progname = "check_procs";
38const char *program_name = "check_procs"; /* Required for coreutils libs */ 38const char *program_name = "check_procs"; /* Required for coreutils libs */
39const char *copyright = "2000-2008"; 39const char *copyright = "2000-2024";
40const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
41 41
42#include "common.h" 42#include "common.h"
43#include "utils.h" 43#include "utils.h"
44#include "utils_cmd.h" 44#include "utils_cmd.h"
45#include "regex.h" 45#include "regex.h"
46#include "states.h"
47#include "check_procs.d/config.h"
46 48
47#include <pwd.h> 49#include <pwd.h>
48#include <errno.h> 50#include <errno.h>
49 51
50#ifdef HAVE_SYS_STAT_H 52#ifdef HAVE_SYS_STAT_H
51#include <sys/stat.h> 53# include <sys/stat.h>
52#endif 54#endif
53 55
54int process_arguments (int, char **); 56typedef struct {
55int validate_arguments (void); 57 int errorcode;
56int convert_to_seconds (char *); 58 check_procs_config config;
57void print_help (void); 59} check_procs_config_wrapper;
58void print_usage (void); 60static check_procs_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
59 61static check_procs_config_wrapper validate_arguments(check_procs_config_wrapper /*config_wrapper*/);
60char *warning_range = NULL; 62
61char *critical_range = NULL; 63static int convert_to_seconds(char * /*etime*/, enum metric /*metric*/);
62thresholds *procs_thresholds = NULL; 64static void print_help(void);
63 65void print_usage(void);
64int options = 0; /* bitmask of filter criteria to test against */ 66
65#define ALL 1 67#define ALL 1
66#define STAT 2 68#define STAT 2
67#define PPID 4 69#define PPID 4
68#define USER 8 70#define USER 8
69#define PROG 16 71#define PROG 16
70#define ARGS 32 72#define ARGS 32
71#define VSZ 64 73#define VSZ 64
72#define RSS 128 74#define RSS 128
73#define PCPU 256 75#define PCPU 256
74#define ELAPSED 512 76#define ELAPSED 512
75#define EREG_ARGS 1024 77#define EREG_ARGS 1024
76#define EXCLUDE_PROGS 2048 78#define EXCLUDE_PROGS 2048
77 79
78#define KTHREAD_PARENT "kthreadd" /* the parent process of kernel threads: 80#define KTHREAD_PARENT \
79 ppid of procs are compared to pid of this proc*/ 81 "kthreadd" /* the parent process of kernel threads: \
80 82 ppid of procs are compared to pid of this proc*/
81/* Different metrics */ 83
82char *metric_name; 84static int verbose = 0;
83enum metric { 85
84 METRIC_PROCS, 86static int stat_exe(const pid_t pid, struct stat *buf) {
85 METRIC_VSZ,
86 METRIC_RSS,
87 METRIC_CPU,
88 METRIC_ELAPSED
89};
90enum metric metric = METRIC_PROCS;
91
92int verbose = 0;
93int uid;
94pid_t ppid;
95int vsz;
96int rss;
97float pcpu;
98char *statopts;
99char *prog;
100char *exclude_progs;
101char **exclude_progs_arr = NULL;
102char exclude_progs_counter = 0;
103char *args;
104char *input_filename = NULL;
105regex_t re_args;
106char *fmt;
107char *fails;
108char tmp[MAX_INPUT_BUFFER];
109int kthread_filter = 0;
110int usepid = 0; /* whether to test for pid or /proc/pid/exe */
111
112FILE *ps_input = NULL;
113
114static int
115stat_exe (const pid_t pid, struct stat *buf) {
116 char *path; 87 char *path;
117 int ret;
118 xasprintf(&path, "/proc/%d/exe", pid); 88 xasprintf(&path, "/proc/%d/exe", pid);
119 ret = stat(path, buf); 89 int ret = stat(path, buf);
120 free(path); 90 free(path);
121 return ret; 91 return ret;
122} 92}
123 93
124 94int main(int argc, char **argv) {
125int 95 setlocale(LC_ALL, "");
126main (int argc, char **argv)
127{
128 char *input_buffer;
129 char *input_line;
130 char *procprog;
131
132 pid_t mypid = 0;
133 pid_t myppid = 0;
134 struct stat statbuf;
135 dev_t mydev = 0;
136 ino_t myino = 0;
137 int procuid = 0;
138 pid_t procpid = 0;
139 pid_t procppid = 0;
140 pid_t kthread_ppid = 0;
141 int procvsz = 0;
142 int procrss = 0;
143 int procseconds = 0;
144 float procpcpu = 0;
145 char procstat[8];
146 char procetime[MAX_INPUT_BUFFER] = { '\0' };
147 char *procargs;
148
149 const char *zombie = "Z";
150
151 int resultsum = 0; /* bitmask of the filter criteria met by a process */
152 int found = 0; /* counter for number of lines returned in `ps` output */
153 int procs = 0; /* counter for number of processes meeting filter criteria */
154 int pos; /* number of spaces before 'args' in `ps` output */
155 int cols; /* number of columns in ps output */
156 int expected_cols = PS_COLS - 1;
157 int warn = 0; /* number of processes in warn state */
158 int crit = 0; /* number of processes in crit state */
159 int i = 0;
160 int result = STATE_UNKNOWN;
161 int ret = 0;
162 output chld_out, chld_err;
163
164 setlocale (LC_ALL, "");
165 bindtextdomain (PACKAGE, LOCALEDIR);
166 textdomain (PACKAGE);
167 setlocale(LC_NUMERIC, "POSIX"); 96 setlocale(LC_NUMERIC, "POSIX");
168 97 bindtextdomain(PACKAGE, LOCALEDIR);
169 input_buffer = malloc (MAX_INPUT_BUFFER); 98 textdomain(PACKAGE);
170 procprog = malloc (MAX_INPUT_BUFFER);
171
172 xasprintf (&metric_name, "PROCS");
173 metric = METRIC_PROCS;
174 99
175 /* Parse extra opts if any */ 100 /* Parse extra opts if any */
176 argv=np_extra_opts (&argc, argv, progname); 101 argv = np_extra_opts(&argc, argv, progname);
102
103 check_procs_config_wrapper tmp_config = process_arguments(argc, argv);
104 if (tmp_config.errorcode == ERROR) {
105 usage4(_("Could not parse arguments"));
106 }
177 107
178 if (process_arguments (argc, argv) == ERROR) 108 check_procs_config config = tmp_config.config;
179 usage4 (_("Could not parse arguments"));
180 109
181 /* find ourself */ 110 /* find ourself */
182 mypid = getpid(); 111 pid_t mypid = getpid();
183 myppid = getppid(); 112 pid_t myppid = getppid();
184 if (usepid || stat_exe(mypid, &statbuf) == -1) { 113 dev_t mydev = 0;
114 ino_t myino = 0;
115 struct stat statbuf;
116 if (config.usepid || stat_exe(mypid, &statbuf) == -1) {
185 /* usepid might have been set by -T */ 117 /* usepid might have been set by -T */
186 usepid = 1; 118 config.usepid = true;
187 } else { 119 } else {
188 usepid = 0; 120 config.usepid = false;
189 mydev = statbuf.st_dev; 121 mydev = statbuf.st_dev;
190 myino = statbuf.st_ino; 122 myino = statbuf.st_ino;
191 } 123 }
192 124
193 /* Set signal handling and alarm timeout */ 125 /* Set signal handling and alarm timeout */
194 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 126 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
195 die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); 127 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
196 } 128 }
197 (void) alarm ((unsigned) timeout_interval); 129 (void)alarm(timeout_interval);
198 130
199 if (verbose >= 2) 131 if (verbose >= 2) {
200 printf (_("CMD: %s\n"), PS_COMMAND); 132 printf(_("CMD: %s\n"), PS_COMMAND);
133 }
201 134
202 if (input_filename == NULL) { 135 output chld_out;
203 result = cmd_run( PS_COMMAND, &chld_out, &chld_err, 0); 136 output chld_err;
137 mp_state_enum result = STATE_UNKNOWN;
138 if (config.input_filename == NULL) {
139 result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0);
204 if (chld_err.lines > 0) { 140 if (chld_err.lines > 0) {
205 printf ("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); 141 printf("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]);
206 exit(STATE_WARNING); 142 exit(STATE_WARNING);
207 } 143 }
208 } else { 144 } else {
209 result = cmd_file_read( input_filename, &chld_out, 0); 145 result = cmd_file_read(config.input_filename, &chld_out, 0);
210 } 146 }
211 147
148 int pos; /* number of spaces before 'args' in `ps` output */
149 uid_t procuid = 0;
150 pid_t procpid = 0;
151 pid_t procppid = 0;
152 pid_t kthread_ppid = 0;
153 int warn = 0; /* number of processes in warn state */
154 int crit = 0; /* number of processes in crit state */
155 int procvsz = 0;
156 int procrss = 0;
157 int procseconds = 0;
158 float procpcpu = 0;
159 char procstat[8];
160 char procetime[MAX_INPUT_BUFFER] = {'\0'};
161 int resultsum = 0; /* bitmask of the filter criteria met by a process */
162 int found = 0; /* counter for number of lines returned in `ps` output */
163 int procs = 0; /* counter for number of processes meeting filter criteria */
164 char *input_buffer = malloc(MAX_INPUT_BUFFER);
165 char *procprog = malloc(MAX_INPUT_BUFFER);
166 const int expected_cols = PS_COLS - 1;
167
212 /* flush first line: j starts at 1 */ 168 /* flush first line: j starts at 1 */
213 for (size_t j = 1; j < chld_out.lines; j++) { 169 for (size_t j = 1; j < chld_out.lines; j++) {
214 input_line = chld_out.line[j]; 170 char *input_line = chld_out.line[j];
215 171
216 if (verbose >= 3) 172 if (verbose >= 3) {
217 printf ("%s", input_line); 173 printf("%s", input_line);
174 }
218 175
219 strcpy (procprog, ""); 176 strcpy(procprog, "");
220 xasprintf (&procargs, "%s", ""); 177 char *procargs;
178 xasprintf(&procargs, "%s", "");
221 179
222 cols = sscanf (input_line, PS_FORMAT, PS_VARLIST); 180 /* number of columns in ps output */
181 int cols = sscanf(input_line, PS_FORMAT, PS_VARLIST);
223 182
224 /* Zombie processes do not give a procprog command */ 183 /* Zombie processes do not give a procprog command */
225 if ( cols < expected_cols && strstr(procstat, zombie) ) { 184 const char *zombie = "Z";
185 if (cols < expected_cols && strstr(procstat, zombie)) {
226 cols = expected_cols; 186 cols = expected_cols;
227 } 187 }
228 if ( cols >= expected_cols ) { 188 if (cols >= expected_cols) {
229 resultsum = 0; 189 resultsum = 0;
230 xasprintf (&procargs, "%s", input_line + pos); 190 xasprintf(&procargs, "%s", input_line + pos);
231 strip (procargs); 191 strip(procargs);
232 192
233 /* Some ps return full pathname for command. This removes path */ 193 /* Some ps return full pathname for command. This removes path */
234 strcpy(procprog, base_name(procprog)); 194 strcpy(procprog, base_name(procprog));
235 195
236 /* we need to convert the elapsed time to seconds */ 196 /* we need to convert the elapsed time to seconds */
237 procseconds = convert_to_seconds(procetime); 197 procseconds = convert_to_seconds(procetime, config.metric);
238 198
239 if (verbose >= 3) 199 if (verbose >= 3) {
240 printf ("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 200 printf("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s "
241 procs, procuid, procvsz, procrss, 201 "prog=%s args=%s\n",
242 procpid, procppid, procpcpu, procstat, 202 procs, procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat,
243 procetime, procprog, procargs); 203 procetime, procprog, procargs);
204 }
244 205
245 /* Ignore self */ 206 /* Ignore self */
246 if ((usepid && mypid == procpid) || 207 int ret = 0;
247 ( ((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || 208 if ((config.usepid && mypid == procpid) ||
248 (ret == -1 && errno == ENOENT)) 209 (((!config.usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) &&
249 ) { 210 statbuf.st_dev == mydev && statbuf.st_ino == myino)) ||
250 if (verbose >= 3) 211 (ret == -1 && errno == ENOENT))) {
251 printf("not considering - is myself or gone\n"); 212 if (verbose >= 3) {
213 printf("not considering - is myself or gone\n");
214 }
252 continue; 215 continue;
253 } 216 }
254 /* Ignore parent*/ 217 /* Ignore parent*/
255 else if (myppid == procpid) { 218 if (myppid == procpid) {
256 if (verbose >= 3) 219 if (verbose >= 3) {
257 printf("not considering - is parent\n"); 220 printf("not considering - is parent\n");
221 }
258 continue; 222 continue;
259 } 223 }
260 224
261 /* Ignore our own children */ 225 /* Ignore our own children */
262 if (procppid == mypid) { 226 if (procppid == mypid) {
263 if (verbose >= 3) 227 if (verbose >= 3) {
264 printf("not considering - is our child\n"); 228 printf("not considering - is our child\n");
229 }
265 continue; 230 continue;
266 } 231 }
267 232
268 /* Ignore excluded processes by name */ 233 /* Ignore excluded processes by name */
269 if(options & EXCLUDE_PROGS) { 234 if (config.options & EXCLUDE_PROGS) {
270 int found = 0; 235 bool found = false;
271 int i = 0; 236 for (int i = 0; i < (config.exclude_progs_counter); i++) {
272 237 if (!strcmp(procprog, config.exclude_progs_arr[i])) {
273 for(i=0; i < (exclude_progs_counter); i++) { 238 found = true;
274 if(!strcmp(procprog, exclude_progs_arr[i])) { 239 }
275 found = 1; 240 }
276 } 241 if (!found) {
277 } 242 resultsum |= EXCLUDE_PROGS;
278 if(found == 0) { 243 } else {
279 resultsum |= EXCLUDE_PROGS; 244 if (verbose >= 3) {
280 } else 245 printf("excluding - by ignorelist\n");
281 { 246 }
282 if(verbose >= 3) 247 }
283 printf("excluding - by ignorelist\n");
284 }
285 } 248 }
286 249
287 /* filter kernel threads (children of KTHREAD_PARENT)*/ 250 /* filter kernel threads (children of KTHREAD_PARENT)*/
288 /* TODO adapt for other OSes than GNU/Linux 251 /* TODO adapt for other OSes than GNU/Linux
289 sorry for not doing that, but I've no other OSes to test :-( */ 252 sorry for not doing that, but I've no other OSes to test :-( */
290 if (kthread_filter == 1) { 253 if (config.kthread_filter) {
291 /* get pid KTHREAD_PARENT */ 254 /* get pid KTHREAD_PARENT */
292 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT) ) 255 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) {
293 kthread_ppid = procpid; 256 kthread_ppid = procpid;
257 }
294 258
295 if (kthread_ppid == procppid) { 259 if (kthread_ppid == procppid) {
296 if (verbose >= 2) 260 if (verbose >= 2) {
297 printf ("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs); 261 printf("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid,
262 procppid, procprog, procargs);
263 }
298 continue; 264 continue;
299 } 265 }
300 } 266 }
301 267
302 if ((options & STAT) && (strstr (procstat, statopts))) 268 if ((config.options & STAT) && (strstr(procstat, config.statopts))) {
303 resultsum |= STAT; 269 resultsum |= STAT;
304 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL)) 270 }
271 if ((config.options & ARGS) && procargs && (strstr(procargs, config.args) != NULL)) {
305 resultsum |= ARGS; 272 resultsum |= ARGS;
306 if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t) 0, NULL, 0) == 0)) 273 }
274 if ((config.options & EREG_ARGS) && procargs &&
275 (regexec(&config.re_args, procargs, (size_t)0, NULL, 0) == 0)) {
307 resultsum |= EREG_ARGS; 276 resultsum |= EREG_ARGS;
308 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0)) 277 }
278 if ((config.options & PROG) && procprog && (strcmp(config.prog, procprog) == 0)) {
309 resultsum |= PROG; 279 resultsum |= PROG;
310 if ((options & PPID) && (procppid == ppid)) 280 }
281 if ((config.options & PPID) && (procppid == config.ppid)) {
311 resultsum |= PPID; 282 resultsum |= PPID;
312 if ((options & USER) && (procuid == uid)) 283 }
284 if ((config.options & USER) && (procuid == config.uid)) {
313 resultsum |= USER; 285 resultsum |= USER;
314 if ((options & VSZ) && (procvsz >= vsz)) 286 }
287 if ((config.options & VSZ) && (procvsz >= config.vsz)) {
315 resultsum |= VSZ; 288 resultsum |= VSZ;
316 if ((options & RSS) && (procrss >= rss)) 289 }
290 if ((config.options & RSS) && (procrss >= config.rss)) {
317 resultsum |= RSS; 291 resultsum |= RSS;
318 if ((options & PCPU) && (procpcpu >= pcpu)) 292 }
293 if ((config.options & PCPU) && (procpcpu >= config.pcpu)) {
319 resultsum |= PCPU; 294 resultsum |= PCPU;
295 }
320 296
321 found++; 297 found++;
322 298
323 /* Next line if filters not matched */ 299 /* Next line if filters not matched */
324 if (!(options == resultsum || options == ALL)) 300 if (!(config.options == resultsum || config.options == ALL)) {
325 continue; 301 continue;
302 }
326 303
327 procs++; 304 procs++;
328 if (verbose >= 2) { 305 if (verbose >= 2) {
329 printf ("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 306 printf("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s "
330 procuid, procvsz, procrss, 307 "prog=%s args=%s\n",
331 procpid, procppid, procpcpu, procstat, 308 procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat, procetime,
332 procetime, procprog, procargs); 309 procprog, procargs);
333 } 310 }
334 311
335 if (metric == METRIC_VSZ) 312 mp_state_enum temporary_result = STATE_OK;
336 i = get_status ((double)procvsz, procs_thresholds); 313 if (config.metric == METRIC_VSZ) {
337 else if (metric == METRIC_RSS) 314 temporary_result = get_status((double)procvsz, config.procs_thresholds);
338 i = get_status ((double)procrss, procs_thresholds); 315 } else if (config.metric == METRIC_RSS) {
316 temporary_result = get_status((double)procrss, config.procs_thresholds);
317 }
339 /* TODO? float thresholds for --metric=CPU */ 318 /* TODO? float thresholds for --metric=CPU */
340 else if (metric == METRIC_CPU) 319 else if (config.metric == METRIC_CPU) {
341 i = get_status (procpcpu, procs_thresholds); 320 temporary_result = get_status(procpcpu, config.procs_thresholds);
342 else if (metric == METRIC_ELAPSED) 321 } else if (config.metric == METRIC_ELAPSED) {
343 i = get_status ((double)procseconds, procs_thresholds); 322 temporary_result = get_status((double)procseconds, config.procs_thresholds);
323 }
344 324
345 if (metric != METRIC_PROCS) { 325 if (config.metric != METRIC_PROCS) {
346 if (i == STATE_WARNING) { 326 if (temporary_result == STATE_WARNING) {
347 warn++; 327 warn++;
348 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 328 xasprintf(&config.fails, "%s%s%s", config.fails,
349 result = max_state (result, i); 329 (strcmp(config.fails, "") ? ", " : ""), procprog);
330 result = max_state(result, temporary_result);
350 } 331 }
351 if (i == STATE_CRITICAL) { 332 if (temporary_result == STATE_CRITICAL) {
352 crit++; 333 crit++;
353 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 334 xasprintf(&config.fails, "%s%s%s", config.fails,
354 result = max_state (result, i); 335 (strcmp(config.fails, "") ? ", " : ""), procprog);
336 result = max_state(result, temporary_result);
355 } 337 }
356 } 338 }
357 } 339 }
@@ -361,339 +343,366 @@ main (int argc, char **argv)
361 } 343 }
362 } 344 }
363 345
364 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ 346 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
365 printf (_("Unable to read output\n")); 347 printf(_("Unable to read output\n"));
366 return STATE_UNKNOWN; 348 return STATE_UNKNOWN;
367 } 349 }
368 350
369 if ( result == STATE_UNKNOWN ) 351 if (result == STATE_UNKNOWN) {
370 result = STATE_OK; 352 result = STATE_OK;
353 }
371 354
372 /* Needed if procs found, but none match filter */ 355 /* Needed if procs found, but none match filter */
373 if ( metric == METRIC_PROCS ) { 356 if (config.metric == METRIC_PROCS) {
374 result = max_state (result, get_status ((double)procs, procs_thresholds) ); 357 result = max_state(result, get_status((double)procs, config.procs_thresholds));
375 } 358 }
376 359
377 if ( result == STATE_OK ) { 360 if (result == STATE_OK) {
378 printf ("%s %s: ", metric_name, _("OK")); 361 printf("%s %s: ", config.metric_name, _("OK"));
379 } else if (result == STATE_WARNING) { 362 } else if (result == STATE_WARNING) {
380 printf ("%s %s: ", metric_name, _("WARNING")); 363 printf("%s %s: ", config.metric_name, _("WARNING"));
381 if ( metric != METRIC_PROCS ) { 364 if (config.metric != METRIC_PROCS) {
382 printf (_("%d warn out of "), warn); 365 printf(_("%d warn out of "), warn);
383 } 366 }
384 } else if (result == STATE_CRITICAL) { 367 } else if (result == STATE_CRITICAL) {
385 printf ("%s %s: ", metric_name, _("CRITICAL")); 368 printf("%s %s: ", config.metric_name, _("CRITICAL"));
386 if (metric != METRIC_PROCS) { 369 if (config.metric != METRIC_PROCS) {
387 printf (_("%d crit, %d warn out of "), crit, warn); 370 printf(_("%d crit, %d warn out of "), crit, warn);
388 } 371 }
389 } 372 }
390 printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs); 373 printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs);
391 374
392 if (strcmp(fmt,"") != 0) { 375 if (strcmp(config.fmt, "") != 0) {
393 printf (_(" with %s"), fmt); 376 printf(_(" with %s"), config.fmt);
394 } 377 }
395 378
396 if ( verbose >= 1 && strcmp(fails,"") ) 379 if (verbose >= 1 && strcmp(config.fails, "")) {
397 printf (" [%s]", fails); 380 printf(" [%s]", config.fails);
381 }
398 382
399 if (metric == METRIC_PROCS) 383 if (config.metric == METRIC_PROCS) {
400 printf (" | procs=%d;%s;%s;0;", procs, 384 printf(" | procs=%d;%s;%s;0;", procs, config.warning_range ? config.warning_range : "",
401 warning_range ? warning_range : "", 385 config.critical_range ? config.critical_range : "");
402 critical_range ? critical_range : ""); 386 } else {
403 else 387 printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit);
404 printf (" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); 388 }
405 389
406 printf ("\n"); 390 printf("\n");
407 return result; 391 exit(result);
408} 392}
409 393
410
411
412/* process command-line arguments */ 394/* process command-line arguments */
413int 395check_procs_config_wrapper process_arguments(int argc, char **argv) {
414process_arguments (int argc, char **argv) 396 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
415{ 397 {"critical", required_argument, 0, 'c'},
416 int c = 1; 398 {"metric", required_argument, 0, 'm'},
417 char *user; 399 {"timeout", required_argument, 0, 't'},
418 struct passwd *pw; 400 {"status", required_argument, 0, 's'},
419 int option = 0; 401 {"ppid", required_argument, 0, 'p'},
420 int err; 402 {"user", required_argument, 0, 'u'},
421 int cflags = REG_NOSUB | REG_EXTENDED; 403 {"command", required_argument, 0, 'C'},
422 char errbuf[MAX_INPUT_BUFFER]; 404 {"vsz", required_argument, 0, 'z'},
423 char *temp_string; 405 {"rss", required_argument, 0, 'r'},
424 int i=0; 406 {"pcpu", required_argument, 0, 'P'},
425 static struct option longopts[] = { 407 {"elapsed", required_argument, 0, 'e'},
426 {"warning", required_argument, 0, 'w'}, 408 {"argument-array", required_argument, 0, 'a'},
427 {"critical", required_argument, 0, 'c'}, 409 {"help", no_argument, 0, 'h'},
428 {"metric", required_argument, 0, 'm'}, 410 {"version", no_argument, 0, 'V'},
429 {"timeout", required_argument, 0, 't'}, 411 {"verbose", no_argument, 0, 'v'},
430 {"status", required_argument, 0, 's'}, 412 {"ereg-argument-array", required_argument, 0, CHAR_MAX + 1},
431 {"ppid", required_argument, 0, 'p'}, 413 {"input-file", required_argument, 0, CHAR_MAX + 2},
432 {"user", required_argument, 0, 'u'}, 414 {"no-kthreads", required_argument, 0, 'k'},
433 {"command", required_argument, 0, 'C'}, 415 {"traditional-filter", no_argument, 0, 'T'},
434 {"vsz", required_argument, 0, 'z'}, 416 {"exclude-process", required_argument, 0, 'X'},
435 {"rss", required_argument, 0, 'r'}, 417 {0, 0, 0, 0}};
436 {"pcpu", required_argument, 0, 'P'}, 418
437 {"elapsed", required_argument, 0, 'e'}, 419 for (int index = 1; index < argc; index++) {
438 {"argument-array", required_argument, 0, 'a'}, 420 if (strcmp("-to", argv[index]) == 0) {
439 {"help", no_argument, 0, 'h'}, 421 strcpy(argv[index], "-t");
440 {"version", no_argument, 0, 'V'}, 422 }
441 {"verbose", no_argument, 0, 'v'}, 423 }
442 {"ereg-argument-array", required_argument, 0, CHAR_MAX+1},
443 {"input-file", required_argument, 0, CHAR_MAX+2},
444 {"no-kthreads", required_argument, 0, 'k'},
445 {"traditional-filter", no_argument, 0, 'T'},
446 {"exclude-process", required_argument, 0, 'X'},
447 {0, 0, 0, 0}
448 };
449 424
450 for (c = 1; c < argc; c++) 425 check_procs_config_wrapper result = {
451 if (strcmp ("-to", argv[c]) == 0) 426 .errorcode = OK,
452 strcpy (argv[c], "-t"); 427 .config = check_procs_config_init(),
428 };
453 429
454 while (1) { 430 while (true) {
455 c = getopt_long (argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", 431 int option = 0;
456 longopts, &option); 432 int option_index =
433 getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option);
457 434
458 if (c == -1 || c == EOF) 435 if (option_index == -1 || option_index == EOF) {
459 break; 436 break;
437 }
460 438
461 switch (c) { 439 switch (option_index) {
462 case '?': /* help */ 440 case '?': /* help */
463 usage5 (); 441 usage5();
464 case 'h': /* help */ 442 case 'h': /* help */
465 print_help (); 443 print_help();
466 exit (STATE_UNKNOWN); 444 exit(STATE_UNKNOWN);
467 case 'V': /* version */ 445 case 'V': /* version */
468 print_revision (progname, NP_VERSION); 446 print_revision(progname, NP_VERSION);
469 exit (STATE_UNKNOWN); 447 exit(STATE_UNKNOWN);
470 case 't': /* timeout period */ 448 case 't': /* timeout period */
471 if (!is_integer (optarg)) 449 if (!is_integer(optarg)) {
472 usage2 (_("Timeout interval must be a positive integer"), optarg); 450 usage2(_("Timeout interval must be a positive integer"), optarg);
473 else 451 } else {
474 timeout_interval = atoi (optarg); 452 timeout_interval = atoi(optarg);
453 }
475 break; 454 break;
476 case 'c': /* critical threshold */ 455 case 'c': /* critical threshold */
477 critical_range = optarg; 456 result.config.critical_range = optarg;
478 break; 457 break;
479 case 'w': /* warning threshold */ 458 case 'w': /* warning threshold */
480 warning_range = optarg; 459 result.config.warning_range = optarg;
481 break; 460 break;
482 case 'p': /* process id */ 461 case 'p': { /* process id */
483 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) { 462 static char tmp[MAX_INPUT_BUFFER];
484 xasprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid); 463 if (sscanf(optarg, "%d%[^0-9]", &result.config.ppid, tmp) == 1) {
485 options |= PPID; 464 xasprintf(&result.config.fmt, "%s%sPPID = %d",
465 (result.config.fmt ? result.config.fmt : ""),
466 (result.config.options ? ", " : ""), result.config.ppid);
467 result.config.options |= PPID;
486 break; 468 break;
487 } 469 }
488 usage4 (_("Parent Process ID must be an integer!")); 470 usage4(_("Parent Process ID must be an integer!"));
489 case 's': /* status */ 471 }
490 if (statopts) 472 case 's': /* status */
473 if (result.config.statopts) {
491 break; 474 break;
492 else 475 } else {
493 statopts = optarg; 476 result.config.statopts = optarg;
494 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 477 }
495 options |= STAT; 478 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"),
479 (result.config.fmt ? result.config.fmt : ""),
480 (result.config.options ? ", " : ""), result.config.statopts);
481 result.config.options |= STAT;
496 break; 482 break;
497 case 'u': /* user or user id */ 483 case 'u': /* user or user id */ {
498 if (is_integer (optarg)) { 484 struct passwd *pw;
499 uid = atoi (optarg); 485 if (is_integer(optarg)) {
500 pw = getpwuid ((uid_t) uid); 486 result.config.uid = atoi(optarg);
487 pw = getpwuid(result.config.uid);
501 /* check to be sure user exists */ 488 /* check to be sure user exists */
502 if (pw == NULL) 489 if (pw == NULL) {
503 usage2 (_("UID was not found"), optarg); 490 usage2(_("UID was not found"), optarg);
504 } 491 }
505 else { 492 } else {
506 pw = getpwnam (optarg); 493 pw = getpwnam(optarg);
507 /* check to be sure user exists */ 494 /* check to be sure user exists */
508 if (pw == NULL) 495 if (pw == NULL) {
509 usage2 (_("User name was not found"), optarg); 496 usage2(_("User name was not found"), optarg);
497 }
510 /* then get uid */ 498 /* then get uid */
511 uid = pw->pw_uid; 499 result.config.uid = pw->pw_uid;
512 } 500 }
513 user = pw->pw_name; 501
514 xasprintf (&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), 502 char *user = pw->pw_name;
515 uid, user); 503 xasprintf(&result.config.fmt, "%s%sUID = %d (%s)",
516 options |= USER; 504 (result.config.fmt ? result.config.fmt : ""),
517 break; 505 (result.config.options ? ", " : ""), result.config.uid, user);
518 case 'C': /* command */ 506 result.config.options |= USER;
507 } break;
508 case 'C': /* command */
519 /* TODO: allow this to be passed in with --metric */ 509 /* TODO: allow this to be passed in with --metric */
520 if (prog) 510 if (result.config.prog) {
521 break; 511 break;
522 else 512 } else {
523 prog = optarg; 513 result.config.prog = optarg;
524 xasprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 514 }
525 prog); 515 xasprintf(&result.config.fmt, _("%s%scommand name '%s'"),
526 options |= PROG; 516 (result.config.fmt ? result.config.fmt : ""),
517 (result.config.options ? ", " : ""), result.config.prog);
518 result.config.options |= PROG;
527 break; 519 break;
528 case 'X': 520 case 'X':
529 if(exclude_progs) 521 if (result.config.exclude_progs) {
530 break; 522 break;
531 else 523 } else {
532 exclude_progs = optarg; 524 result.config.exclude_progs = optarg;
533 xasprintf (&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 525 }
534 exclude_progs); 526 xasprintf(&result.config.fmt, _("%s%sexclude progs '%s'"),
535 char *p = strtok(exclude_progs, ","); 527 (result.config.fmt ? result.config.fmt : ""),
536 528 (result.config.options ? ", " : ""), result.config.exclude_progs);
537 while(p){ 529 char *tmp_pointer = strtok(result.config.exclude_progs, ",");
538 exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char*) * ++exclude_progs_counter); 530
539 exclude_progs_arr[exclude_progs_counter-1] = p; 531 while (tmp_pointer) {
540 p = strtok(NULL, ","); 532 result.config.exclude_progs_arr =
533 realloc(result.config.exclude_progs_arr,
534 sizeof(char *) * ++result.config.exclude_progs_counter);
535 result.config.exclude_progs_arr[result.config.exclude_progs_counter - 1] =
536 tmp_pointer;
537 tmp_pointer = strtok(NULL, ",");
541 } 538 }
542 539
543 options |= EXCLUDE_PROGS; 540 result.config.options |= EXCLUDE_PROGS;
544 break; 541 break;
545 case 'a': /* args (full path name with args) */ 542 case 'a': /* args (full path name with args) */
546 /* TODO: allow this to be passed in with --metric */ 543 /* TODO: allow this to be passed in with --metric */
547 if (args) 544 if (result.config.args) {
548 break; 545 break;
549 else 546 } else {
550 args = optarg; 547 result.config.args = optarg;
551 xasprintf (&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); 548 }
552 options |= ARGS; 549 xasprintf(&result.config.fmt, "%s%sargs '%s'",
550 (result.config.fmt ? result.config.fmt : ""),
551 (result.config.options ? ", " : ""), result.config.args);
552 result.config.options |= ARGS;
553 break; 553 break;
554 case CHAR_MAX+1: 554 case CHAR_MAX + 1: {
555 err = regcomp(&re_args, optarg, cflags); 555 int cflags = REG_NOSUB | REG_EXTENDED;
556 int err = regcomp(&result.config.re_args, optarg, cflags);
556 if (err != 0) { 557 if (err != 0) {
557 regerror (err, &re_args, errbuf, MAX_INPUT_BUFFER); 558 char errbuf[MAX_INPUT_BUFFER];
558 die (STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 559 regerror(err, &result.config.re_args, errbuf, MAX_INPUT_BUFFER);
560 die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"),
561 _("Could not compile regular expression"), errbuf);
559 } 562 }
560 /* Strip off any | within the regex optarg */ 563 /* Strip off any | within the regex optarg */
561 temp_string = strdup(optarg); 564 char *temp_string = strdup(optarg);
562 while(temp_string[i]!='\0'){ 565 int index = 0;
563 if(temp_string[i]=='|') 566 while (temp_string[index] != '\0') {
564 temp_string[i]=','; 567 if (temp_string[index] == '|') {
565 i++; 568 temp_string[index] = ',';
566 } 569 }
567 xasprintf (&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); 570 index++;
568 options |= EREG_ARGS; 571 }
569 break; 572 xasprintf(&result.config.fmt, "%s%sregex args '%s'",
570 case 'r': /* RSS */ 573 (result.config.fmt ? result.config.fmt : ""),
571 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) { 574 (result.config.options ? ", " : ""), temp_string);
572 xasprintf (&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); 575 result.config.options |= EREG_ARGS;
573 options |= RSS; 576 } break;
577 case 'r': { /* RSS */
578 static char tmp[MAX_INPUT_BUFFER];
579 if (sscanf(optarg, "%d%[^0-9]", &result.config.rss, tmp) == 1) {
580 xasprintf(&result.config.fmt, "%s%sRSS >= %d",
581 (result.config.fmt ? result.config.fmt : ""),
582 (result.config.options ? ", " : ""), result.config.rss);
583 result.config.options |= RSS;
574 break; 584 break;
575 } 585 }
576 usage4 (_("RSS must be an integer!")); 586 usage4(_("RSS must be an integer!"));
577 case 'z': /* VSZ */ 587 }
578 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) { 588 case 'z': { /* VSZ */
579 xasprintf (&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); 589 static char tmp[MAX_INPUT_BUFFER];
580 options |= VSZ; 590 if (sscanf(optarg, "%d%[^0-9]", &result.config.vsz, tmp) == 1) {
591 xasprintf(&result.config.fmt, "%s%sVSZ >= %d",
592 (result.config.fmt ? result.config.fmt : ""),
593 (result.config.options ? ", " : ""), result.config.vsz);
594 result.config.options |= VSZ;
581 break; 595 break;
582 } 596 }
583 usage4 (_("VSZ must be an integer!")); 597 usage4(_("VSZ must be an integer!"));
584 case 'P': /* PCPU */ 598 }
599 case 'P': { /* PCPU */
585 /* TODO: -P 1.5.5 is accepted */ 600 /* TODO: -P 1.5.5 is accepted */
586 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { 601 static char tmp[MAX_INPUT_BUFFER];
587 xasprintf (&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); 602 if (sscanf(optarg, "%f%[^0-9.]", &result.config.pcpu, tmp) == 1) {
588 options |= PCPU; 603 xasprintf(&result.config.fmt, "%s%sPCPU >= %.2f",
604 (result.config.fmt ? result.config.fmt : ""),
605 (result.config.options ? ", " : ""), result.config.pcpu);
606 result.config.options |= PCPU;
589 break; 607 break;
590 } 608 }
591 usage4 (_("PCPU must be a float!")); 609 usage4(_("PCPU must be a float!"));
610 }
592 case 'm': 611 case 'm':
593 xasprintf (&metric_name, "%s", optarg); 612 xasprintf(&result.config.metric_name, "%s", optarg);
594 if ( strcmp(optarg, "PROCS") == 0) { 613 if (strcmp(optarg, "PROCS") == 0) {
595 metric = METRIC_PROCS; 614 result.config.metric = METRIC_PROCS;
596 break; 615 break;
597 } 616 }
598 else if ( strcmp(optarg, "VSZ") == 0) { 617 if (strcmp(optarg, "VSZ") == 0) {
599 metric = METRIC_VSZ; 618 result.config.metric = METRIC_VSZ;
600 break; 619 break;
601 } 620 }
602 else if ( strcmp(optarg, "RSS") == 0 ) { 621 if (strcmp(optarg, "RSS") == 0) {
603 metric = METRIC_RSS; 622 result.config.metric = METRIC_RSS;
604 break; 623 break;
605 } 624 }
606 else if ( strcmp(optarg, "CPU") == 0 ) { 625 if (strcmp(optarg, "CPU") == 0) {
607 metric = METRIC_CPU; 626 result.config.metric = METRIC_CPU;
608 break; 627 break;
609 } 628 }
610 else if ( strcmp(optarg, "ELAPSED") == 0) { 629 if (strcmp(optarg, "ELAPSED") == 0) {
611 metric = METRIC_ELAPSED; 630 result.config.metric = METRIC_ELAPSED;
612 break; 631 break;
613 } 632 }
614 633
615 usage4 (_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); 634 usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!"));
616 case 'k': /* linux kernel thread filter */ 635 case 'k': /* linux kernel thread filter */
617 kthread_filter = 1; 636 result.config.kthread_filter = true;
618 break; 637 break;
619 case 'v': /* command */ 638 case 'v': /* command */
620 verbose++; 639 verbose++;
621 break; 640 break;
622 case 'T': 641 case 'T':
623 usepid = 1; 642 result.config.usepid = true;
624 break; 643 break;
625 case CHAR_MAX+2: 644 case CHAR_MAX + 2:
626 input_filename = optarg; 645 result.config.input_filename = optarg;
627 break; 646 break;
628 } 647 }
629 } 648 }
630 649
631 c = optind; 650 int index = optind;
632 if ((! warning_range) && argv[c]) 651 if ((!result.config.warning_range) && argv[index]) {
633 warning_range = argv[c++]; 652 result.config.warning_range = argv[index++];
634 if ((! critical_range) && argv[c]) 653 }
635 critical_range = argv[c++]; 654 if ((!result.config.critical_range) && argv[index]) {
636 if (statopts == NULL && argv[c]) { 655 result.config.critical_range = argv[index++];
637 xasprintf (&statopts, "%s", argv[c++]); 656 }
638 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 657 if (result.config.statopts == NULL && argv[index]) {
639 options |= STAT; 658 xasprintf(&result.config.statopts, "%s", argv[index++]);
659 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"),
660 (result.config.fmt ? result.config.fmt : ""), (result.config.options ? ", " : ""),
661 result.config.statopts);
662 result.config.options |= STAT;
640 } 663 }
641 664
642 /* this will abort in case of invalid ranges */ 665 /* this will abort in case of invalid ranges */
643 set_thresholds (&procs_thresholds, warning_range, critical_range); 666 set_thresholds(&result.config.procs_thresholds, result.config.warning_range,
667 result.config.critical_range);
644 668
645 return validate_arguments (); 669 return validate_arguments(result);
646} 670}
647 671
672check_procs_config_wrapper validate_arguments(check_procs_config_wrapper config_wrapper) {
673 if (config_wrapper.config.options == 0) {
674 config_wrapper.config.options = ALL;
675 }
648 676
677 if (config_wrapper.config.statopts == NULL) {
678 config_wrapper.config.statopts = strdup("");
679 }
649 680
650int 681 if (config_wrapper.config.prog == NULL) {
651validate_arguments () 682 config_wrapper.config.prog = strdup("");
652{ 683 }
653 if (options == 0)
654 options = ALL;
655
656 if (statopts==NULL)
657 statopts = strdup("");
658
659 if (prog==NULL)
660 prog = strdup("");
661 684
662 if (args==NULL) 685 if (config_wrapper.config.args == NULL) {
663 args = strdup(""); 686 config_wrapper.config.args = strdup("");
687 }
664 688
665 if (fmt==NULL) 689 if (config_wrapper.config.fmt == NULL) {
666 fmt = strdup(""); 690 config_wrapper.config.fmt = strdup("");
691 }
667 692
668 if (fails==NULL) 693 if (config_wrapper.config.fails == NULL) {
669 fails = strdup(""); 694 config_wrapper.config.fails = strdup("");
695 }
670 696
671 return options; 697 // return options;
698 return config_wrapper;
672} 699}
673 700
674
675/* convert the elapsed time to seconds */ 701/* convert the elapsed time to seconds */
676int 702int convert_to_seconds(char *etime, enum metric metric) {
677convert_to_seconds(char *etime) { 703 int hyphcnt = 0;
678 704 int coloncnt = 0;
679 char *ptr; 705 for (char *ptr = etime; *ptr != '\0'; ptr++) {
680 int total;
681
682 int hyphcnt;
683 int coloncnt;
684 int days;
685 int hours;
686 int minutes;
687 int seconds;
688
689 hyphcnt = 0;
690 coloncnt = 0;
691 days = 0;
692 hours = 0;
693 minutes = 0;
694 seconds = 0;
695
696 for (ptr = etime; *ptr != '\0'; ptr++) {
697 706
698 if (*ptr == '-') { 707 if (*ptr == '-') {
699 hyphcnt++; 708 hyphcnt++;
@@ -705,9 +714,12 @@ convert_to_seconds(char *etime) {
705 } 714 }
706 } 715 }
707 716
717 int days = 0;
718 int hours = 0;
719 int minutes = 0;
720 int seconds = 0;
708 if (hyphcnt > 0) { 721 if (hyphcnt > 0) {
709 sscanf(etime, "%d-%d:%d:%d", 722 sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds);
710 &days, &hours, &minutes, &seconds);
711 /* linux 2.6.5/2.6.6 reporting some processes with infinite 723 /* linux 2.6.5/2.6.6 reporting some processes with infinite
712 * elapsed times for some reason */ 724 * elapsed times for some reason */
713 if (days == 49710) { 725 if (days == 49710) {
@@ -715,135 +727,129 @@ convert_to_seconds(char *etime) {
715 } 727 }
716 } else { 728 } else {
717 if (coloncnt == 2) { 729 if (coloncnt == 2) {
718 sscanf(etime, "%d:%d:%d", 730 sscanf(etime, "%d:%d:%d", &hours, &minutes, &seconds);
719 &hours, &minutes, &seconds);
720 } else if (coloncnt == 1) { 731 } else if (coloncnt == 1) {
721 sscanf(etime, "%d:%d", 732 sscanf(etime, "%d:%d", &minutes, &seconds);
722 &minutes, &seconds);
723 } 733 }
724 } 734 }
725 735
726 total = (days * 86400) + 736 int total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds;
727 (hours * 3600) +
728 (minutes * 60) +
729 seconds;
730 737
731 if (verbose >= 3 && metric == METRIC_ELAPSED) { 738 if (verbose >= 3 && metric == METRIC_ELAPSED) {
732 printf("seconds: %d\n", total); 739 printf("seconds: %d\n", total);
733 } 740 }
734 return total; 741 return total;
735} 742}
736 743
737 744void print_help(void) {
738void 745 print_revision(progname, NP_VERSION);
739print_help (void) 746
740{ 747 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
741 print_revision (progname, NP_VERSION); 748 printf(COPYRIGHT, copyright, email);
742 749
743 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 750 printf("%s\n",
744 printf (COPYRIGHT, copyright, email); 751 _("Checks all processes and generates WARNING or CRITICAL states if the specified"));
745 752 printf("%s\n",
746 printf ("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified")); 753 _("metric is outside the required threshold ranges. The metric defaults to number"));
747 printf ("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); 754 printf("%s\n",
748 printf ("%s\n", _("of processes. Search filters can be applied to limit the processes to check.")); 755 _("of processes. Search filters can be applied to limit the processes to check."));
749 756
750 printf ("\n\n"); 757 printf("\n\n");
751 758
752 printf ("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); 759 printf("%s\n",
753 printf ("%s\n", _("are excluded from any checks to prevent false positives.")); 760 _("The parent process, check_procs itself and any child process of check_procs (ps)"));
754 761 printf("%s\n", _("are excluded from any checks to prevent false positives."));
755 printf ("\n\n"); 762
756 763 printf("\n\n");
757 print_usage (); 764
758 765 print_usage();
759 printf (UT_HELP_VRSN); 766
760 printf (UT_EXTRA_OPTS); 767 printf(UT_HELP_VRSN);
761 printf (" %s\n", "-w, --warning=RANGE"); 768 printf(UT_EXTRA_OPTS);
762 printf (" %s\n", _("Generate warning state if metric is outside this range")); 769 printf(" %s\n", "-w, --warning=RANGE");
763 printf (" %s\n", "-c, --critical=RANGE"); 770 printf(" %s\n", _("Generate warning state if metric is outside this range"));
764 printf (" %s\n", _("Generate critical state if metric is outside this range")); 771 printf(" %s\n", "-c, --critical=RANGE");
765 printf (" %s\n", "-m, --metric=TYPE"); 772 printf(" %s\n", _("Generate critical state if metric is outside this range"));
766 printf (" %s\n", _("Check thresholds against metric. Valid types:")); 773 printf(" %s\n", "-m, --metric=TYPE");
767 printf (" %s\n", _("PROCS - number of processes (default)")); 774 printf(" %s\n", _("Check thresholds against metric. Valid types:"));
768 printf (" %s\n", _("VSZ - virtual memory size")); 775 printf(" %s\n", _("PROCS - number of processes (default)"));
769 printf (" %s\n", _("RSS - resident set memory size")); 776 printf(" %s\n", _("VSZ - virtual memory size"));
770 printf (" %s\n", _("CPU - percentage CPU")); 777 printf(" %s\n", _("RSS - resident set memory size"));
778 printf(" %s\n", _("CPU - percentage CPU"));
771/* only linux etime is support currently */ 779/* only linux etime is support currently */
772#if defined( __linux__ ) 780#if defined(__linux__)
773 printf (" %s\n", _("ELAPSED - time elapsed in seconds")); 781 printf(" %s\n", _("ELAPSED - time elapsed in seconds"));
774#endif /* defined(__linux__) */ 782#endif /* defined(__linux__) */
775 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 783 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
776 784
777 printf (" %s\n", "-v, --verbose"); 785 printf(" %s\n", "-v, --verbose");
778 printf (" %s\n", _("Extra information. Up to 3 verbosity levels")); 786 printf(" %s\n", _("Extra information. Up to 3 verbosity levels"));
779 787
780 printf (" %s\n", "-T, --traditional"); 788 printf(" %s\n", "-T, --traditional");
781 printf (" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); 789 printf(" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe"));
782 790
783 printf ("\n"); 791 printf("\n");
784 printf ("%s\n", "Filters:"); 792 printf("%s\n", "Filters:");
785 printf (" %s\n", "-s, --state=STATUSFLAGS"); 793 printf(" %s\n", "-s, --state=STATUSFLAGS");
786 printf (" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); 794 printf(" %s\n", _("Only scan for processes that have, in the output of `ps`, one or"));
787 printf (" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); 795 printf(" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,"));
788 printf (" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); 796 printf(" %s\n", _("RSZDT, plus others based on the output of your 'ps' command)."));
789 printf (" %s\n", "-p, --ppid=PPID"); 797 printf(" %s\n", "-p, --ppid=PPID");
790 printf (" %s\n", _("Only scan for children of the parent process ID indicated.")); 798 printf(" %s\n", _("Only scan for children of the parent process ID indicated."));
791 printf (" %s\n", "-z, --vsz=VSZ"); 799 printf(" %s\n", "-z, --vsz=VSZ");
792 printf (" %s\n", _("Only scan for processes with VSZ higher than indicated.")); 800 printf(" %s\n", _("Only scan for processes with VSZ higher than indicated."));
793 printf (" %s\n", "-r, --rss=RSS"); 801 printf(" %s\n", "-r, --rss=RSS");
794 printf (" %s\n", _("Only scan for processes with RSS higher than indicated.")); 802 printf(" %s\n", _("Only scan for processes with RSS higher than indicated."));
795 printf (" %s\n", "-P, --pcpu=PCPU"); 803 printf(" %s\n", "-P, --pcpu=PCPU");
796 printf (" %s\n", _("Only scan for processes with PCPU higher than indicated.")); 804 printf(" %s\n", _("Only scan for processes with PCPU higher than indicated."));
797 printf (" %s\n", "-u, --user=USER"); 805 printf(" %s\n", "-u, --user=USER");
798 printf (" %s\n", _("Only scan for processes with user name or ID indicated.")); 806 printf(" %s\n", _("Only scan for processes with user name or ID indicated."));
799 printf (" %s\n", "-a, --argument-array=STRING"); 807 printf(" %s\n", "-a, --argument-array=STRING");
800 printf (" %s\n", _("Only scan for processes with args that contain STRING.")); 808 printf(" %s\n", _("Only scan for processes with args that contain STRING."));
801 printf (" %s\n", "--ereg-argument-array=STRING"); 809 printf(" %s\n", "--ereg-argument-array=STRING");
802 printf (" %s\n", _("Only scan for processes with args that contain the regex STRING.")); 810 printf(" %s\n", _("Only scan for processes with args that contain the regex STRING."));
803 printf (" %s\n", "-C, --command=COMMAND"); 811 printf(" %s\n", "-C, --command=COMMAND");
804 printf (" %s\n", _("Only scan for exact matches of COMMAND (without path).")); 812 printf(" %s\n", _("Only scan for exact matches of COMMAND (without path)."));
805 printf (" %s\n", "-X, --exclude-process"); 813 printf(" %s\n", "-X, --exclude-process");
806 printf (" %s\n", _("Exclude processes which match this comma separated list")); 814 printf(" %s\n", _("Exclude processes which match this comma separated list"));
807 printf (" %s\n", "-k, --no-kthreads"); 815 printf(" %s\n", "-k, --no-kthreads");
808 printf (" %s\n", _("Only scan for non kernel threads (works on Linux only).")); 816 printf(" %s\n", _("Only scan for non kernel threads (works on Linux only)."));
809 817
810 printf(_("\n\ 818 printf(_("\n\
811RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\ 819RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
812specified 'max:min', a warning status will be generated if the\n\ 820specified 'max:min', a warning status will be generated if the\n\
813count is inside the specified range\n\n")); 821count is inside the specified range\n\n"));
814 822
815 printf(_("\ 823 printf(_("\
816This plugin checks the number of currently running processes and\n\ 824This plugin checks the number of currently running processes and\n\
817generates WARNING or CRITICAL states if the process count is outside\n\ 825generates WARNING or CRITICAL states if the process count is outside\n\
818the specified threshold ranges. The process count can be filtered by\n\ 826the specified threshold ranges. The process count can be filtered by\n\
819process owner, parent process PID, current state (e.g., 'Z'), or may\n\ 827process owner, parent process PID, current state (e.g., 'Z'), or may\n\
820be the total number of running processes\n\n")); 828be the total number of running processes\n\n"));
821 829
822 printf ("%s\n", _("Examples:")); 830 printf("%s\n", _("Examples:"));
823 printf (" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); 831 printf(" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry");
824 printf (" %s\n", _("Warning if not two processes with command name portsentry.")); 832 printf(" %s\n", _("Warning if not two processes with command name portsentry."));
825 printf (" %s\n\n", _("Critical if < 2 or > 1024 processes")); 833 printf(" %s\n\n", _("Critical if < 2 or > 1024 processes"));
826 printf (" %s\n", "check_procs -c 1: -C sshd"); 834 printf(" %s\n", "check_procs -c 1: -C sshd");
827 printf (" %s\n", _("Critical if not at least 1 process with command sshd")); 835 printf(" %s\n", _("Critical if not at least 1 process with command sshd"));
828 printf (" %s\n", "check_procs -w 1024 -c 1: -C sshd"); 836 printf(" %s\n", "check_procs -w 1024 -c 1: -C sshd");
829 printf (" %s\n", _("Warning if > 1024 processes with command name sshd.")); 837 printf(" %s\n", _("Warning if > 1024 processes with command name sshd."));
830 printf (" %s\n\n", _("Critical if < 1 processes with command name sshd.")); 838 printf(" %s\n\n", _("Critical if < 1 processes with command name sshd."));
831 printf (" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); 839 printf(" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root");
832 printf (" %s\n", _("Warning alert if > 10 processes with command arguments containing")); 840 printf(" %s\n", _("Warning alert if > 10 processes with command arguments containing"));
833 printf (" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); 841 printf(" %s\n\n", _("'/usr/local/bin/perl' and owned by root"));
834 printf (" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); 842 printf(" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ");
835 printf (" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); 843 printf(" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K"));
836 printf (" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); 844 printf(" %s\n", "check_procs -w 10 -c 20 --metric=CPU");
837 printf (" %s\n", _("Alert if CPU of any processes over 10\% or 20\%")); 845 printf(" %s\n", _("Alert if CPU of any processes over 10%% or 20%%"));
838 846
839 printf (UT_SUPPORT); 847 printf(UT_SUPPORT);
840} 848}
841 849
842void 850void print_usage(void) {
843print_usage (void) 851 printf("%s\n", _("Usage:"));
844{ 852 printf("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname);
845 printf ("%s\n", _("Usage:")); 853 printf(" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
846 printf ("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname); 854 printf(" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
847 printf (" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
848 printf (" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
849} 855}
diff --git a/plugins/check_procs.d/config.h b/plugins/check_procs.d/config.h
new file mode 100644
index 00000000..e32ca066
--- /dev/null
+++ b/plugins/check_procs.d/config.h
@@ -0,0 +1,75 @@
1#pragma once
2
3#include "../../config.h"
4#include "regex.h"
5#include "thresholds.h"
6#include <stddef.h>
7#include <string.h>
8#include <sys/types.h>
9
10enum metric {
11 METRIC_PROCS,
12 METRIC_VSZ,
13 METRIC_RSS,
14 METRIC_CPU,
15 METRIC_ELAPSED
16};
17
18typedef struct {
19 int options; /* bitmask of filter criteria to test against */
20 enum metric metric;
21 char *metric_name;
22 char *input_filename;
23 char *prog;
24 char *args;
25 char *fmt;
26 char *fails;
27 char *exclude_progs;
28 char **exclude_progs_arr;
29 char exclude_progs_counter;
30 regex_t re_args;
31
32 bool kthread_filter;
33 bool usepid; /* whether to test for pid or /proc/pid/exe */
34 uid_t uid;
35 pid_t ppid;
36 int vsz;
37 int rss;
38 float pcpu;
39 char *statopts;
40
41 char *warning_range;
42 char *critical_range;
43 thresholds *procs_thresholds;
44} check_procs_config;
45
46check_procs_config check_procs_config_init() {
47 check_procs_config tmp = {
48 .options = 0,
49 .metric = METRIC_PROCS,
50 .metric_name = strdup("PROCS"),
51 .input_filename = NULL,
52 .prog = NULL,
53 .args = NULL,
54 .fmt = NULL,
55 .fails = NULL,
56 .exclude_progs = NULL,
57 .exclude_progs_arr = NULL,
58 .exclude_progs_counter = 0,
59 .re_args = {0},
60
61 .kthread_filter = false,
62 .usepid = false,
63 .uid = 0,
64 .ppid = 0,
65 .vsz = 0,
66 .rss = 0,
67 .pcpu = 0,
68 .statopts = NULL,
69
70 .warning_range = NULL,
71 .critical_range = NULL,
72 .procs_thresholds = NULL,
73 };
74 return tmp;
75}
diff --git a/plugins/check_radius.c b/plugins/check_radius.c
index 6b32710a..d26f7cf3 100644
--- a/plugins/check_radius.c
+++ b/plugins/check_radius.c
@@ -1,99 +1,93 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_radius plugin 3 * Monitoring check_radius plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2008 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_radius plugin 10 * This file contains the check_radius plugin
11* 11 *
12* Tests to see if a radius server is accepting connections. 12 * Tests to see if a radius server is accepting connections.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_radius"; 31const char *progname = "check_radius";
32const char *copyright = "2000-2008"; 32const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "common.h"
36#include "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
49int process_arguments (int, char **); 51typedef struct {
50void 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
81int my_rc_read_config(char *); 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)
84rc_handle *rch = NULL;
85#endif
86 89
87char *server = NULL; 90static bool verbose = false;
88char *username = NULL;
89char *password = NULL;
90char *nasid = NULL;
91char *nasipaddress = NULL;
92char *expect = NULL;
93char *config_file = NULL;
94unsigned short port = PW_AUTH_UDP_PORT;
95int retries = 1;
96bool verbose = false;
97 91
98/****************************************************************************** 92/******************************************************************************
99 93
@@ -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 15e035b6..66d07f8f 100644
--- a/plugins/check_real.c
+++ b/plugins/check_real.c
@@ -1,454 +1,445 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_real plugin 3 * Monitoring check_real plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_real plugin 10 * This file contains the check_real plugin
11* 11 *
12* This plugin tests the REAL service on the specified host. 12 * This plugin tests the REAL service on the specified host.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
32#include <stdio.h>
31const char *progname = "check_real"; 33const char *progname = "check_real";
32const char *copyright = "2000-2007"; 34const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
34 36
35#include "common.h" 37#include "common.h"
36#include "netutils.h" 38#include "netutils.h"
37#include "utils.h" 39#include "utils.h"
40#include "check_real.d/config.h"
38 41
39enum { 42#define EXPECT "RTSP/1."
40 PORT = 554 43#define URL ""
41};
42
43#define EXPECT "RTSP/1."
44#define URL ""
45
46int process_arguments (int, char **);
47int validate_arguments (void);
48void print_help (void);
49void print_usage (void);
50
51int server_port = PORT;
52char *server_address;
53char *host_name;
54char *server_url = NULL;
55char *server_expect;
56int warning_time = 0;
57bool check_warning_time = false;
58int critical_time = 0;
59bool check_critical_time = false;
60bool verbose = false;
61 44
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*/);
62 50
51static void print_help(void);
52void print_usage(void);
63 53
64int 54static bool verbose = false;
65main (int argc, char **argv)
66{
67 int sd;
68 int result = STATE_UNKNOWN;
69 char buffer[MAX_INPUT_BUFFER];
70 char *status_line = NULL;
71 55
72 setlocale (LC_ALL, ""); 56int main(int argc, char **argv) {
73 bindtextdomain (PACKAGE, LOCALEDIR); 57 setlocale(LC_ALL, "");
74 textdomain (PACKAGE); 58 bindtextdomain(PACKAGE, LOCALEDIR);
59 textdomain(PACKAGE);
75 60
76 /* Parse extra opts if any */ 61 /* Parse extra opts if any */
77 argv=np_extra_opts (&argc, argv, progname); 62 argv = np_extra_opts(&argc, argv, progname);
63
64 check_real_config_wrapper tmp_config = process_arguments(argc, argv);
65 if (tmp_config.errorcode == ERROR) {
66 usage4(_("Could not parse arguments"));
67 }
78 68
79 if (process_arguments (argc, argv) == ERROR) 69 const check_real_config config = tmp_config.config;
80 usage4 (_("Could not parse arguments"));
81 70
82 /* initialize alarm signal handling */ 71 /* initialize alarm signal handling */
83 signal (SIGALRM, socket_timeout_alarm_handler); 72 signal(SIGALRM, socket_timeout_alarm_handler);
84 73
85 /* set socket timeout */ 74 /* set socket timeout */
86 alarm (socket_timeout); 75 alarm(socket_timeout);
87 time (&start_time); 76 time(&start_time);
88 77
89 /* try to connect to the host at the given port number */ 78 /* try to connect to the host at the given port number */
90 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) 79 int socket;
91 die (STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), 80 if (my_tcp_connect(config.server_address, config.server_port, &socket) != STATE_OK) {
92 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 }
93 84
94 /* Part I - Server Check */ 85 /* Part I - Server Check */
95 86
96 /* send the OPTIONS request */ 87 /* send the OPTIONS request */
97 sprintf (buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", host_name, server_port); 88 char buffer[MAX_INPUT_BUFFER];
98 result = send (sd, buffer, strlen (buffer), 0); 89 sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", config.host_name, config.server_port);
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 }
99 94
100 /* send the header sync */ 95 /* send the header sync */
101 sprintf (buffer, "CSeq: 1\r\n"); 96 sprintf(buffer, "CSeq: 1\r\n");
102 result = send (sd, 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 }
103 101
104 /* 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 */
105 sprintf (buffer, "\r\n"); 103 sprintf(buffer, "\r\n");
106 result = send (sd, 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 }
107 108
108 /* watch for the REAL connection string */ 109 /* watch for the REAL connection string */
109 result = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0); 110 ssize_t received_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
110 111
111 /* return a CRITICAL status if we couldn't read any data */ 112 /* return a CRITICAL status if we couldn't read any data */
112 if (result == -1) 113 if (received_bytes == -1) {
113 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 }
114 116
117 mp_state_enum result = STATE_OK;
118 char *status_line = NULL;
115 /* make sure we find the response we are looking for */ 119 /* make sure we find the response we are looking for */
116 if (!strstr (buffer, server_expect)) { 120 if (!strstr(buffer, config.server_expect)) {
117 if (server_port == PORT) 121 if (config.server_port == PORT) {
118 printf ("%s\n", _("Invalid REAL response received from host")); 122 printf("%s\n", _("Invalid REAL response received from host"));
119 else 123 } else {
120 printf (_("Invalid REAL response received from host on port %d\n"), 124 printf(_("Invalid REAL response received from host on port %d\n"), config.server_port);
121 server_port); 125 }
122 } 126 } else {
123 else {
124 /* else we got the REAL string, so check the return code */ 127 /* else we got the REAL string, so check the return code */
125 128
126 time (&end_time); 129 time(&end_time);
127 130
128 result = STATE_OK; 131 result = STATE_OK;
129 132
130 status_line = (char *) strtok (buffer, "\n"); 133 status_line = strtok(buffer, "\n");
131 134
132 if (strstr (status_line, "200")) 135 if (strstr(status_line, "200")) {
133 result = STATE_OK; 136 result = STATE_OK;
137 }
134 138
135 /* client errors result in a warning state */ 139 /* client errors result in a warning state */
136 else if (strstr (status_line, "400")) 140 else if (strstr(status_line, "400")) {
137 result = STATE_WARNING; 141 result = STATE_WARNING;
138 else if (strstr (status_line, "401")) 142 } else if (strstr(status_line, "401")) {
139 result = STATE_WARNING; 143 result = STATE_WARNING;
140 else if (strstr (status_line, "402")) 144 } else if (strstr(status_line, "402")) {
141 result = STATE_WARNING; 145 result = STATE_WARNING;
142 else if (strstr (status_line, "403")) 146 } else if (strstr(status_line, "403")) {
143 result = STATE_WARNING; 147 result = STATE_WARNING;
144 else if (strstr (status_line, "404")) 148 } else if (strstr(status_line, "404")) {
145 result = STATE_WARNING; 149 result = STATE_WARNING;
146 150 } else if (strstr(status_line, "500")) {
147 /* server errors result in a critical state */ 151 /* server errors result in a critical state */
148 else if (strstr (status_line, "500"))
149 result = STATE_CRITICAL; 152 result = STATE_CRITICAL;
150 else if (strstr (status_line, "501")) 153 } else if (strstr(status_line, "501")) {
151 result = STATE_CRITICAL; 154 result = STATE_CRITICAL;
152 else if (strstr (status_line, "502")) 155 } else if (strstr(status_line, "502")) {
153 result = STATE_CRITICAL; 156 result = STATE_CRITICAL;
154 else if (strstr (status_line, "503")) 157 } else if (strstr(status_line, "503")) {
155 result = STATE_CRITICAL; 158 result = STATE_CRITICAL;
156 159 } else {
157 else
158 result = STATE_UNKNOWN; 160 result = STATE_UNKNOWN;
161 }
159 } 162 }
160 163
161 /* Part II - Check stream exists and is ok */ 164 /* Part II - Check stream exists and is ok */
162 if ((result == STATE_OK )&& (server_url != NULL) ) { 165 if ((result == STATE_OK) && (config.server_url != NULL)) {
163 166
164 /* Part I - Server Check */ 167 /* Part I - Server Check */
165 168
166 /* send the DESCRIBE request */ 169 /* send the DESCRIBE request */
167 sprintf (buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", host_name, 170 sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", config.host_name,
168 server_port, server_url); 171 config.server_port, config.server_url);
169 result = send (sd, buffer, strlen (buffer), 0); 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 }
170 177
171 /* send the header sync */ 178 /* send the header sync */
172 sprintf (buffer, "CSeq: 2\r\n"); 179 sprintf(buffer, "CSeq: 2\r\n");
173 result = send (sd, 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 }
174 184
175 /* 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 */
176 sprintf (buffer, "\r\n"); 186 sprintf(buffer, "\r\n");
177 result = send (sd, 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 }
178 191
179 /* watch for the REAL connection string */ 192 /* watch for the REAL connection string */
180 result = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0); 193 ssize_t recv_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
181 buffer[result] = '\0'; /* null terminate received buffer */ 194 if (recv_bytes == -1) {
182 195 /* return a CRITICAL status if we couldn't read any data */
183 /* return a CRITICAL status if we couldn't read any data */ 196 printf(_("No data received from host\n"));
184 if (result == -1) {
185 printf (_("No data received from host\n"));
186 result = STATE_CRITICAL; 197 result = STATE_CRITICAL;
187 } 198 } else {
188 else { 199 buffer[result] = '\0'; /* null terminate received buffer */
189 /* make sure we find the response we are looking for */ 200 /* make sure we find the response we are looking for */
190 if (!strstr (buffer, server_expect)) { 201 if (!strstr(buffer, config.server_expect)) {
191 if (server_port == PORT) 202 if (config.server_port == PORT) {
192 printf ("%s\n", _("Invalid REAL response received from host")); 203 printf("%s\n", _("Invalid REAL response received from host"));
193 else 204 } else {
194 printf (_("Invalid REAL response received from host on port %d\n"), 205 printf(_("Invalid REAL response received from host on port %d\n"),
195 server_port); 206 config.server_port);
196 } 207 }
197 else { 208 } else {
198 209
199 /* else we got the REAL string, so check the return code */ 210 /* else we got the REAL string, so check the return code */
200 211
201 time (&end_time); 212 time(&end_time);
202 213
203 result = STATE_OK; 214 result = STATE_OK;
204 215
205 status_line = (char *) strtok (buffer, "\n"); 216 status_line = strtok(buffer, "\n");
206 217
207 if (strstr (status_line, "200")) 218 if (strstr(status_line, "200")) {
208 result = STATE_OK; 219 result = STATE_OK;
220 }
209 221
210 /* client errors result in a warning state */ 222 /* client errors result in a warning state */
211 else if (strstr (status_line, "400")) 223 else if (strstr(status_line, "400")) {
212 result = STATE_WARNING; 224 result = STATE_WARNING;
213 else if (strstr (status_line, "401")) 225 } else if (strstr(status_line, "401")) {
214 result = STATE_WARNING; 226 result = STATE_WARNING;
215 else if (strstr (status_line, "402")) 227 } else if (strstr(status_line, "402")) {
216 result = STATE_WARNING; 228 result = STATE_WARNING;
217 else if (strstr (status_line, "403")) 229 } else if (strstr(status_line, "403")) {
218 result = STATE_WARNING; 230 result = STATE_WARNING;
219 else if (strstr (status_line, "404")) 231 } else if (strstr(status_line, "404")) {
220 result = STATE_WARNING; 232 result = STATE_WARNING;
233 }
221 234
222 /* server errors result in a critical state */ 235 /* server errors result in a critical state */
223 else if (strstr (status_line, "500")) 236 else if (strstr(status_line, "500")) {
224 result = STATE_CRITICAL; 237 result = STATE_CRITICAL;
225 else if (strstr (status_line, "501")) 238 } else if (strstr(status_line, "501")) {
226 result = STATE_CRITICAL; 239 result = STATE_CRITICAL;
227 else if (strstr (status_line, "502")) 240 } else if (strstr(status_line, "502")) {
228 result = STATE_CRITICAL; 241 result = STATE_CRITICAL;
229 else if (strstr (status_line, "503")) 242 } else if (strstr(status_line, "503")) {
230 result = STATE_CRITICAL; 243 result = STATE_CRITICAL;
244 }
231 245
232 else 246 else {
233 result = STATE_UNKNOWN; 247 result = STATE_UNKNOWN;
248 }
234 } 249 }
235 } 250 }
236 } 251 }
237 252
238 /* Return results */ 253 /* Return results */
239 if (result == STATE_OK) { 254 if (result == STATE_OK) {
240 255 if (config.check_critical_time && (end_time - start_time) > config.critical_time) {
241 if (check_critical_time 256 result = STATE_CRITICAL;
242 && (end_time - start_time) > critical_time) result = STATE_CRITICAL; 257 } else if (config.check_warning_time && (end_time - start_time) > config.warning_time) {
243 else if (check_warning_time 258 result = STATE_WARNING;
244 && (end_time - start_time) > warning_time) result = 259 }
245 STATE_WARNING;
246 260
247 /* Put some HTML in here to create a dynamic link */ 261 /* Put some HTML in here to create a dynamic link */
248 printf (_("REAL %s - %d second response time\n"), 262 printf(_("REAL %s - %d second response time\n"), state_text(result),
249 state_text (result), 263 (int)(end_time - start_time));
250 (int) (end_time - start_time)); 264 } else {
265 printf("%s\n", status_line);
251 } 266 }
252 else
253 printf ("%s\n", status_line);
254 267
255 /* close the connection */ 268 /* close the connection */
256 close (sd); 269 close(socket);
257 270
258 /* reset the alarm */ 271 /* reset the alarm */
259 alarm (0); 272 alarm(0);
260 273
261 return result; 274 exit(result);
262} 275}
263 276
264
265
266/* process command-line arguments */ 277/* process command-line arguments */
267int 278check_real_config_wrapper process_arguments(int argc, char **argv) {
268process_arguments (int argc, char **argv)
269{
270 int c;
271
272 int option = 0;
273 static struct option longopts[] = { 279 static struct option longopts[] = {
274 {"hostname", required_argument, 0, 'H'}, 280 {"hostname", required_argument, 0, 'H'}, {"IPaddress", required_argument, 0, 'I'},
275 {"IPaddress", required_argument, 0, 'I'}, 281 {"expect", required_argument, 0, 'e'}, {"url", required_argument, 0, 'u'},
276 {"expect", required_argument, 0, 'e'}, 282 {"port", required_argument, 0, 'p'}, {"critical", required_argument, 0, 'c'},
277 {"url", required_argument, 0, 'u'}, 283 {"warning", required_argument, 0, 'w'}, {"timeout", required_argument, 0, 't'},
278 {"port", required_argument, 0, 'p'}, 284 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
279 {"critical", required_argument, 0, 'c'}, 285 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
280 {"warning", required_argument, 0, 'w'}, 286
281 {"timeout", required_argument, 0, 't'}, 287 check_real_config_wrapper result = {
282 {"verbose", no_argument, 0, 'v'}, 288 .errorcode = OK,
283 {"version", no_argument, 0, 'V'}, 289 .config = check_real_config_init(),
284 {"help", no_argument, 0, 'h'},
285 {0, 0, 0, 0}
286 }; 290 };
287 291
288 if (argc < 2) 292 if (argc < 2) {
289 return ERROR; 293 result.errorcode = ERROR;
294 return result;
295 }
290 296
291 for (c = 1; c < argc; c++) { 297 for (int i = 1; i < argc; i++) {
292 if (strcmp ("-to", argv[c]) == 0) 298 if (strcmp("-to", argv[i]) == 0) {
293 strcpy (argv[c], "-t"); 299 strcpy(argv[i], "-t");
294 else if (strcmp ("-wt", argv[c]) == 0) 300 } else if (strcmp("-wt", argv[i]) == 0) {
295 strcpy (argv[c], "-w"); 301 strcpy(argv[i], "-w");
296 else if (strcmp ("-ct", argv[c]) == 0) 302 } else if (strcmp("-ct", argv[i]) == 0) {
297 strcpy (argv[c], "-c"); 303 strcpy(argv[i], "-c");
304 }
298 } 305 }
299 306
300 while (1) { 307 while (true) {
301 c = getopt_long (argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, 308 int option = 0;
302 &option); 309 int option_char = getopt_long(argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, &option);
303 310
304 if (c == -1 || c == EOF) 311 if (option_char == -1 || option_char == EOF) {
305 break; 312 break;
313 }
306 314
307 switch (c) { 315 switch (option_char) {
308 case 'I': /* hostname */ 316 case 'I': /* hostname */
309 case 'H': /* hostname */ 317 case 'H': /* hostname */
310 if (server_address) 318 if (result.config.server_address) {
311 break; 319 break;
312 else if (is_host (optarg)) 320 } else if (is_host(optarg)) {
313 server_address = optarg; 321 result.config.server_address = optarg;
314 else 322 } else {
315 usage2 (_("Invalid hostname/address"), optarg); 323 usage2(_("Invalid hostname/address"), optarg);
324 }
316 break; 325 break;
317 case 'e': /* string to expect in response header */ 326 case 'e': /* string to expect in response header */
318 server_expect = optarg; 327 result.config.server_expect = optarg;
319 break; 328 break;
320 case 'u': /* server URL */ 329 case 'u': /* server URL */
321 server_url = optarg; 330 result.config.server_url = optarg;
322 break; 331 break;
323 case 'p': /* port */ 332 case 'p': /* port */
324 if (is_intpos (optarg)) { 333 if (is_intpos(optarg)) {
325 server_port = atoi (optarg); 334 result.config.server_port = atoi(optarg);
326 } 335 } else {
327 else { 336 usage4(_("Port must be a positive integer"));
328 usage4 (_("Port must be a positive integer"));
329 } 337 }
330 break; 338 break;
331 case 'w': /* warning time threshold */ 339 case 'w': /* warning time threshold */
332 if (is_intnonneg (optarg)) { 340 if (is_intnonneg(optarg)) {
333 warning_time = atoi (optarg); 341 result.config.warning_time = atoi(optarg);
334 check_warning_time = true; 342 result.config.check_warning_time = true;
335 } 343 } else {
336 else { 344 usage4(_("Warning time must be a positive integer"));
337 usage4 (_("Warning time must be a positive integer"));
338 } 345 }
339 break; 346 break;
340 case 'c': /* critical time threshold */ 347 case 'c': /* critical time threshold */
341 if (is_intnonneg (optarg)) { 348 if (is_intnonneg(optarg)) {
342 critical_time = atoi (optarg); 349 result.config.critical_time = atoi(optarg);
343 check_critical_time = true; 350 result.config.check_critical_time = true;
344 } 351 } else {
345 else { 352 usage4(_("Critical time must be a positive integer"));
346 usage4 (_("Critical time must be a positive integer"));
347 } 353 }
348 break; 354 break;
349 case 'v': /* verbose */ 355 case 'v': /* verbose */
350 verbose = true; 356 verbose = true;
351 break; 357 break;
352 case 't': /* timeout */ 358 case 't': /* timeout */
353 if (is_intnonneg (optarg)) { 359 if (is_intnonneg(optarg)) {
354 socket_timeout = atoi (optarg); 360 socket_timeout = atoi(optarg);
355 } 361 } else {
356 else { 362 usage4(_("Timeout interval must be a positive integer"));
357 usage4 (_("Timeout interval must be a positive integer"));
358 } 363 }
359 break; 364 break;
360 case 'V': /* version */ 365 case 'V': /* version */
361 print_revision (progname, NP_VERSION); 366 print_revision(progname, NP_VERSION);
362 exit (STATE_UNKNOWN); 367 exit(STATE_UNKNOWN);
363 case 'h': /* help */ 368 case 'h': /* help */
364 print_help (); 369 print_help();
365 exit (STATE_UNKNOWN); 370 exit(STATE_UNKNOWN);
366 case '?': /* usage */ 371 case '?': /* usage */
367 usage5 (); 372 usage5();
368 } 373 }
369 } 374 }
370 375
371 c = optind; 376 int option_char = optind;
372 if (server_address==NULL && argc>c) { 377 if (result.config.server_address == NULL && argc > option_char) {
373 if (is_host (argv[c])) { 378 if (is_host(argv[option_char])) {
374 server_address = argv[c++]; 379 result.config.server_address = argv[option_char++];
375 } 380 } else {
376 else { 381 usage2(_("Invalid hostname/address"), argv[option_char]);
377 usage2 (_("Invalid hostname/address"), argv[c]);
378 } 382 }
379 } 383 }
380 384
381 if (server_address==NULL) 385 if (result.config.server_address == NULL) {
382 usage4 (_("You must provide a server to check")); 386 usage4(_("You must provide a server to check"));
383 387 }
384 if (host_name==NULL)
385 host_name = strdup (server_address);
386
387 if (server_expect == NULL)
388 server_expect = strdup(EXPECT);
389
390 return validate_arguments ();
391}
392 388
389 if (result.config.host_name == NULL) {
390 result.config.host_name = strdup(result.config.server_address);
391 }
393 392
393 if (result.config.server_expect == NULL) {
394 result.config.server_expect = strdup(EXPECT);
395 }
394 396
395int 397 return result;
396validate_arguments (void)
397{
398 return OK;
399} 398}
400 399
401 400void print_help(void) {
402
403void
404print_help (void)
405{
406 char *myport; 401 char *myport;
407 xasprintf (&myport, "%d", PORT); 402 xasprintf(&myport, "%d", PORT);
408 403
409 print_revision (progname, NP_VERSION); 404 print_revision(progname, NP_VERSION);
410 405
411 printf ("Copyright (c) 1999 Pedro Leite <leite@cic.ua.pt>\n"); 406 printf("Copyright (c) 1999 Pedro Leite <leite@cic.ua.pt>\n");
412 printf (COPYRIGHT, copyright, email); 407 printf(COPYRIGHT, copyright, email);
413 408
414 printf ("%s\n", _("This plugin tests the REAL service on the specified host.")); 409 printf("%s\n", _("This plugin tests the REAL service on the specified host."));
415 410
416 printf ("\n\n"); 411 printf("\n\n");
417 412
418 print_usage (); 413 print_usage();
419 414
420 printf (UT_HELP_VRSN); 415 printf(UT_HELP_VRSN);
421 printf (UT_EXTRA_OPTS); 416 printf(UT_EXTRA_OPTS);
422 417
423 printf (UT_HOST_PORT, 'p', myport); 418 printf(UT_HOST_PORT, 'p', myport);
424 419
425 printf (" %s\n", "-u, --url=STRING"); 420 printf(" %s\n", "-u, --url=STRING");
426 printf (" %s\n", _("Connect to this url")); 421 printf(" %s\n", _("Connect to this url"));
427 printf (" %s\n", "-e, --expect=STRING"); 422 printf(" %s\n", "-e, --expect=STRING");
428 printf (_("String to expect in first line of server response (default: %s)\n"), 423 printf(_("String to expect in first line of server response (default: %s)\n"), EXPECT);
429 EXPECT);
430 424
431 printf (UT_WARN_CRIT); 425 printf(UT_WARN_CRIT);
432 426
433 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 427 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
434 428
435 printf (UT_VERBOSE); 429 printf(UT_VERBOSE);
436 430
437 printf ("\n"); 431 printf("\n");
438 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."));
439 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"));
440 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,"));
441 printf ("%s\n", _("but incorrect response messages from the host result in STATE_WARNING return")); 435 printf("%s\n",
442 printf ("%s\n", _("values.")); 436 _("but incorrect response messages from the host result in STATE_WARNING return"));
437 printf("%s\n", _("values."));
443 438
444 printf (UT_SUPPORT); 439 printf(UT_SUPPORT);
445} 440}
446 441
447 442void print_usage(void) {
448 443 printf("%s\n", _("Usage:"));
449void 444 printf("%s -H host [-e expect] [-p port] [-w warn] [-c crit] [-t timeout] [-v]\n", progname);
450print_usage (void)
451{
452 printf ("%s\n", _("Usage:"));
453 printf ("%s -H host [-e expect] [-p port] [-w warn] [-c crit] [-t timeout] [-v]\n", progname);
454} 445}
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 986c3e18..83ad575c 100644
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
@@ -1,437 +1,450 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_smtp plugin 3 * Monitoring check_smtp plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2023 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_smtp plugin 10 * This file contains the check_smtp plugin
11* 11 *
12* This plugin will attempt to open an SMTP connection with the host. 12 * This plugin will attempt to open an SMTP connection with the host.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_smtp"; 31const char *progname = "check_smtp";
32const char *copyright = "2000-2007"; 32const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "common.h"
36#include "netutils.h" 36#include "netutils.h"
37#include "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
43bool check_cert = false; 64 if ((config.use_starttls || config.use_ssl) && ssl_established) {
44int 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
65#define EHLO_SUPPORTS_STARTTLS 1
66 77
67int process_arguments (int, char **); 78 return np_net_ssl_write(buf, num);
68int validate_arguments (void); 79 }
69void print_help (void); 80 return (int)send(socket_descriptor, buf, (size_t)num, 0);
70void print_usage (void); 81#else /* ifndef HAVE_SSL */
71void smtp_quit(void); 82 return send(socket_descriptor, buf, len, 0);
72int recvline(char *, size_t); 83#endif
73int recvlines(char *, size_t); 84}
74int my_close(void);
75 85
76#include "regex.h" 86static void print_help(void);
77char regex_expect[MAX_INPUT_BUFFER] = ""; 87void print_usage(void);
78regex_t preg; 88static char *smtp_quit(check_smtp_config /*config*/, char /*buffer*/[MAX_INPUT_BUFFER],
79regmatch_t pmatch[10]; 89 int /*socket_descriptor*/, bool /*ssl_established*/);
80char timestamp[20] = ""; 90static int recvline(char * /*buf*/, size_t /*bufsize*/, check_smtp_config /*config*/,
81char errbuf[MAX_INPUT_BUFFER]; 91 int /*socket_descriptor*/, bool /*ssl_established*/);
82int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 92static int recvlines(check_smtp_config /*config*/, char * /*buf*/, size_t /*bufsize*/,
83int eflags = 0; 93 int /*socket_descriptor*/, bool /*ssl_established*/);
84int errcode, excode; 94static int my_close(int /*socket_descriptor*/);
85
86int server_port = SMTP_PORT;
87int server_port_option = 0;
88char *server_address = NULL;
89char *server_expect = NULL;
90char *mail_command = NULL;
91char *from_arg = NULL;
92int send_mail_from=0;
93int ncommands=0;
94int command_size=0;
95int nresponses=0;
96int response_size=0;
97char **commands = NULL;
98char **responses = NULL;
99char *authtype = NULL;
100char *authuser = NULL;
101char *authpass = NULL;
102double warning_time = 0;
103bool check_warning_time = false;
104double critical_time = 0;
105bool check_critical_time = false;
106int verbose = 0;
107bool use_ssl = false;
108bool use_starttls = false;
109bool use_sni = false;
110bool use_proxy_prefix = false;
111bool use_ehlo = false;
112bool use_lhlo = false;
113bool ssl_established = false;
114char *localhostname = NULL;
115int sd;
116char buffer[MAX_INPUT_BUFFER];
117enum {
118 TCP_PROTOCOL = 1,
119 UDP_PROTOCOL = 2,
120};
121bool ignore_send_quit_failure = false;
122
123
124int
125main (int argc, char **argv)
126{
127 bool supports_tls = false;
128 int n = 0;
129 double elapsed_time;
130 long microsec;
131 int result = STATE_UNKNOWN;
132 char *cmd_str = NULL;
133 char *helocmd = NULL;
134 char *error_msg = "";
135 char *server_response = NULL;
136 struct timeval tv;
137 95
138 /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ 96static int verbose = 0;
139 (void) signal (SIGPIPE, SIG_IGN);
140 97
141 setlocale (LC_ALL, ""); 98int main(int argc, char **argv) {
142 bindtextdomain (PACKAGE, LOCALEDIR); 99 setlocale(LC_ALL, "");
143 textdomain (PACKAGE); 100 bindtextdomain(PACKAGE, LOCALEDIR);
101 textdomain(PACKAGE);
144 102
145 /* Parse extra opts if any */ 103 /* Parse extra opts if any */
146 argv=np_extra_opts (&argc, argv, progname); 104 argv = np_extra_opts(&argc, argv, progname);
147 105
148 if (process_arguments (argc, argv) == ERROR) 106 check_smtp_config_wrapper tmp_config = process_arguments(argc, argv);
149 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;
150 113
151 /* If localhostname not set on command line, use gethostname to set */ 114 /* If localhostname not set on command line, use gethostname to set */
152 if(! localhostname){ 115 char *localhostname = config.localhostname;
153 localhostname = malloc (HOST_MAX_BYTES); 116 if (!localhostname) {
154 if(!localhostname){ 117 localhostname = malloc(HOST_MAX_BYTES);
118 if (!localhostname) {
155 printf(_("malloc() failed!\n")); 119 printf(_("malloc() failed!\n"));
156 return STATE_CRITICAL; 120 exit(STATE_CRITICAL);
157 } 121 }
158 if(gethostname(localhostname, HOST_MAX_BYTES)){ 122 if (gethostname(localhostname, HOST_MAX_BYTES)) {
159 printf(_("gethostname() failed!\n")); 123 printf(_("gethostname() failed!\n"));
160 return STATE_CRITICAL; 124 exit(STATE_CRITICAL);
161 } 125 }
162 } 126 }
163 if(use_lhlo) 127
164 xasprintf (&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); 128 char *helocmd = NULL;
165 else if(use_ehlo) 129 if (config.use_lhlo) {
166 xasprintf (&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n"); 130 xasprintf(&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n");
167 else 131 } else if (config.use_ehlo) {
168 xasprintf (&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n"); 132 xasprintf(&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n");
169 133 } else {
170 if (verbose) 134 xasprintf(&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n");
135 }
136
137 if (verbose) {
171 printf("HELOCMD: %s", helocmd); 138 printf("HELOCMD: %s", helocmd);
139 }
172 140
141 char *mail_command = strdup("MAIL ");
142 char *cmd_str = NULL;
173 /* initialize the MAIL command with optional FROM command */ 143 /* initialize the MAIL command with optional FROM command */
174 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");
175 145
176 if (verbose && send_mail_from) 146 if (verbose && config.send_mail_from) {
177 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);
178 152
179 /* initialize alarm signal handling */ 153 /* initialize alarm signal handling */
180 (void) signal (SIGALRM, socket_timeout_alarm_handler); 154 (void)signal(SIGALRM, socket_timeout_alarm_handler);
181 155
182 /* set socket timeout */ 156 /* set socket timeout */
183 (void) alarm (socket_timeout); 157 (void)alarm(socket_timeout);
184 158
159 struct timeval start_time;
185 /* start timer */ 160 /* start timer */
186 gettimeofday (&tv, NULL); 161 gettimeofday(&start_time, NULL);
187 162
163 int socket_descriptor = 0;
188 /* try to connect to the host at the given port number */ 164 /* try to connect to the host at the given port number */
189 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);
190 167
168 char *error_msg = "";
169 char buffer[MAX_INPUT_BUFFER];
170 bool ssl_established = false;
191 if (result == STATE_OK) { /* we connected */ 171 if (result == STATE_OK) { /* we connected */
192 /* If requested, send PROXY header */ 172 /* If requested, send PROXY header */
193 if (use_proxy_prefix) { 173 if (config.use_proxy_prefix) {
194 if (verbose) 174 if (verbose) {
195 printf ("Sending header %s\n", PROXY_PREFIX); 175 printf("Sending header %s\n", PROXY_PREFIX);
196 my_send(PROXY_PREFIX, strlen(PROXY_PREFIX)); 176 }
177 my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
197 } 178 }
198 179
199#ifdef HAVE_SSL 180#ifdef HAVE_SSL
200 if (use_ssl) { 181 if (config.use_ssl) {
201 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));
202 if (result != STATE_OK) { 184 if (result != STATE_OK) {
203 printf (_("CRITICAL - Cannot create SSL context.\n")); 185 printf(_("CRITICAL - Cannot create SSL context.\n"));
204 close(sd); 186 close(socket_descriptor);
205 np_net_ssl_cleanup(); 187 np_net_ssl_cleanup();
206 return STATE_CRITICAL; 188 exit(STATE_CRITICAL);
207 } else {
208 ssl_established = 1;
209 } 189 }
190 ssl_established = true;
210 } 191 }
211#endif 192#endif
212 193
213 /* watch for the SMTP connection string and */ 194 /* watch for the SMTP connection string and */
214 /* return a WARNING status if we couldn't read any data */ 195 /* return a WARNING status if we couldn't read any data */
215 if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { 196 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
216 printf (_("recv() failed\n")); 197 printf(_("recv() failed\n"));
217 return STATE_WARNING; 198 exit(STATE_WARNING);
218 } 199 }
219 200
201 char *server_response = NULL;
220 /* save connect return (220 hostname ..) for later use */ 202 /* save connect return (220 hostname ..) for later use */
221 xasprintf(&server_response, "%s", buffer); 203 xasprintf(&server_response, "%s", buffer);
222 204
223 /* send the HELO/EHLO command */ 205 /* send the HELO/EHLO command */
224 my_send(helocmd, strlen(helocmd)); 206 my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
225 207
226 /* allow for response to helo command to reach us */ 208 /* allow for response to helo command to reach us */
227 if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { 209 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
228 printf (_("recv() failed\n")); 210 printf(_("recv() failed\n"));
229 return STATE_WARNING; 211 exit(STATE_WARNING);
230 } else if(use_ehlo || use_lhlo){ 212 }
231 if(strstr(buffer, "250 STARTTLS") != NULL || 213
232 strstr(buffer, "250-STARTTLS") != NULL){ 214 bool supports_tls = false;
233 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;
234 } 218 }
235 } 219 }
236 220
237 if(use_starttls && ! supports_tls){ 221 if (config.use_starttls && !supports_tls) {
238 printf(_("WARNING - TLS not supported by server\n")); 222 printf(_("WARNING - TLS not supported by server\n"));
239 smtp_quit(); 223 smtp_quit(config, buffer, socket_descriptor, ssl_established);
240 return STATE_WARNING; 224 exit(STATE_WARNING);
241 } 225 }
242 226
243#ifdef HAVE_SSL 227#ifdef HAVE_SSL
244 if(use_starttls) { 228 if (config.use_starttls) {
245 /* send the STARTTLS command */ 229 /* send the STARTTLS command */
246 send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); 230 send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
247 231
248 recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */ 232 recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
249 if (!strstr (buffer, SMTP_EXPECT)) { 233 ssl_established); /* wait for it */
250 printf (_("Server does not support STARTTLS\n")); 234 if (!strstr(buffer, SMTP_EXPECT)) {
251 smtp_quit(); 235 printf(_("Server does not support STARTTLS\n"));
252 return STATE_UNKNOWN; 236 smtp_quit(config, buffer, socket_descriptor, ssl_established);
253 } 237 exit(STATE_UNKNOWN);
254 result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); 238 }
255 if(result != STATE_OK) { 239
256 printf (_("CRITICAL - Cannot create SSL context.\n")); 240 result = np_net_ssl_init_with_hostname(socket_descriptor,
257 close(sd); 241 (config.use_sni ? config.server_address : NULL));
258 np_net_ssl_cleanup(); 242 if (result != STATE_OK) {
259 return STATE_CRITICAL; 243 printf(_("CRITICAL - Cannot create SSL context.\n"));
260 } else { 244 close(socket_descriptor);
261 ssl_established = 1; 245 np_net_ssl_cleanup();
262 } 246 exit(STATE_CRITICAL);
263 247 }
264 /* 248
265 * Resend the EHLO command. 249 ssl_established = true;
266 * 250
267 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge 251 /*
268 * obtained from the server, such as the list of SMTP service 252 * Resend the EHLO command.
269 * extensions, which was not obtained from the TLS negotiation 253 *
270 * itself. The client SHOULD send an EHLO command as the first 254 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
271 * command after a successful TLS negotiation.'' For this 255 * obtained from the server, such as the list of SMTP service
272 * reason, some MTAs will not allow an AUTH LOGIN command before 256 * extensions, which was not obtained from the TLS negotiation
273 * we resent EHLO via TLS. 257 * itself. The client SHOULD send an EHLO command as the first
274 */ 258 * command after a successful TLS negotiation.'' For this
275 if (my_send(helocmd, strlen(helocmd)) <= 0) { 259 * reason, some MTAs will not allow an AUTH LOGIN command before
276 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS.")); 260 * we resent EHLO via TLS.
277 my_close(); 261 */
278 return STATE_UNKNOWN; 262 if (my_send(config, helocmd, strlen(helocmd), socket_descriptor, ssl_established) <=
279 } 263 0) {
280 if (verbose) 264 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
281 printf(_("sent %s"), helocmd); 265 my_close(socket_descriptor);
282 if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 266 exit(STATE_UNKNOWN);
283 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); 267 }
284 my_close(); 268
285 return STATE_UNKNOWN; 269 if (verbose) {
286 } 270 printf(_("sent %s"), helocmd);
287 if (verbose) { 271 }
288 printf("%s", buffer);
289 }
290 272
291# ifdef USE_OPENSSL 273 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <=
292 if ( check_cert ) { 274 0) {
293 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."));
294 smtp_quit(); 276 my_close(socket_descriptor);
295 my_close(); 277 exit(STATE_UNKNOWN);
296 return result; 278 }
297 } 279
298# 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 */
299 } 293 }
300#endif 294#endif
301 295
302 if (verbose) 296 if (verbose) {
303 printf ("%s", buffer); 297 printf("%s", buffer);
298 }
304 299
305 /* save buffer for later use */ 300 /* save buffer for later use */
306 xasprintf(&server_response, "%s%s", server_response, buffer); 301 xasprintf(&server_response, "%s%s", server_response, buffer);
307 /* strip the buffer of carriage returns */ 302 /* strip the buffer of carriage returns */
308 strip (server_response); 303 strip(server_response);
309 304
310 /* make sure we find the droids we are looking for */ 305 /* make sure we find the droids we are looking for */
311 if (!strstr (server_response, server_expect)) { 306 if (!strstr(server_response, config.server_expect)) {
312 if (server_port == SMTP_PORT) 307 if (config.server_port == SMTP_PORT) {
313 printf (_("Invalid SMTP response received from host: %s\n"), server_response); 308 printf(_("Invalid SMTP response received from host: %s\n"), server_response);
314 else 309 } else {
315 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"),
316 server_port, server_response); 311 config.server_port, server_response);
317 return STATE_WARNING; 312 }
313 exit(STATE_WARNING);
318 } 314 }
319 315
320 if (send_mail_from) { 316 if (config.send_mail_from) {
321 my_send(cmd_str, strlen(cmd_str)); 317 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
322 if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) 318 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
323 printf("%s", buffer); 319 1 &&
320 verbose) {
321 printf("%s", buffer);
322 }
324 } 323 }
325 324
326 n = 0; 325 int counter = 0;
327 while (n < ncommands) { 326 while (counter < config.ncommands) {
328 xasprintf (&cmd_str, "%s%s", commands[n], "\r\n"); 327 xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
329 my_send(cmd_str, strlen(cmd_str)); 328 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
330 if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) 329 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
330 1 &&
331 verbose) {
331 printf("%s", buffer); 332 printf("%s", buffer);
332 strip (buffer); 333 }
333 if (n < nresponses) { 334 strip(buffer);
334 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 335 if (counter < config.nresponses) {
335 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];
336 if (errcode != 0) { 340 if (errcode != 0) {
337 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 341 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
338 printf (_("Could Not Compile Regular Expression")); 342 printf(_("Could Not Compile Regular Expression"));
339 return ERROR; 343 exit(STATE_UNKNOWN);
340 } 344 }
341 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);
342 if (excode == 0) { 349 if (excode == 0) {
343 result = STATE_OK; 350 result = STATE_OK;
344 } 351 } else if (excode == REG_NOMATCH) {
345 else if (excode == REG_NOMATCH) {
346 result = STATE_WARNING; 352 result = STATE_WARNING;
347 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"),
348 } 354 state_text(result), buffer, config.commands[counter]);
349 else { 355 } else {
350 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER); 356 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
351 printf (_("Execute Error: %s\n"), errbuf); 357 printf(_("Execute Error: %s\n"), errbuf);
352 result = STATE_UNKNOWN; 358 result = STATE_UNKNOWN;
353 } 359 }
354 } 360 }
355 n++; 361 counter++;
356 } 362 }
357 363
358 if (authtype != NULL) { 364 if (config.authtype != NULL) {
359 if (strcmp (authtype, "LOGIN") == 0) { 365 if (strcmp(config.authtype, "LOGIN") == 0) {
360 char *abuf; 366 char *abuf;
361 int ret; 367 int ret;
362 do { 368 do {
363 if (authuser == NULL) { 369 if (config.authuser == NULL) {
364 result = STATE_CRITICAL; 370 result = STATE_CRITICAL;
365 xasprintf(&error_msg, _("no authuser specified, ")); 371 xasprintf(&error_msg, _("no authuser specified, "));
366 break; 372 break;
367 } 373 }
368 if (authpass == NULL) { 374 if (config.authpass == NULL) {
369 result = STATE_CRITICAL; 375 result = STATE_CRITICAL;
370 xasprintf(&error_msg, _("no authpass specified, ")); 376 xasprintf(&error_msg, _("no authpass specified, "));
371 break; 377 break;
372 } 378 }
373 379
374 /* send AUTH LOGIN */ 380 /* send AUTH LOGIN */
375 my_send(SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN)); 381 my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
376 if (verbose) 382 ssl_established);
377 printf (_("sent %s\n"), "AUTH LOGIN"); 383 if (verbose) {
384 printf(_("sent %s\n"), "AUTH LOGIN");
385 }
378 386
379 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 387 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
388 ssl_established)) <= 0) {
380 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, ")); 389 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
381 result = STATE_WARNING; 390 result = STATE_WARNING;
382 break; 391 break;
383 } 392 }
384 if (verbose) 393 if (verbose) {
385 printf (_("received %s\n"), buffer); 394 printf(_("received %s\n"), buffer);
395 }
386 396
387 if (strncmp (buffer, "334", 3) != 0) { 397 if (strncmp(buffer, "334", 3) != 0) {
388 result = STATE_CRITICAL; 398 result = STATE_CRITICAL;
389 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, ")); 399 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
390 break; 400 break;
391 } 401 }
392 402
393 /* encode authuser with base64 */ 403 /* encode authuser with base64 */
394 base64_encode_alloc (authuser, strlen(authuser), &abuf); 404 base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
395 xasprintf(&abuf, "%s\r\n", abuf); 405 xasprintf(&abuf, "%s\r\n", abuf);
396 my_send(abuf, strlen(abuf)); 406 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
397 if (verbose) 407 if (verbose) {
398 printf (_("sent %s\n"), abuf); 408 printf(_("sent %s\n"), abuf);
409 }
399 410
400 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 411 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
412 ssl_established)) <= 0) {
401 result = STATE_CRITICAL; 413 result = STATE_CRITICAL;
402 xasprintf(&error_msg, _("recv() failed after sending authuser, ")); 414 xasprintf(&error_msg, _("recv() failed after sending authuser, "));
403 break; 415 break;
404 } 416 }
405 if (verbose) { 417 if (verbose) {
406 printf (_("received %s\n"), buffer); 418 printf(_("received %s\n"), buffer);
407 } 419 }
408 if (strncmp (buffer, "334", 3) != 0) { 420 if (strncmp(buffer, "334", 3) != 0) {
409 result = STATE_CRITICAL; 421 result = STATE_CRITICAL;
410 xasprintf(&error_msg, _("invalid response received after authuser, ")); 422 xasprintf(&error_msg, _("invalid response received after authuser, "));
411 break; 423 break;
412 } 424 }
413 /* encode authpass with base64 */ 425 /* encode authpass with base64 */
414 base64_encode_alloc (authpass, strlen(authpass), &abuf); 426 base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
415 xasprintf(&abuf, "%s\r\n", abuf); 427 xasprintf(&abuf, "%s\r\n", abuf);
416 my_send(abuf, strlen(abuf)); 428 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
417 if (verbose) { 429 if (verbose) {
418 printf (_("sent %s\n"), abuf); 430 printf(_("sent %s\n"), abuf);
419 } 431 }
420 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 432 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
433 ssl_established)) <= 0) {
421 result = STATE_CRITICAL; 434 result = STATE_CRITICAL;
422 xasprintf(&error_msg, _("recv() failed after sending authpass, ")); 435 xasprintf(&error_msg, _("recv() failed after sending authpass, "));
423 break; 436 break;
424 } 437 }
425 if (verbose) { 438 if (verbose) {
426 printf (_("received %s\n"), buffer); 439 printf(_("received %s\n"), buffer);
427 } 440 }
428 if (strncmp (buffer, "235", 3) != 0) { 441 if (strncmp(buffer, "235", 3) != 0) {
429 result = STATE_CRITICAL; 442 result = STATE_CRITICAL;
430 xasprintf(&error_msg, _("invalid response received after authpass, ")); 443 xasprintf(&error_msg, _("invalid response received after authpass, "));
431 break; 444 break;
432 } 445 }
433 break; 446 break;
434 } while (0); 447 } while (false);
435 } else { 448 } else {
436 result = STATE_CRITICAL; 449 result = STATE_CRITICAL;
437 xasprintf(&error_msg, _("only authtype LOGIN is supported, ")); 450 xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
@@ -439,243 +452,249 @@ main (int argc, char **argv)
439 } 452 }
440 453
441 /* tell the server we're done */ 454 /* tell the server we're done */
442 smtp_quit(); 455 smtp_quit(config, buffer, socket_descriptor, ssl_established);
443 456
444 /* finally close the connection */ 457 /* finally close the connection */
445 close (sd); 458 close(socket_descriptor);
446 } 459 }
447 460
448 /* reset the alarm */ 461 /* reset the alarm */
449 alarm (0); 462 alarm(0);
450 463
451 microsec = deltime (tv); 464 long microsec = deltime(start_time);
452 elapsed_time = (double)microsec / 1.0e6; 465 double elapsed_time = (double)microsec / 1.0e6;
453 466
454 if (result == STATE_OK) { 467 if (result == STATE_OK) {
455 if (check_critical_time && elapsed_time > critical_time) 468 if (config.check_critical_time && elapsed_time > config.critical_time) {
456 result = STATE_CRITICAL; 469 result = STATE_CRITICAL;
457 else if (check_warning_time && elapsed_time > warning_time) 470 } else if (config.check_warning_time && elapsed_time > config.warning_time) {
458 result = STATE_WARNING; 471 result = STATE_WARNING;
472 }
459 } 473 }
460 474
461 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,
462 state_text (result), 476 elapsed_time, verbose ? ", " : "", verbose ? buffer : "",
463 error_msg, 477 fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time,
464 elapsed_time, 478 config.check_critical_time, config.critical_time, true, 0, false, 0));
465 verbose?", ":"", verbose?buffer:"",
466 fperfdata ("time", elapsed_time, "s",
467 (int)check_warning_time, warning_time,
468 (int)check_critical_time, critical_time,
469 true, 0, false, 0));
470 479
471 return result; 480 exit(result);
472} 481}
473 482
474
475
476/* process command-line arguments */ 483/* process command-line arguments */
477int 484check_smtp_config_wrapper process_arguments(int argc, char **argv) {
478process_arguments (int argc, char **argv)
479{
480 int c;
481 char* temp;
482
483 bool implicit_tls = false;
484
485 enum { 485 enum {
486 SNI_OPTION 486 SNI_OPTION = CHAR_MAX + 1
487 }; 487 };
488 488
489 int option = 0; 489 int option = 0;
490 static struct option longopts[] = { 490 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
491 {"hostname", required_argument, 0, 'H'}, 491 {"expect", required_argument, 0, 'e'},
492 {"expect", required_argument, 0, 'e'}, 492 {"critical", required_argument, 0, 'c'},
493 {"critical", required_argument, 0, 'c'}, 493 {"warning", required_argument, 0, 'w'},
494 {"warning", required_argument, 0, 'w'}, 494 {"timeout", required_argument, 0, 't'},
495 {"timeout", required_argument, 0, 't'}, 495 {"port", required_argument, 0, 'p'},
496 {"port", required_argument, 0, 'p'}, 496 {"from", required_argument, 0, 'f'},
497 {"from", required_argument, 0, 'f'}, 497 {"fqdn", required_argument, 0, 'F'},
498 {"fqdn", required_argument, 0, 'F'}, 498 {"authtype", required_argument, 0, 'A'},
499 {"authtype", required_argument, 0, 'A'}, 499 {"authuser", required_argument, 0, 'U'},
500 {"authuser", required_argument, 0, 'U'}, 500 {"authpass", required_argument, 0, 'P'},
501 {"authpass", required_argument, 0, 'P'}, 501 {"command", required_argument, 0, 'C'},
502 {"command", required_argument, 0, 'C'}, 502 {"response", required_argument, 0, 'R'},
503 {"response", required_argument, 0, 'R'}, 503 {"verbose", no_argument, 0, 'v'},
504 {"verbose", no_argument, 0, 'v'}, 504 {"version", no_argument, 0, 'V'},
505 {"version", no_argument, 0, 'V'}, 505 {"use-ipv4", no_argument, 0, '4'},
506 {"use-ipv4", no_argument, 0, '4'}, 506 {"use-ipv6", no_argument, 0, '6'},
507 {"use-ipv6", no_argument, 0, '6'}, 507 {"help", no_argument, 0, 'h'},
508 {"help", no_argument, 0, 'h'}, 508 {"lmtp", no_argument, 0, 'L'},
509 {"lmtp", no_argument, 0, 'L'}, 509 {"ssl", no_argument, 0, 's'},
510 {"ssl", no_argument, 0, 's'}, 510 {"tls", no_argument, 0, 's'},
511 {"tls", no_argument, 0, 's'}, 511 {"starttls", no_argument, 0, 'S'},
512 {"starttls",no_argument,0,'S'}, 512 {"sni", no_argument, 0, SNI_OPTION},
513 {"sni", no_argument, 0, SNI_OPTION}, 513 {"certificate", required_argument, 0, 'D'},
514 {"certificate",required_argument,0,'D'}, 514 {"ignore-quit-failure", no_argument, 0, 'q'},
515 {"ignore-quit-failure",no_argument,0,'q'}, 515 {"proxy", no_argument, 0, 'r'},
516 {"proxy",no_argument,0,'r'}, 516 {0, 0, 0, 0}};
517 {0, 0, 0, 0} 517
518 check_smtp_config_wrapper result = {
519 .config = check_smtp_config_init(),
520 .errorcode = OK,
518 }; 521 };
519 522
520 if (argc < 2) 523 if (argc < 2) {
521 return ERROR; 524 result.errorcode = ERROR;
525 return result;
526 }
522 527
523 for (c = 1; c < argc; c++) { 528 for (int index = 1; index < argc; index++) {
524 if (strcmp ("-to", argv[c]) == 0) 529 if (strcmp("-to", argv[index]) == 0) {
525 strcpy (argv[c], "-t"); 530 strcpy(argv[index], "-t");
526 else if (strcmp ("-wt", argv[c]) == 0) 531 } else if (strcmp("-wt", argv[index]) == 0) {
527 strcpy (argv[c], "-w"); 532 strcpy(argv[index], "-w");
528 else if (strcmp ("-ct", argv[c]) == 0) 533 } else if (strcmp("-ct", argv[index]) == 0) {
529 strcpy (argv[c], "-c"); 534 strcpy(argv[index], "-c");
535 }
530 } 536 }
531 537
532 while (1) { 538 int command_size = 0;
533 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;
534 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);
535 545
536 if (c == -1 || c == EOF) 546 if (opt_index == -1 || opt_index == EOF) {
537 break; 547 break;
548 }
538 549
539 switch (c) { 550 switch (opt_index) {
540 case 'H': /* hostname */ 551 case 'H': /* hostname */
541 if (is_host (optarg)) { 552 if (is_host(optarg)) {
542 server_address = optarg; 553 result.config.server_address = optarg;
543 } 554 } else {
544 else { 555 usage2(_("Invalid hostname/address"), optarg);
545 usage2 (_("Invalid hostname/address"), optarg);
546 } 556 }
547 break; 557 break;
548 case 'p': /* port */ 558 case 'p': /* port */
549 if (is_intpos (optarg)) 559 if (is_intpos(optarg)) {
550 server_port_option = atoi (optarg); 560 server_port_option = atoi(optarg);
551 else 561 } else {
552 usage4 (_("Port must be a positive integer")); 562 usage4(_("Port must be a positive integer"));
563 }
553 break; 564 break;
554 case 'F': 565 case 'F':
555 /* localhostname */ 566 /* localhostname */
556 localhostname = strdup(optarg); 567 result.config.localhostname = strdup(optarg);
557 break; 568 break;
558 case 'f': /* from argument */ 569 case 'f': /* from argument */
559 from_arg = optarg + strspn(optarg, "<"); 570 result.config.from_arg = optarg + strspn(optarg, "<");
560 from_arg = strndup(from_arg, strcspn(from_arg, ">")); 571 result.config.from_arg =
561 send_mail_from = 1; 572 strndup(result.config.from_arg, strcspn(result.config.from_arg, ">"));
573 result.config.send_mail_from = true;
562 break; 574 break;
563 case 'A': 575 case 'A':
564 authtype = optarg; 576 result.config.authtype = optarg;
565 use_ehlo = true; 577 result.config.use_ehlo = true;
566 break; 578 break;
567 case 'U': 579 case 'U':
568 authuser = optarg; 580 result.config.authuser = optarg;
569 break; 581 break;
570 case 'P': 582 case 'P':
571 authpass = optarg; 583 result.config.authpass = optarg;
572 break; 584 break;
573 case 'e': /* server expect string on 220 */ 585 case 'e': /* server expect string on 220 */
574 server_expect = optarg; 586 result.config.server_expect = optarg;
575 break; 587 break;
576 case 'C': /* commands */ 588 case 'C': /* commands */
577 if (ncommands >= command_size) { 589 if (result.config.ncommands >= command_size) {
578 command_size+=8; 590 command_size += 8;
579 commands = realloc (commands, sizeof(char *) * command_size); 591 result.config.commands =
580 if (commands == NULL) 592 realloc(result.config.commands, sizeof(char *) * command_size);
581 die (STATE_UNKNOWN, 593 if (result.config.commands == NULL) {
582 _("Could not realloc() units [%d]\n"), ncommands); 594 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
595 result.config.ncommands);
596 }
583 } 597 }
584 commands[ncommands] = (char *) malloc (sizeof(char) * 255); 598 result.config.commands[result.config.ncommands] = (char *)malloc(sizeof(char) * 255);
585 strncpy (commands[ncommands], optarg, 255); 599 strncpy(result.config.commands[result.config.ncommands], optarg, 255);
586 ncommands++; 600 result.config.ncommands++;
587 break; 601 break;
588 case 'R': /* server responses */ 602 case 'R': /* server responses */
589 if (nresponses >= response_size) { 603 if (result.config.nresponses >= response_size) {
590 response_size += 8; 604 response_size += 8;
591 responses = realloc (responses, sizeof(char *) * response_size); 605 result.config.responses =
592 if (responses == NULL) 606 realloc(result.config.responses, sizeof(char *) * response_size);
593 die (STATE_UNKNOWN, 607 if (result.config.responses == NULL) {
594 _("Could not realloc() units [%d]\n"), nresponses); 608 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
609 result.config.nresponses);
610 }
595 } 611 }
596 responses[nresponses] = (char *) malloc (sizeof(char) * 255); 612 result.config.responses[result.config.nresponses] = (char *)malloc(sizeof(char) * 255);
597 strncpy (responses[nresponses], optarg, 255); 613 strncpy(result.config.responses[result.config.nresponses], optarg, 255);
598 nresponses++; 614 result.config.nresponses++;
599 break; 615 break;
600 case 'c': /* critical time threshold */ 616 case 'c': /* critical time threshold */
601 if (!is_nonnegative (optarg)) 617 if (!is_nonnegative(optarg)) {
602 usage4 (_("Critical time must be a positive")); 618 usage4(_("Critical time must be a positive"));
603 else { 619 } else {
604 critical_time = strtod (optarg, NULL); 620 result.config.critical_time = strtod(optarg, NULL);
605 check_critical_time = true; 621 result.config.check_critical_time = true;
606 } 622 }
607 break; 623 break;
608 case 'w': /* warning time threshold */ 624 case 'w': /* warning time threshold */
609 if (!is_nonnegative (optarg)) 625 if (!is_nonnegative(optarg)) {
610 usage4 (_("Warning time must be a positive")); 626 usage4(_("Warning time must be a positive"));
611 else { 627 } else {
612 warning_time = strtod (optarg, NULL); 628 result.config.warning_time = strtod(optarg, NULL);
613 check_warning_time = true; 629 result.config.check_warning_time = true;
614 } 630 }
615 break; 631 break;
616 case 'v': /* verbose */ 632 case 'v': /* verbose */
617 verbose++; 633 verbose++;
618 break; 634 break;
619 case 'q': 635 case 'q':
620 ignore_send_quit_failure = true; /* ignore problem sending QUIT */ 636 result.config.ignore_send_quit_failure = true; /* ignore problem sending QUIT */
621 break; 637 break;
622 case 't': /* timeout */ 638 case 't': /* timeout */
623 if (is_intnonneg (optarg)) { 639 if (is_intnonneg(optarg)) {
624 socket_timeout = atoi (optarg); 640 socket_timeout = atoi(optarg);
625 } 641 } else {
626 else { 642 usage4(_("Timeout interval must be a positive integer"));
627 usage4 (_("Timeout interval must be a positive integer"));
628 } 643 }
629 break; 644 break;
630 case 'D': 645 case 'D': {
631 /* Check SSL cert validity */ 646 /* Check SSL cert validity */
632#ifdef USE_OPENSSL 647#ifdef USE_OPENSSL
633 if ((temp=strchr(optarg,','))!=NULL) { 648 char *temp;
634 *temp='\0'; 649 if ((temp = strchr(optarg, ',')) != NULL) {
635 if (!is_intnonneg (optarg)) 650 *temp = '\0';
636 usage2 ("Invalid certificate expiration period", optarg); 651 if (!is_intnonneg(optarg)) {
637 days_till_exp_warn = atoi(optarg); 652 usage2("Invalid certificate expiration period", optarg);
638 *temp=','; 653 }
639 temp++; 654 result.config.days_till_exp_warn = atoi(optarg);
640 if (!is_intnonneg (temp)) 655 *temp = ',';
641 usage2 (_("Invalid certificate expiration period"), temp); 656 temp++;
642 days_till_exp_crit = atoi (temp); 657 if (!is_intnonneg(temp)) {
643 } 658 usage2(_("Invalid certificate expiration period"), temp);
644 else { 659 }
645 days_till_exp_crit=0; 660 result.config.days_till_exp_crit = atoi(temp);
646 if (!is_intnonneg (optarg)) 661 } else {
647 usage2 ("Invalid certificate expiration period", optarg); 662 result.config.days_till_exp_crit = 0;
648 days_till_exp_warn = atoi (optarg); 663 if (!is_intnonneg(optarg)) {
649 } 664 usage2("Invalid certificate expiration period", optarg);
650 check_cert = true; 665 }
651 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;
652#else 670#else
653 usage (_("SSL support not available - install OpenSSL and recompile")); 671 usage(_("SSL support not available - install OpenSSL and recompile"));
654#endif 672#endif
655 implicit_tls = true; 673 implicit_tls = true;
656 // fallthrough 674 // fallthrough
657 case 's': 675 case 's':
658 /* ssl */ 676 /* ssl */
659 use_ssl = true; 677 result.config.use_ssl = true;
660 server_port = SMTPS_PORT; 678 result.config.server_port = SMTPS_PORT;
661 break; 679 break;
662 case 'S': 680 case 'S':
663 /* starttls */ 681 /* starttls */
664 use_starttls = true; 682 result.config.use_starttls = true;
665 use_ehlo = true; 683 result.config.use_ehlo = true;
666 break; 684 break;
685 }
667 case SNI_OPTION: 686 case SNI_OPTION:
668#ifdef HAVE_SSL 687#ifdef HAVE_SSL
669 use_sni = true; 688 result.config.use_sni = true;
670#else 689#else
671 usage (_("SSL support not available - install OpenSSL and recompile")); 690 usage(_("SSL support not available - install OpenSSL and recompile"));
672#endif 691#endif
673 break; 692 break;
674 case 'r': 693 case 'r':
675 use_proxy_prefix = true; 694 result.config.use_proxy_prefix = true;
676 break; 695 break;
677 case 'L': 696 case 'L':
678 use_lhlo = true; 697 result.config.use_lhlo = true;
679 break; 698 break;
680 case '4': 699 case '4':
681 address_family = AF_INET; 700 address_family = AF_INET;
@@ -684,102 +703,81 @@ process_arguments (int argc, char **argv)
684#ifdef USE_IPV6 703#ifdef USE_IPV6
685 address_family = AF_INET6; 704 address_family = AF_INET6;
686#else 705#else
687 usage4 (_("IPv6 support not available")); 706 usage4(_("IPv6 support not available"));
688#endif 707#endif
689 break; 708 break;
690 case 'V': /* version */ 709 case 'V': /* version */
691 print_revision (progname, NP_VERSION); 710 print_revision(progname, NP_VERSION);
692 exit (STATE_UNKNOWN); 711 exit(STATE_UNKNOWN);
693 case 'h': /* help */ 712 case 'h': /* help */
694 print_help (); 713 print_help();
695 exit (STATE_UNKNOWN); 714 exit(STATE_UNKNOWN);
696 case '?': /* help */ 715 case '?': /* help */
697 usage5 (); 716 usage5();
698 } 717 }
699 } 718 }
700 719
701 c = optind; 720 int c = optind;
702 if (server_address == NULL) { 721 if (result.config.server_address == NULL) {
703 if (argv[c]) { 722 if (argv[c]) {
704 if (is_host (argv[c])) 723 if (is_host(argv[c])) {
705 server_address = argv[c]; 724 result.config.server_address = argv[c];
706 else 725 } else {
707 usage2 (_("Invalid hostname/address"), argv[c]); 726 usage2(_("Invalid hostname/address"), argv[c]);
708 } 727 }
709 else { 728 } else {
710 xasprintf (&server_address, "127.0.0.1"); 729 result.config.server_address = strdup("localhost");
711 } 730 }
712 } 731 }
713 732
714 if (server_expect == NULL) 733 if (result.config.use_starttls && result.config.use_ssl) {
715 server_expect = strdup (SMTP_EXPECT);
716
717 if (mail_command == NULL)
718 mail_command = strdup("MAIL ");
719
720 if (from_arg==NULL)
721 from_arg = strdup(" ");
722
723 if (use_starttls && use_ssl) {
724 if (implicit_tls) { 734 if (implicit_tls) {
725 use_ssl = false; 735 result.config.use_ssl = false;
726 server_port = SMTP_PORT;
727 } else { 736 } else {
728 usage4 (_("Set either -s/--ssl/--tls or -S/--starttls")); 737 usage4(_("Set either -s/--ssl/--tls or -S/--starttls"));
729 } 738 }
730 } 739 }
731 740
732 if (server_port_option != 0) { 741 if (server_port_option != 0) {
733 server_port = server_port_option; 742 result.config.server_port = server_port_option;
734 } 743 }
735 744
736 return validate_arguments (); 745 return result;
737}
738
739
740
741int
742validate_arguments (void)
743{
744 return OK;
745} 746}
746 747
747 748char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int socket_descriptor,
748void 749 bool ssl_established) {
749smtp_quit(void) 750 int sent_bytes =
750{ 751 my_send(config, SMTP_QUIT, strlen(SMTP_QUIT), socket_descriptor, ssl_established);
751 int bytes; 752 if (sent_bytes < 0) {
752 int n; 753 if (config.ignore_send_quit_failure) {
753 754 if (verbose) {
754 n = my_send(SMTP_QUIT, strlen(SMTP_QUIT));
755 if(n < 0) {
756 if(ignore_send_quit_failure) {
757 if(verbose) {
758 printf(_("Connection closed by server before sending QUIT command\n")); 755 printf(_("Connection closed by server before sending QUIT command\n"));
759 } 756 }
760 return; 757 return buffer;
761 } 758 }
762 die (STATE_UNKNOWN, 759 die(STATE_UNKNOWN, _("Connection closed by server before sending QUIT command\n"));
763 _("Connection closed by server before sending QUIT command\n"));
764 } 760 }
765 761
766 if (verbose) 762 if (verbose) {
767 printf(_("sent %s\n"), "QUIT"); 763 printf(_("sent %s\n"), "QUIT");
764 }
768 765
769 /* read the response but don't care about problems */ 766 /* read the response but don't care about problems */
770 bytes = recvlines(buffer, MAX_INPUT_BUFFER); 767 int bytes = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established);
771 if (verbose) { 768 if (verbose) {
772 if (bytes < 0) 769 if (bytes < 0) {
773 printf(_("recv() failed after QUIT.")); 770 printf(_("recv() failed after QUIT."));
774 else if (bytes == 0) 771 } else if (bytes == 0) {
775 printf(_("Connection reset by peer.")); 772 printf(_("Connection reset by peer."));
776 else { 773 } else {
777 buffer[bytes] = '\0'; 774 buffer[bytes] = '\0';
778 printf(_("received %s\n"), buffer); 775 printf(_("received %s\n"), buffer);
779 } 776 }
780 } 777 }
781}
782 778
779 return buffer;
780}
783 781
784/* 782/*
785 * 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
@@ -790,24 +788,23 @@ smtp_quit(void)
790 * 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
791 * 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.
792 */ 790 */
793int 791int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor,
794recvline(char *buf, size_t bufsize) 792 bool ssl_established) {
795{
796 int result; 793 int result;
797 unsigned i; 794 int counter;
798 795
799 for (i = result = 0; i < bufsize - 1; i++) { 796 for (counter = result = 0; counter < bufsize - 1; counter++) {
800 if ((result = my_recv(&buf[i], 1)) != 1) 797 if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) {
801 break; 798 break;
802 if (buf[i] == '\n') { 799 }
803 buf[++i] = '\0'; 800 if (buf[counter] == '\n') {
804 return i; 801 buf[++counter] = '\0';
802 return counter;
805 } 803 }
806 } 804 }
807 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 */
808} 806}
809 807
810
811/* 808/*
812 * 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
813 * 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
@@ -822,117 +819,110 @@ recvline(char *buf, size_t bufsize)
822 * 819 *
823 * 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.
824 */ 821 */
825int 822int recvlines(check_smtp_config config, char *buf, size_t bufsize, int socket_descriptor,
826recvlines(char *buf, size_t bufsize) 823 bool ssl_established) {
827{ 824 int result;
828 int result, i; 825 int counter;
829 826
830 for (i = 0; /* forever */; i += result) 827 for (counter = 0; /* forever */; counter += result) {
831 if (!((result = recvline(buf + i, bufsize - i)) > 3 && 828 if (!((result = recvline(buf + counter, bufsize - counter, config, socket_descriptor,
832 isdigit((int)buf[i]) && 829 ssl_established)) > 3 &&
833 isdigit((int)buf[i + 1]) && 830 isdigit((int)buf[counter]) && isdigit((int)buf[counter + 1]) &&
834 isdigit((int)buf[i + 2]) && 831 isdigit((int)buf[counter + 2]) && buf[counter + 3] == '-')) {
835 buf[i + 3] == '-'))
836 break; 832 break;
833 }
834 }
837 835
838 return (result <= 0) ? result : result + i; 836 return (result <= 0) ? result : result + counter;
839} 837}
840 838
841 839int my_close(int socket_descriptor) {
842int
843my_close (void)
844{
845 int result; 840 int result;
846 result = close(sd); 841 result = close(socket_descriptor);
847#ifdef HAVE_SSL 842#ifdef HAVE_SSL
848 np_net_ssl_cleanup(); 843 np_net_ssl_cleanup();
849#endif 844#endif
850 return result; 845 return result;
851} 846}
852 847
853 848void print_help(void) {
854void
855print_help (void)
856{
857 char *myport; 849 char *myport;
858 xasprintf (&myport, "%d", SMTP_PORT); 850 xasprintf(&myport, "%d", SMTP_PORT);
859 851
860 print_revision (progname, NP_VERSION); 852 print_revision(progname, NP_VERSION);
861 853
862 printf ("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n"); 854 printf("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n");
863 printf (COPYRIGHT, copyright, email); 855 printf(COPYRIGHT, copyright, email);
864 856
865 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."));
866 858
867 printf ("\n\n"); 859 printf("\n\n");
868 860
869 print_usage (); 861 print_usage();
870 862
871 printf (UT_HELP_VRSN); 863 printf(UT_HELP_VRSN);
872 printf (UT_EXTRA_OPTS); 864 printf(UT_EXTRA_OPTS);
873 865
874 printf (UT_HOST_PORT, 'p', myport); 866 printf(UT_HOST_PORT, 'p', myport);
875 867
876 printf (UT_IPv46); 868 printf(UT_IPv46);
877 869
878 printf (" %s\n", "-e, --expect=STRING"); 870 printf(" %s\n", "-e, --expect=STRING");
879 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"),
880 printf (" %s\n", "-C, --command=STRING"); 872 SMTP_EXPECT);
881 printf (" %s\n", _("SMTP command (may be used repeatedly)")); 873 printf(" %s\n", "-C, --command=STRING");
882 printf (" %s\n", "-R, --response=STRING"); 874 printf(" %s\n", _("SMTP command (may be used repeatedly)"));
883 printf (" %s\n", _("Expected response to command (may be used repeatedly)")); 875 printf(" %s\n", "-R, --response=STRING");
884 printf (" %s\n", "-f, --from=STRING"); 876 printf(" %s\n", _("Expected response to command (may be used repeatedly)"));
885 printf (" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")), 877 printf(" %s\n", "-f, --from=STRING");
886 printf (" %s\n", "-F, --fqdn=STRING"); 878 printf(" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")),
887 printf (" %s\n", _("FQDN used for HELO")); 879 printf(" %s\n", "-F, --fqdn=STRING");
888 printf (" %s\n", "-r, --proxy"); 880 printf(" %s\n", _("FQDN used for HELO"));
889 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."));
890#ifdef HAVE_SSL 883#ifdef HAVE_SSL
891 printf (" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); 884 printf(" %s\n", "-D, --certificate=INTEGER[,INTEGER]");
892 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."));
893 printf (" %s\n", "-s, --ssl, --tls"); 886 printf(" %s\n", "-s, --ssl, --tls");
894 printf (" %s\n", _("Use SSL/TLS for the connection.")); 887 printf(" %s\n", _("Use SSL/TLS for the connection."));
895 printf (_(" Sets default port to %d.\n"), SMTPS_PORT); 888 printf(_(" Sets default port to %d.\n"), SMTPS_PORT);
896 printf (" %s\n", "-S, --starttls"); 889 printf(" %s\n", "-S, --starttls");
897 printf (" %s\n", _("Use STARTTLS for the connection.")); 890 printf(" %s\n", _("Use STARTTLS for the connection."));
898 printf (" %s\n", "--sni"); 891 printf(" %s\n", "--sni");
899 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 892 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
900#endif 893#endif
901 894
902 printf (" %s\n", "-A, --authtype=STRING"); 895 printf(" %s\n", "-A, --authtype=STRING");
903 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)"));
904 printf (" %s\n", "-U, --authuser=STRING"); 897 printf(" %s\n", "-U, --authuser=STRING");
905 printf (" %s\n", _("SMTP AUTH username")); 898 printf(" %s\n", _("SMTP AUTH username"));
906 printf (" %s\n", "-P, --authpass=STRING"); 899 printf(" %s\n", "-P, --authpass=STRING");
907 printf (" %s\n", _("SMTP AUTH password")); 900 printf(" %s\n", _("SMTP AUTH password"));
908 printf (" %s\n", "-L, --lmtp"); 901 printf(" %s\n", "-L, --lmtp");
909 printf (" %s\n", _("Send LHLO instead of HELO/EHLO")); 902 printf(" %s\n", _("Send LHLO instead of HELO/EHLO"));
910 printf (" %s\n", "-q, --ignore-quit-failure"); 903 printf(" %s\n", "-q, --ignore-quit-failure");
911 printf (" %s\n", _("Ignore failure when sending QUIT command to server")); 904 printf(" %s\n", _("Ignore failure when sending QUIT command to server"));
912
913 printf (UT_WARN_CRIT);
914 905
915 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 906 printf(UT_WARN_CRIT);
916 907
917 printf (UT_VERBOSE); 908 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
909
910 printf(UT_VERBOSE);
918 911
919 printf("\n"); 912 printf("\n");
920 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"));
921 printf ("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful")); 914 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful"));
922 printf ("%s\n", _("connects, but incorrect response messages from the host result in")); 915 printf("%s\n", _("connects, but incorrect response messages from the host result in"));
923 printf ("%s\n", _("STATE_WARNING return values.")); 916 printf("%s\n", _("STATE_WARNING return values."));
924 917
925 printf (UT_SUPPORT); 918 printf(UT_SUPPORT);
926} 919}
927 920
928 921void print_usage(void) {
929 922 printf("%s\n", _("Usage:"));
930void 923 printf("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n",
931print_usage (void) 924 progname);
932{ 925 printf("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n");
933 printf ("%s\n", _("Usage:")); 926 printf("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] "
934 printf ("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n", progname); 927 "[-v] \n");
935 printf ("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n");
936 printf ("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] [-v] \n");
937} 928}
938
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 937b3a5d..a5a7afe8 100644
--- a/plugins/check_snmp.c
+++ b/plugins/check_snmp.c
@@ -1,699 +1,397 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_snmp plugin 3 * Monitoring check_snmp plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_snmp plugin 10 * This file contains the check_snmp plugin
11* 11 *
12* Check status of remote machines and obtain system information via SNMP 12 * Check status of remote machines and obtain system information via SNMP
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_snmp"; 31const char *progname = "check_snmp";
32const char *copyright = "1999-2007"; 32const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "./common.h"
36#include "runcmd.h" 36#include "./runcmd.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_cmd.h" 38#include "../lib/states.h"
39 39
40#define DEFAULT_COMMUNITY "public" 40#include "../lib/utils_base.h"
41#define DEFAULT_PORT "161" 41#include "../lib/output.h"
42#define DEFAULT_MIBLIST "ALL" 42#include "check_snmp.d/check_snmp_helpers.h"
43#define DEFAULT_PROTOCOL "1" 43
44#define DEFAULT_RETRIES 5 44#include <bits/getopt_core.h>
45#include <bits/getopt_ext.h>
46#include <strings.h>
47#include <stdint.h>
48
49#include "check_snmp.d/config.h"
50#include <stdlib.h>
51#include <arpa/inet.h>
52#include <net-snmp/library/parse.h>
53#include <net-snmp/net-snmp-config.h>
54#include <net-snmp/net-snmp-includes.h>
55#include <net-snmp/library/snmp.h>
56#include <net-snmp/library/keytools.h>
57#include <net-snmp/library/snmp_api.h>
58#include <net-snmp/session_api.h>
59#include <net-snmp/definitions.h>
60#include <net-snmp/library/asn1.h>
61#include <net-snmp/mib_api.h>
62#include <net-snmp/library/snmp_impl.h>
63#include <string.h>
64#include "../gl/regex.h"
65#include "../gl/base64.h"
66#include <assert.h>
67
68const char DEFAULT_COMMUNITY[] = "public";
69const char DEFAULT_MIBLIST[] = "ALL";
45#define DEFAULT_AUTH_PROTOCOL "MD5" 70#define DEFAULT_AUTH_PROTOCOL "MD5"
46#define DEFAULT_PRIV_PROTOCOL "DES"
47#define DEFAULT_DELIMITER "="
48#define DEFAULT_OUTPUT_DELIMITER " "
49#define DEFAULT_BUFFER_SIZE 100
50
51#define mark(a) ((a)!=0?"*":"")
52
53#define CHECK_UNDEF 0
54#define CRIT_PRESENT 1
55#define CRIT_STRING 2
56#define CRIT_REGEX 4
57#define WARN_PRESENT 8
58#define WARN_STRING 16
59#define WARN_REGEX 32
60
61#define OID_COUNT_STEP 8
62
63/* Longopts only arguments */
64#define L_CALCULATE_RATE CHAR_MAX+1
65#define L_RATE_MULTIPLIER CHAR_MAX+2
66#define L_INVERT_SEARCH CHAR_MAX+3
67#define L_OFFSET CHAR_MAX+4
68#define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX+5
69
70/* Gobble to string - stop incrementing c when c[0] match one of the
71 * characters in s */
72#define GOBBLE_TOS(c, s) while(c[0]!='\0' && strchr(s, c[0])==NULL) { c++; }
73/* Given c, keep track of backslashes (bk) and double-quotes (dq)
74 * from c[0] */
75#define COUNT_SEQ(c, bk, dq) switch(c[0]) {\
76 case '\\': \
77 if (bk) bk--; \
78 else bk++; \
79 break; \
80 case '"': \
81 if (!dq) { dq++; } \
82 else if(!bk) { dq--; } \
83 else { bk--; } \
84 break; \
85 }
86
87
88 71
89int process_arguments (int, char **); 72#ifdef HAVE_USM_DES_PRIV_PROTOCOL
90int validate_arguments (void); 73# define DEFAULT_PRIV_PROTOCOL "DES"
91char *thisarg (char *str); 74#else
92char *nextarg (char *str); 75# define DEFAULT_PRIV_PROTOCOL "AES"
93void print_usage (void); 76#endif
94void print_help (void);
95char *multiply (char *str);
96
97#include "regex.h"
98char regex_expect[MAX_INPUT_BUFFER] = "";
99regex_t preg;
100regmatch_t pmatch[10];
101char errbuf[MAX_INPUT_BUFFER] = "";
102char perfstr[MAX_INPUT_BUFFER] = "| ";
103int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
104int eflags = 0;
105int errcode, excode;
106
107char *server_address = NULL;
108char *community = NULL;
109char **contextargs = NULL;
110char *context = NULL;
111char **authpriv = NULL;
112char *proto = NULL;
113char *seclevel = NULL;
114char *secname = NULL;
115char *authproto = NULL;
116char *privproto = NULL;
117char *authpasswd = NULL;
118char *privpasswd = NULL;
119int nulloid = STATE_UNKNOWN;
120char **oids = NULL;
121size_t oids_size = 0;
122char *label;
123char *units;
124char *port;
125char *snmpcmd;
126char string_value[MAX_INPUT_BUFFER] = "";
127int invert_search=0;
128char **labels = NULL;
129char **unitv = NULL;
130size_t nlabels = 0;
131size_t labels_size = OID_COUNT_STEP;
132size_t nunits = 0;
133size_t unitv_size = OID_COUNT_STEP;
134size_t numoids = 0;
135int numauthpriv = 0;
136int numcontext = 0;
137int verbose = 0;
138bool usesnmpgetnext = false;
139char *warning_thresholds = NULL;
140char *critical_thresholds = NULL;
141thresholds **thlds;
142size_t thlds_size = OID_COUNT_STEP;
143double *response_value;
144size_t response_size = OID_COUNT_STEP;
145int retries = 0;
146int *eval_method;
147size_t eval_size = OID_COUNT_STEP;
148char *delimiter;
149char *output_delim;
150char *miblist = NULL;
151bool needmibs = false;
152int calculate_rate = 0;
153double offset = 0.0;
154int rate_multiplier = 1;
155state_data *previous_state;
156double *previous_value;
157size_t previous_size = OID_COUNT_STEP;
158int perf_labels = 1;
159char* ip_version = "";
160double multiplier = 1.0;
161char *fmtstr = "";
162bool fmtstr_set = false;
163char buffer[DEFAULT_BUFFER_SIZE];
164bool ignore_mib_parsing_errors = false;
165
166static char *fix_snmp_range(char *th)
167{
168 double left, right;
169 char *colon, *ret;
170
171 if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0')
172 return th;
173
174 left = strtod(th, NULL);
175 right = strtod(colon + 1, NULL);
176 if (right >= left)
177 return th;
178
179 if ((ret = malloc(strlen(th) + 2)) == NULL)
180 die(STATE_UNKNOWN, _("Cannot malloc"));
181 *colon = '\0';
182 sprintf(ret, "@%s:%s", colon + 1, th);
183 free(th);
184 return ret;
185}
186 77
187int 78typedef struct proces_arguments_wrapper {
188main (int argc, char **argv) 79 int errorcode;
189{ 80 check_snmp_config config;
190 int len, total_oids; 81} process_arguments_wrapper;
191 size_t line;
192 unsigned int bk_count = 0, dq_count = 0;
193 int iresult = STATE_UNKNOWN;
194 int result = STATE_UNKNOWN;
195 int return_code = 0;
196 int external_error = 0;
197 char **command_line = NULL;
198 char *cl_hidden_auth = NULL;
199 char *oidname = NULL;
200 char *response = NULL;
201 char *mult_resp = NULL;
202 char *outbuff;
203 char *ptr = NULL;
204 char *show = NULL;
205 char *th_warn=NULL;
206 char *th_crit=NULL;
207 char type[8] = "";
208 output chld_out, chld_err;
209 char *previous_string=NULL;
210 char *ap=NULL;
211 char *state_string=NULL;
212 size_t response_length, current_length, string_length;
213 char *temp_string=NULL;
214 char *quote_string=NULL;
215 time_t current_time;
216 double temp_double;
217 time_t duration;
218 char *conv = "12345678";
219 int is_counter=0;
220
221 setlocale (LC_ALL, "");
222 bindtextdomain (PACKAGE, LOCALEDIR);
223 textdomain (PACKAGE);
224
225 labels = malloc (labels_size * sizeof(*labels));
226 unitv = malloc (unitv_size * sizeof(*unitv));
227 thlds = malloc (thlds_size * sizeof(*thlds));
228 response_value = malloc (response_size * sizeof(*response_value));
229 previous_value = malloc (previous_size * sizeof(*previous_value));
230 eval_method = calloc (eval_size, sizeof(*eval_method));
231 oids = calloc(oids_size, sizeof (char *));
232
233 label = strdup ("SNMP");
234 units = strdup ("");
235 port = strdup (DEFAULT_PORT);
236 outbuff = strdup ("");
237 delimiter = strdup (" = ");
238 output_delim = strdup (DEFAULT_OUTPUT_DELIMITER);
239 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
240 retries = DEFAULT_RETRIES;
241 82
242 np_init( (char *) progname, argc, argv ); 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);
243 88
244 /* Parse extra opts if any */ 89int verbose = 0;
245 argv=np_extra_opts (&argc, argv, progname);
246
247 np_set_args(argc, argv);
248 90
249 time(&current_time); 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 }
250 128
251 if (process_arguments (argc, argv) == ERROR) 129 switch (entries[i].type) {
252 usage4 (_("Could not parse arguments")); 130 case ASN_GAUGE:
253 131 case ASN_TIMETICKS:
254 if(calculate_rate) { 132 case ASN_COUNTER:
255 if (!strcmp(label, "SNMP")) 133 case ASN_UINTEGER:
256 label = strdup("SNMP RATE"); 134 case ASN_COUNTER64:
257 135 printf("Value %llu\n", entries[i].value.uIntVal);
258 size_t i = 0; 136 break;
259 137 case ASN_FLOAT:
260 previous_state = np_state_read(); 138 case ASN_DOUBLE:
261 if(previous_state!=NULL) { 139 printf("Value %f\n", entries[i].value.doubleVal);
262 /* Split colon separated values */ 140 break;
263 previous_string = strdup((char *) previous_state->data); 141 case ASN_INTEGER:
264 while((ap = strsep(&previous_string, ":")) != NULL) { 142 printf("Value %lld\n", entries[i].value.intVal);
265 if(verbose>2) 143 break;
266 printf("State for %zd=%s\n", i, ap);
267 while (i >= previous_size) {
268 previous_size += OID_COUNT_STEP;
269 previous_value = realloc(previous_value, previous_size * sizeof(*previous_value));
270 }
271 previous_value[i++]=strtod(ap,NULL);
272 } 144 }
273 } 145 }
274 } 146 }
275 147
276 /* Populate the thresholds */ 148 idx_t encoded = base64_encode_alloc((const char *)entries,
277 th_warn=warning_thresholds; 149 (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)),
278 th_crit=critical_thresholds; 150 &encoded_string);
279 for (size_t i = 0; i < numoids; i++) {
280 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
281 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
282 /* translate "2:1" to "@1:2" for backwards compatibility */
283 w = w ? fix_snmp_range(w) : NULL;
284 c = c ? fix_snmp_range(c) : NULL;
285
286 while (i >= thlds_size) {
287 thlds_size += OID_COUNT_STEP;
288 thlds = realloc(thlds, thlds_size * sizeof(*thlds));
289 }
290 151
291 /* Skip empty thresholds, while avoiding segfault */ 152 if (encoded > 0 && encoded_string != NULL) {
292 set_thresholds(&thlds[i], 153 // success
293 w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL, 154 if (verbose > 1) {
294 c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL); 155 printf("encoded string: %s\n", encoded_string);
295 if (w) { 156 printf("encoded string length: %lu\n", strlen(encoded_string));
296 th_warn=strchr(th_warn, ',');
297 if (th_warn) th_warn++;
298 free(w);
299 }
300 if (c) {
301 th_crit=strchr(th_crit, ',');
302 if (th_crit) th_crit++;
303 free(c);
304 } 157 }
158 result.state_string = encoded_string;
159 return result;
305 } 160 }
161 result.errorcode = ERROR;
162 return result;
163}
306 164
307 /* Create the command array to execute */ 165typedef struct {
308 if(usesnmpgetnext) { 166 int errorcode;
309 snmpcmd = strdup (PATH_TO_SNMPGETNEXT); 167 check_snmp_state_entry *state;
310 }else{ 168} recover_state_data_type;
311 snmpcmd = strdup (PATH_TO_SNMPGET); 169recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) {
170 recover_state_data_type result = {.errorcode = OK, .state = NULL};
171
172 if (verbose > 1) {
173 printf("%s:\n", __FUNCTION__);
174 printf("State string: %s\n", state_string);
175 printf("State string length: %lu\n", state_string_length);
312 } 176 }
313 177
314 /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ 178 idx_t outlen = 0;
179 bool decoded =
180 base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen);
315 181
316 unsigned index = 0; 182 if (!decoded) {
317 command_line = calloc (11 + numcontext + numauthpriv + 1 + numoids + 1, sizeof (char *)); 183 if (verbose) {
184 printf("Failed to decode state string\n");
185 }
186 // failure to decode
187 result.errorcode = ERROR;
188 return result;
189 }
318 190
319 command_line[index++] = snmpcmd; 191 if (result.state == NULL) {
320 command_line[index++] = strdup ("-Le"); 192 // Memory Error?
321 command_line[index++] = strdup ("-t"); 193 result.errorcode = ERROR;
322 xasprintf (&command_line[index++], "%d", timeout_interval); 194 return result;
323 command_line[index++] = strdup ("-r"); 195 }
324 xasprintf (&command_line[index++], "%d", retries);
325 command_line[index++] = strdup ("-m");
326 command_line[index++] = strdup (miblist);
327 command_line[index++] = "-v";
328 command_line[index++] = strdup (proto);
329 196
330 xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s", 197 if (verbose > 1) {
331 snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", proto); 198 printf("Recovered %lu entries of size %lu\n",
199 (size_t)outlen / sizeof(check_snmp_state_entry), outlen);
200
201 for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) {
202 printf("Entry timestamp %lu: %s", result.state[i].timestamp,
203 ctime(&result.state[i].timestamp));
204 switch (result.state[i].type) {
205 case ASN_GAUGE:
206 printf("Type GAUGE\n");
207 break;
208 case ASN_TIMETICKS:
209 printf("Type TIMETICKS\n");
210 break;
211 case ASN_COUNTER:
212 printf("Type COUNTER\n");
213 break;
214 case ASN_UINTEGER:
215 printf("Type UINTEGER\n");
216 break;
217 case ASN_COUNTER64:
218 printf("Type COUNTER64\n");
219 break;
220 case ASN_FLOAT:
221 printf("Type FLOAT\n");
222 case ASN_DOUBLE:
223 printf("Type DOUBLE\n");
224 break;
225 case ASN_INTEGER:
226 printf("Type INTEGER\n");
227 break;
228 }
332 229
333 if (ignore_mib_parsing_errors) { 230 switch (result.state[i].type) {
334 command_line[index++] = "-Pe"; 231 case ASN_GAUGE:
335 xasprintf(&cl_hidden_auth, "%s -Pe", cl_hidden_auth); 232 case ASN_TIMETICKS:
233 case ASN_COUNTER:
234 case ASN_UINTEGER:
235 case ASN_COUNTER64:
236 printf("Value %llu\n", result.state[i].value.uIntVal);
237 break;
238 case ASN_FLOAT:
239 case ASN_DOUBLE:
240 printf("Value %f\n", result.state[i].value.doubleVal);
241 break;
242 case ASN_INTEGER:
243 printf("Value %lld\n", result.state[i].value.intVal);
244 break;
245 }
246 }
336 } 247 }
337 248
249 return result;
250}
338 251
339 for (int i = 0; i < numcontext; i++) { 252int main(int argc, char **argv) {
340 command_line[index++] = contextargs[i]; 253 setlocale(LC_ALL, "");
341 } 254 bindtextdomain(PACKAGE, LOCALEDIR);
255 textdomain(PACKAGE);
342 256
343 for (int i = 0; i < numauthpriv; i++) { 257 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
344 command_line[index++] = authpriv[i];
345 }
346 258
347 xasprintf (&command_line[index++], "%s:%s", server_address, port); 259 np_init((char *)progname, argc, argv);
348 260
349 xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", 261 state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv);
350 cl_hidden_auth,
351 server_address,
352 port);
353 262
354 for (size_t i = 0; i < numoids; i++) { 263 /* Parse extra opts if any */
355 command_line[index++] = oids[i]; 264 argv = np_extra_opts(&argc, argv, progname);
356 xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]);
357 }
358 265
359 command_line[index++] = NULL; 266 np_set_args(argc, argv);
360 267
361 if (verbose) { 268 // Initialize net-snmp before touching the session we are going to use
362 printf ("%s\n", cl_hidden_auth); 269 init_snmp("check_snmp");
363 }
364 270
365 /* Set signal handling and alarm */ 271 process_arguments_wrapper paw_tmp = process_arguments(argc, argv);
366 if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 272 if (paw_tmp.errorcode == ERROR) {
367 usage4 (_("Cannot catch SIGALRM")); 273 usage4(_("Could not parse arguments"));
368 }
369 alarm(timeout_interval * retries + 5);
370
371 /* Run the command */
372 return_code = cmd_run_array (command_line, &chld_out, &chld_err, 0);
373
374 /* disable alarm again */
375 alarm(0);
376
377 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
378 only return state unknown if return code is non zero or there is no stdout.
379 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
380 */
381 if (return_code != 0)
382 external_error=1;
383 if (chld_out.lines == 0)
384 external_error=1;
385 if (external_error) {
386 if (chld_err.lines > 0) {
387 printf (_("External command error: %s\n"), chld_err.line[0]);
388 for (size_t i = 1; i < chld_err.lines; i++) {
389 printf ("%s\n", chld_err.line[i]);
390 }
391 } else {
392 printf(_("External command error with no output (return code: %d)\n"), return_code);
393 }
394 exit (STATE_UNKNOWN);
395 } 274 }
396 275
397 if (verbose) { 276 check_snmp_config config = paw_tmp.config;
398 for (size_t i = 0; i < chld_out.lines; i++) { 277
399 printf ("%s\n", chld_out.line[i]); 278 if (config.output_format_is_set) {
400 } 279 mp_set_format(config.output_format);
401 } 280 }
402 281
403 line = 0; 282 /* Set signal handling and alarm */
404 total_oids = 0; 283 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
405 for (size_t i = 0; line < chld_out.lines && i < numoids ; line++, i++, total_oids++) { 284 usage4(_("Cannot catch SIGALRM"));
406 if(calculate_rate) 285 }
407 conv = "%.10g";
408 else
409 conv = "%.0f";
410
411 ptr = chld_out.line[line];
412 oidname = strpcpy (oidname, ptr, delimiter);
413 response = strstr (ptr, delimiter);
414 if (response == NULL)
415 break;
416 286
417 if (verbose > 2) { 287 time_t current_time;
418 printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i+1, line+1, oidname, response); 288 time(&current_time);
419 }
420 289
421 /* Clean up type array - Sol10 does not necessarily zero it out */ 290 if (verbose > 2) {
422 bzero(type, sizeof(type)); 291 printf("current time: %s (timestamp: %lu)\n", ctime(&current_time), current_time);
292 }
423 293
424 is_counter=0; 294 snmp_responces response = do_snmp_query(config.snmp_params);
425 /* We strip out the datatype indicator for PHBs */
426 if (strstr (response, "Gauge: ")) {
427 show = multiply (strstr (response, "Gauge: ") + 7);
428 }
429 else if (strstr (response, "Gauge32: ")) {
430 show = multiply (strstr (response, "Gauge32: ") + 9);
431 }
432 else if (strstr (response, "Counter32: ")) {
433 show = strstr (response, "Counter32: ") + 11;
434 is_counter=1;
435 if(!calculate_rate)
436 strcpy(type, "c");
437 }
438 else if (strstr (response, "Counter64: ")) {
439 show = strstr (response, "Counter64: ") + 11;
440 is_counter=1;
441 if(!calculate_rate)
442 strcpy(type, "c");
443 }
444 else if (strstr (response, "INTEGER: ")) {
445 show = multiply (strstr (response, "INTEGER: ") + 9);
446 295
447 if (fmtstr_set) { 296 mp_check overall = mp_check_init();
448 conv = fmtstr;
449 }
450 }
451 else if (strstr (response, "OID: ")) {
452 show = strstr (response, "OID: ") + 5;
453 }
454 else if (strstr (response, "STRING: ")) {
455 show = strstr (response, "STRING: ") + 8;
456 conv = "%.10g";
457
458 /* Get the rest of the string on multi-line strings */
459 ptr = show;
460 COUNT_SEQ(ptr, bk_count, dq_count)
461 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
462 ptr++;
463 GOBBLE_TOS(ptr, "\n\"\\")
464 COUNT_SEQ(ptr, bk_count, dq_count)
465 }
466 297
467 if (dq_count) { /* unfinished line */ 298 if (response.errorcode == OK) {
468 /* copy show verbatim first */ 299 mp_subcheck sc_successfull_query = mp_subcheck_init();
469 if (!mult_resp) mult_resp = strdup(""); 300 xasprintf(&sc_successfull_query.output, "SNMP query was successful");
470 xasprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show); 301 sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK);
471 /* then strip out unmatched double-quote from single-line output */ 302 mp_add_subcheck_to_check(&overall, sc_successfull_query);
472 if (show[0] == '"') show++; 303 } else {
473 304 // Error treatment here, either partial or whole
474 /* Keep reading until we match end of double-quoted string */ 305 mp_subcheck sc_failed_query = mp_subcheck_init();
475 for (line++; line < chld_out.lines; line++) { 306 xasprintf(&sc_failed_query.output, "SNMP query failed");
476 ptr = chld_out.line[line]; 307 sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK);
477 xasprintf (&mult_resp, "%s%s\n", mult_resp, ptr); 308 mp_add_subcheck_to_check(&overall, sc_failed_query);
478 309 mp_exit(overall);
479 COUNT_SEQ(ptr, bk_count, dq_count) 310 }
480 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
481 ptr++;
482 GOBBLE_TOS(ptr, "\n\"\\")
483 COUNT_SEQ(ptr, bk_count, dq_count)
484 }
485 /* Break for loop before next line increment when done */
486 if (!dq_count) break;
487 }
488 }
489 311
490 } 312 check_snmp_state_entry *prev_state = NULL;
491 else if (strstr (response, "Timeticks: ")) { 313 bool have_previous_state = false;
492 show = strstr (response, "Timeticks: ");
493 }
494 else
495 show = response + 3;
496 314
497 iresult = STATE_DEPENDENT; 315 if (config.evaluation_params.calculate_rate) {
316 state_data *previous_state = np_state_read(stateKey);
317 if (previous_state == NULL) {
318 // failed to recover state
319 // or no previous state
320 have_previous_state = false;
321 } else {
322 // sanity check
323 recover_state_data_type prev_state_wrapper =
324 recover_state_data(previous_state->data, (idx_t)previous_state->length);
498 325
499 /* Process this block for numeric comparisons */ 326 if (prev_state_wrapper.errorcode == OK) {
500 /* Make some special values,like Timeticks numeric only if a threshold is defined */ 327 have_previous_state = true;
501 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { 328 prev_state = prev_state_wrapper.state;
502 if (verbose > 2) {
503 print_thresholds(" thresholds", thlds[i]);
504 }
505 ptr = strpbrk (show, "-0123456789");
506 if (ptr == NULL){
507 if (nulloid == 3)
508 die (STATE_UNKNOWN,_("No valid data returned (%s)\n"), show);
509 else if (nulloid == 0)
510 die (STATE_OK,_("No valid data returned (%s)\n"), show);
511 else if (nulloid == 1)
512 die (STATE_WARNING,_("No valid data returned (%s)\n"), show);
513 else if (nulloid == 2)
514 die (STATE_CRITICAL,_("No valid data returned (%s)\n"), show);
515 }
516 while (i >= response_size) {
517 response_size += OID_COUNT_STEP;
518 response_value = realloc(response_value, response_size * sizeof(*response_value));
519 }
520 response_value[i] = strtod (ptr, NULL) + offset;
521
522 if(calculate_rate) {
523 if (previous_state!=NULL) {
524 duration = current_time-previous_state->time;
525 if(duration<=0)
526 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
527 temp_double = response_value[i]-previous_value[i];
528 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
529 if(is_counter) {
530 if(temp_double<(double)0.0)
531 temp_double+=(double)4294967296.0; /* 2^32 */
532 if(temp_double<(double)0.0)
533 temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
534 }
535 /* Convert to per second, then use multiplier */
536 temp_double = temp_double/duration*rate_multiplier;
537 iresult = get_status(temp_double, thlds[i]);
538 xasprintf (&show, conv, temp_double);
539 }
540 } else { 329 } else {
541 iresult = get_status(response_value[i], thlds[i]); 330 have_previous_state = false;
542 xasprintf (&show, conv, response_value[i]); 331 prev_state = NULL;
543 } 332 }
544 } 333 }
334 }
545 335
546 /* Process this block for string matching */ 336 check_snmp_state_entry *new_state = NULL;
547 else if (eval_size > i && eval_method[i] & CRIT_STRING) { 337 if (config.evaluation_params.calculate_rate) {
548 if (strcmp (show, string_value)) 338 new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry));
549 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK; 339 if (new_state == NULL) {
550 else 340 die(STATE_UNKNOWN, "memory allocation failed");
551 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
552 } 341 }
342 }
553 343
554 /* Process this block for regex matching */ 344 // We got the the query results, now process them
555 else if (eval_size > i && eval_method[i] & CRIT_REGEX) { 345 for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) {
556 excode = regexec (&preg, response, 10, pmatch, eflags); 346 if (verbose > 0) {
557 if (excode == 0) { 347 printf("loop_index: %zu\n", loop_index);
558 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
559 }
560 else if (excode != REG_NOMATCH) {
561 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
562 printf (_("Execute Error: %s\n"), errbuf);
563 exit (STATE_CRITICAL);
564 }
565 else {
566 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
567 }
568 } 348 }
569 349
570 /* Process this block for existence-nonexistence checks */ 350 check_snmp_state_entry previous_unit_state = {};
571 /* TV: Should this be outside of this else block? */ 351 if (config.evaluation_params.calculate_rate && have_previous_state) {
572 else { 352 previous_unit_state = prev_state[loop_index];
573 if (eval_size > i && eval_method[i] & CRIT_PRESENT)
574 iresult = STATE_CRITICAL;
575 else if (eval_size > i && eval_method[i] & WARN_PRESENT)
576 iresult = STATE_WARNING;
577 else if (response && iresult == STATE_DEPENDENT)
578 iresult = STATE_OK;
579 } 353 }
580 354
581 /* Result is the worst outcome of all the OIDs tested */ 355 check_snmp_evaluation single_eval =
582 result = max_state (result, iresult); 356 evaluate_single_unit(response.response_values[loop_index], config.evaluation_params,
583 357 config.snmp_params.test_units[loop_index], current_time,
584 /* Prepend a label for this OID if there is one */ 358 previous_unit_state, have_previous_state);
585 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
586 xasprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
587 (i == 0) ? " " : output_delim,
588 labels[i], mark (iresult), show, mark (iresult));
589 else
590 xasprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
591 mark (iresult), show, mark (iresult));
592
593 /* Append a unit string for this OID if there is one */
594 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
595 xasprintf (&outbuff, "%s %s", outbuff, unitv[i]);
596
597 /* Write perfdata with whatever can be parsed by strtod, if possible */
598 ptr = NULL;
599 strtod(show, &ptr);
600 if (ptr > show) {
601 if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
602 temp_string=labels[i];
603 else
604 temp_string=oidname;
605 if (strpbrk (temp_string, " ='\"") == NULL) {
606 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
607 } else {
608 if (strpbrk (temp_string, "'") == NULL) {
609 quote_string="'";
610 } else {
611 quote_string="\"";
612 }
613 strncat(perfstr, quote_string, sizeof(perfstr)-strlen(perfstr)-1);
614 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
615 strncat(perfstr, quote_string, sizeof(perfstr)-strlen(perfstr)-1);
616 }
617 strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
618 len = sizeof(perfstr)-strlen(perfstr)-1;
619 strncat(perfstr, show, len>ptr-show ? ptr-show : len);
620
621 if (strcmp(type, "") != 0) {
622 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
623 }
624
625 if (warning_thresholds) {
626 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
627 if(thlds[i]->warning && thlds[i]->warning->text)
628 strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr)-strlen(perfstr)-1);
629 }
630
631 if (critical_thresholds) {
632 if (!warning_thresholds)
633 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
634 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
635 if(thlds[i]->critical && thlds[i]->critical->text)
636 strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr)-strlen(perfstr)-1);
637 }
638 359
639 strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1); 360 if (config.evaluation_params.calculate_rate &&
361 mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) {
362 new_state[loop_index] = single_eval.state;
640 } 363 }
641 }
642 364
643 /* Save state data, as all data collected now */ 365 mp_add_subcheck_to_check(&overall, single_eval.sc);
644 if(calculate_rate) {
645 string_length=1024;
646 state_string=malloc(string_length);
647 if(state_string==NULL)
648 die(STATE_UNKNOWN, _("Cannot malloc"));
649
650 current_length=0;
651 for(int i = 0; i < total_oids; i++) {
652 xasprintf(&temp_string,"%.0f",response_value[i]);
653 if(temp_string==NULL)
654 die(STATE_UNKNOWN,_("Cannot asprintf()"));
655 response_length = strlen(temp_string);
656 if(current_length+response_length>string_length) {
657 string_length=current_length+1024;
658 state_string=realloc(state_string,string_length);
659 if(state_string==NULL)
660 die(STATE_UNKNOWN, _("Cannot realloc()"));
661 }
662 strcpy(&state_string[current_length],temp_string);
663 current_length=current_length+response_length;
664 state_string[current_length]=':';
665 current_length++;
666 free(temp_string);
667 }
668 state_string[--current_length]='\0';
669 if (verbose > 2)
670 printf("State string=%s\n",state_string);
671
672 /* This is not strictly the same as time now, but any subtle variations will cancel out */
673 np_state_write_string(current_time, state_string );
674 if(previous_state==NULL) {
675 /* Or should this be highest state? */
676 die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
677 }
678 } 366 }
679 367
680 printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr); 368 if (config.evaluation_params.calculate_rate) {
681 if (mult_resp) printf ("%s", mult_resp); 369 // store state
370 gen_state_string_type current_state_wrapper =
371 gen_state_string(new_state, config.snmp_params.num_of_test_units);
682 372
683 return result; 373 if (current_state_wrapper.errorcode == OK) {
374 np_state_write_string(stateKey, current_time, current_state_wrapper.state_string);
375 } else {
376 die(STATE_UNKNOWN, "failed to create state string");
377 }
378 }
379 mp_exit(overall);
684} 380}
685 381
686
687
688/* process command-line arguments */ 382/* process command-line arguments */
689int 383static process_arguments_wrapper process_arguments(int argc, char **argv) {
690process_arguments (int argc, char **argv) 384 enum {
691{ 385 /* Longopts only arguments */
692 char *ptr; 386 invert_search_index = CHAR_MAX + 1,
693 int c = 1; 387 offset_index,
694 size_t j = 0, jj = 0; 388 ignore_mib_parsing_errors_index,
389 connection_prefix_index,
390 output_format_index,
391 calculate_rate,
392 rate_multiplier
393 };
695 394
696 int option = 0;
697 static struct option longopts[] = { 395 static struct option longopts[] = {
698 STD_LONG_OPTS, 396 STD_LONG_OPTS,
699 {"community", required_argument, 0, 'C'}, 397 {"community", required_argument, 0, 'C'},
@@ -721,662 +419,738 @@ process_arguments (int argc, char **argv)
721 {"authpasswd", required_argument, 0, 'A'}, 419 {"authpasswd", required_argument, 0, 'A'},
722 {"privpasswd", required_argument, 0, 'X'}, 420 {"privpasswd", required_argument, 0, 'X'},
723 {"next", no_argument, 0, 'n'}, 421 {"next", no_argument, 0, 'n'},
724 {"rate", no_argument, 0, L_CALCULATE_RATE}, 422 {"offset", required_argument, 0, offset_index},
725 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER}, 423 {"invert-search", no_argument, 0, invert_search_index},
726 {"offset", required_argument, 0, L_OFFSET},
727 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
728 {"perf-oids", no_argument, 0, 'O'}, 424 {"perf-oids", no_argument, 0, 'O'},
729 {"ipv4", no_argument, 0, '4'}, 425 {"ipv4", no_argument, 0, '4'},
730 {"ipv6", no_argument, 0, '6'}, 426 {"ipv6", no_argument, 0, '6'},
731 {"multiplier", required_argument, 0, 'M'}, 427 {"multiplier", required_argument, 0, 'M'},
732 {"fmtstr", required_argument, 0, 'f'}, 428 {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index},
733 {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS}, 429 {"connection-prefix", required_argument, 0, connection_prefix_index},
734 {0, 0, 0, 0} 430 {"output-format", required_argument, 0, output_format_index},
735 }; 431 {"rate", no_argument, 0, calculate_rate},
432 {"rate-multiplier", required_argument, 0, rate_multiplier},
433 {0, 0, 0, 0}};
434
435 if (argc < 2) {
436 process_arguments_wrapper result = {
437 .errorcode = ERROR,
438 };
439 return result;
440 }
441
442 // Count number of OIDs here first
443 int option = 0;
444 size_t oid_counter = 0;
445 while (true) {
446 int option_char = getopt_long(
447 argc, argv,
448 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
449
450 if (option_char == -1 || option_char == EOF) {
451 break;
452 }
453
454 switch (option_char) {
455 case 'o': {
456 // we are going to parse this again, so we work on a copy of that string
457 char *tmp_oids = strdup(optarg);
458 if (tmp_oids == NULL) {
459 die(STATE_UNKNOWN, "strdup failed");
460 }
461
462 for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL;
463 ptr = strtok(NULL, ", "), oid_counter++) {
464 }
465 break;
466 }
467 case '?': /* usage */
468 usage5();
469 // fallthrough
470 case 'h': /* help */
471 print_help();
472 exit(STATE_UNKNOWN);
473 case 'V': /* version */
474 print_revision(progname, NP_VERSION);
475 exit(STATE_UNKNOWN);
476
477 default:
478 continue;
479 }
480 }
736 481
737 if (argc < 2) 482 /* Check whether at least one OID was given */
738 return ERROR; 483 if (oid_counter == 0) {
739 484 die(STATE_UNKNOWN, _("No OIDs specified\n"));
740 /* reverse compatibility for very old non-POSIX usage forms */
741 for (c = 1; c < argc; c++) {
742 if (strcmp ("-to", argv[c]) == 0)
743 strcpy (argv[c], "-t");
744 if (strcmp ("-wv", argv[c]) == 0)
745 strcpy (argv[c], "-w");
746 if (strcmp ("-cv", argv[c]) == 0)
747 strcpy (argv[c], "-c");
748 } 485 }
749 486
750 while (1) { 487 // Allocate space for test units
751 c = getopt_long (argc, argv, "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", 488 check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit));
752 longopts, &option); 489 if (tmp == NULL) {
490 die(STATE_UNKNOWN, "Failed to calloc");
491 }
753 492
754 if (c == -1 || c == EOF) 493 for (size_t i = 0; i < oid_counter; i++) {
494 tmp[i] = check_snmp_test_unit_init();
495 }
496
497 check_snmp_config config = check_snmp_config_init();
498 config.snmp_params.test_units = tmp;
499 config.snmp_params.num_of_test_units = oid_counter;
500
501 option = 0;
502 optind = 1; // Reset argument scanner
503 size_t tmp_oid_counter = 0;
504 size_t eval_counter = 0;
505 size_t unitv_counter = 0;
506 size_t labels_counter = 0;
507 unsigned char *authpasswd = NULL;
508 unsigned char *privpasswd = NULL;
509 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
510 char *port = NULL;
511 char *miblist = NULL;
512 char *connection_prefix = NULL;
513 bool snmp_version_set_explicitely = false;
514 // TODO error checking
515 while (true) {
516 int option_char = getopt_long(
517 argc, argv,
518 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
519
520 if (option_char == -1 || option_char == EOF) {
755 break; 521 break;
522 }
756 523
757 switch (c) { 524 switch (option_char) {
758 case '?': /* usage */ 525 case '?': /* usage */
759 usage5 (); 526 usage5();
760 case 'h': /* help */ 527 case 'h': /* help */
761 print_help (); 528 print_help();
762 exit (STATE_UNKNOWN); 529 exit(STATE_UNKNOWN);
763 case 'V': /* version */ 530 case 'V': /* version */
764 print_revision (progname, NP_VERSION); 531 print_revision(progname, NP_VERSION);
765 exit (STATE_UNKNOWN); 532 exit(STATE_UNKNOWN);
766 case 'v': /* verbose */ 533 case 'v': /* verbose */
767 verbose++; 534 verbose++;
768 break; 535 break;
769 536
770 /* Connection info */ 537 /* Connection info */
771 case 'C': /* group or community */ 538 case 'C': /* group or community */
772 community = optarg; 539 config.snmp_params.snmp_session.community = (unsigned char *)optarg;
540 config.snmp_params.snmp_session.community_len = strlen(optarg);
773 break; 541 break;
774 case 'H': /* Host or server */ 542 case 'H': /* Host or server */
775 server_address = optarg; 543 config.snmp_params.snmp_session.peername = optarg;
776 break; 544 break;
777 case 'p': /* TCP port number */ 545 case 'p': /*port number */
546 // Add port to "peername" below to not rely on argument order
778 port = optarg; 547 port = optarg;
779 break; 548 break;
780 case 'm': /* List of MIBS */ 549 case 'm': /* List of MIBS */
781 miblist = optarg; 550 miblist = optarg;
782 break; 551 break;
783 case 'n': /* usesnmpgetnext */ 552 case 'n': /* use_getnext instead of get */
784 usesnmpgetnext = true; 553 config.snmp_params.use_getnext = true;
785 break; 554 break;
786 case 'P': /* SNMP protocol version */ 555 case 'P': /* SNMP protocol version */
787 proto = optarg; 556 if (strcasecmp("1", optarg) == 0) {
557 config.snmp_params.snmp_session.version = SNMP_VERSION_1;
558 } else if (strcasecmp("2c", optarg) == 0) {
559 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
560 } else if (strcasecmp("3", optarg) == 0) {
561 config.snmp_params.snmp_session.version = SNMP_VERSION_3;
562 } else {
563 die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg);
564 }
565 snmp_version_set_explicitely = true;
566
788 break; 567 break;
789 case 'N': /* SNMPv3 context */ 568 case 'N': /* SNMPv3 context name */
790 context = optarg; 569 config.snmp_params.snmp_session.contextName = optarg;
570 config.snmp_params.snmp_session.contextNameLen = strlen(optarg);
791 break; 571 break;
792 case 'L': /* security level */ 572 case 'L': /* security level */
793 seclevel = optarg; 573 if (strcasecmp("noAuthNoPriv", optarg) == 0) {
574 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
575 } else if (strcasecmp("authNoPriv", optarg) == 0) {
576 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
577 } else if (strcasecmp("authPriv", optarg) == 0) {
578 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
579 } else {
580 die(STATE_UNKNOWN, "invalid security level: %s", optarg);
581 }
794 break; 582 break;
795 case 'U': /* security username */ 583 case 'U': /* security username */
796 secname = optarg; 584 config.snmp_params.snmp_session.securityName = optarg;
585 config.snmp_params.snmp_session.securityNameLen = strlen(optarg);
797 break; 586 break;
798 case 'a': /* auth protocol */ 587 case 'a': /* auth protocol */
799 authproto = optarg; 588 // SNMPv3: SHA or MD5
589 // TODO Test for availability of individual protocols
590 if (strcasecmp("MD5", optarg) == 0) {
591 config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol;
592 config.snmp_params.snmp_session.securityAuthProtoLen =
593 OID_LENGTH(usmHMACMD5AuthProtocol);
594 } else if (strcasecmp("SHA", optarg) == 0) {
595 config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol;
596 config.snmp_params.snmp_session.securityAuthProtoLen =
597 OID_LENGTH(usmHMACSHA1AuthProtocol);
598 } else if (strcasecmp("SHA224", optarg) == 0) {
599 config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol;
600 config.snmp_params.snmp_session.securityAuthProtoLen =
601 OID_LENGTH(usmHMAC128SHA224AuthProtocol);
602 } else if (strcasecmp("SHA256", optarg) == 0) {
603 config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol;
604 config.snmp_params.snmp_session.securityAuthProtoLen =
605 OID_LENGTH(usmHMAC192SHA256AuthProtocol);
606 } else if (strcasecmp("SHA384", optarg) == 0) {
607 config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol;
608 config.snmp_params.snmp_session.securityAuthProtoLen =
609 OID_LENGTH(usmHMAC256SHA384AuthProtocol);
610 } else if (strcasecmp("SHA512", optarg) == 0) {
611 config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol;
612 config.snmp_params.snmp_session.securityAuthProtoLen =
613 OID_LENGTH(usmHMAC384SHA512AuthProtocol);
614 } else {
615 die(STATE_UNKNOWN, "Unknown authentication protocol");
616 }
800 break; 617 break;
801 case 'x': /* priv protocol */ 618 case 'x': /* priv protocol */
802 privproto = optarg; 619 if (strcasecmp("DES", optarg) == 0) {
620#ifdef HAVE_USM_DES_PRIV_PROTOCOL
621 config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol;
622 config.snmp_params.snmp_session.securityAuthProtoLen =
623 OID_LENGTH(usmDESPrivProtocol);
624#else
625 die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform");
626#endif
627 } else if (strcasecmp("AES", optarg) == 0) {
628 config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol;
629 config.snmp_params.snmp_session.securityAuthProtoLen =
630 OID_LENGTH(usmAESPrivProtocol);
631 // } else if (strcasecmp("AES128", optarg)) {
632 // config.snmp_session.securityAuthProto = usmAES128PrivProtocol;
633 // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol)
634 // / OID_LENGTH(oid);
635 } else if (strcasecmp("AES192", optarg) == 0) {
636 config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol;
637 config.snmp_params.snmp_session.securityAuthProtoLen =
638 OID_LENGTH(usmAES192PrivProtocol);
639 } else if (strcasecmp("AES256", optarg) == 0) {
640 config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol;
641 config.snmp_params.snmp_session.securityAuthProtoLen =
642 OID_LENGTH(usmAES256PrivProtocol);
643 // } else if (strcasecmp("AES192Cisco", optarg)) {
644 // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol;
645 // config.snmp_session.securityAuthProtoLen =
646 // sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); } else if
647 // (strcasecmp("AES256Cisco", optarg)) { config.snmp_session.securityAuthProto =
648 // usmAES256CiscoPrivProtocol; config.snmp_session.securityAuthProtoLen =
649 // sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); } else if
650 // (strcasecmp("AES192Cisco2", optarg)) { config.snmp_session.securityAuthProto
651 // = usmAES192Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
652 // sizeof(usmAES192Cisco2PrivProtocol) / sizeof(oid); } else if
653 // (strcasecmp("AES256Cisco2", optarg)) { config.snmp_session.securityAuthProto
654 // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
655 // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid);
656 } else {
657 die(STATE_UNKNOWN, "Unknown privacy protocol");
658 }
803 break; 659 break;
804 case 'A': /* auth passwd */ 660 case 'A': /* auth passwd */
805 authpasswd = optarg; 661 authpasswd = (unsigned char *)optarg;
806 break; 662 break;
807 case 'X': /* priv passwd */ 663 case 'X': /* priv passwd */
808 privpasswd = optarg; 664 privpasswd = (unsigned char *)optarg;
809 break; 665 break;
810 case 't': /* timeout period */ 666 case 'e':
811 if (!is_integer (optarg)) 667 case 'E':
812 usage2 (_("Timeout interval must be a positive integer"), optarg); 668 if (!is_integer(optarg)) {
813 else 669 usage2(_("Retries interval must be a positive integer"), optarg);
814 timeout_interval = atoi (optarg); 670 } else {
671 config.snmp_params.snmp_session.retries = atoi(optarg);
672 }
815 break; 673 break;
816 674 case 't': /* timeout period */
817 /* Test parameters */ 675 if (!is_integer(optarg)) {
818 case 'c': /* critical threshold */ 676 usage2(_("Timeout interval must be a positive integer"), optarg);
819 critical_thresholds = optarg; 677 } else {
678 timeout_interval = (unsigned int)atoi(optarg);
679 }
820 break; 680 break;
821 case 'w': /* warning threshold */ 681
822 warning_thresholds = optarg; 682 /* Test parameters */
683 case 'c': /* critical threshold */
684 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true);
823 break; 685 break;
824 case 'e': /* PRELIMINARY - may change */ 686 case 'w': /* warning threshold */
825 case 'E': /* PRELIMINARY - may change */ 687 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false);
826 if (!is_integer (optarg))
827 usage2 (_("Retries interval must be a positive integer"), optarg);
828 else
829 retries = atoi(optarg);
830 break; 688 break;
831 case 'o': /* object identifier */ 689 case 'o': /* object identifier */
832 if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) { 690 if (strspn(optarg, "0123456789.,") != strlen(optarg)) {
833 /* 691 /*
834 * we have something other than digits, periods and comas, 692 * we have something other than digits, periods and comas,
835 * so we have a mib variable, rather than just an SNMP OID, 693 * so we have a mib variable, rather than just an SNMP OID,
836 * so we have to actually read the mib files 694 * so we have to actually read the mib files
837 */ 695 */
838 needmibs = true; 696 config.snmp_params.need_mibs = true;
839 } 697 }
840 for (ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) { 698
841 while (j >= oids_size) { 699 for (char *ptr = strtok(optarg, ", "); ptr != NULL;
842 oids_size += OID_COUNT_STEP; 700 ptr = strtok(NULL, ", "), tmp_oid_counter++) {
843 oids = realloc(oids, oids_size * sizeof (*oids)); 701 config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr);
844 }
845 oids[j] = strdup(ptr);
846 }
847 numoids = j;
848 if (c == 'E' || c == 'e') {
849 jj++;
850 while (j+1 >= eval_size) {
851 eval_size += OID_COUNT_STEP;
852 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
853 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
854 }
855 if (c == 'E')
856 eval_method[j+1] |= WARN_PRESENT;
857 else if (c == 'e')
858 eval_method[j+1] |= CRIT_PRESENT;
859 } 702 }
860 break; 703 break;
861 case 'z': /* Null OID Return Check */ 704 case 'z': /* Null OID Return Check */
862 if (!is_integer (optarg)) 705 if (!is_integer(optarg)) {
863 usage2 (_("Exit status must be a positive integer"), optarg); 706 usage2(_("Exit status must be a positive integer"), optarg);
864 else 707 } else {
865 nulloid = atoi(optarg); 708 config.evaluation_params.nulloid_result = atoi(optarg);
866 break;
867 case 's': /* string or substring */
868 strncpy (string_value, optarg, sizeof (string_value) - 1);
869 string_value[sizeof (string_value) - 1] = 0;
870 while (jj >= eval_size) {
871 eval_size += OID_COUNT_STEP;
872 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
873 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
874 } 709 }
875 eval_method[jj++] = CRIT_STRING;
876 break; 710 break;
877 case 'R': /* regex */ 711 case 's': /* string or substring */
712 strncpy(config.evaluation_params.string_cmp_value, optarg,
713 sizeof(config.evaluation_params.string_cmp_value) - 1);
714 config.evaluation_params
715 .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0;
716 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true;
717 break;
718 case 'R': /* regex */
878 cflags = REG_ICASE; 719 cflags = REG_ICASE;
879 // fall through 720 // fall through
880 case 'r': /* regex */ 721 case 'r': /* regex */
722 {
723 char regex_expect[MAX_INPUT_BUFFER] = "";
881 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 724 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
882 strncpy (regex_expect, optarg, sizeof (regex_expect) - 1); 725 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1);
883 regex_expect[sizeof (regex_expect) - 1] = 0; 726 regex_expect[sizeof(regex_expect) - 1] = 0;
884 errcode = regcomp (&preg, regex_expect, cflags); 727 int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags);
885 if (errcode != 0) { 728 if (errcode != 0) {
886 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 729 char errbuf[MAX_INPUT_BUFFER] = "";
887 printf (_("Could Not Compile Regular Expression")); 730 regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf,
888 return ERROR; 731 MAX_INPUT_BUFFER);
732 printf("Could Not Compile Regular Expression: %s", errbuf);
733 process_arguments_wrapper result = {
734 .errorcode = ERROR,
735 };
736 return result;
889 } 737 }
890 while (jj >= eval_size) { 738 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true;
891 eval_size += OID_COUNT_STEP; 739 } break;
892 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 740 case 'l': /* label */
893 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); 741 {
742 if (labels_counter >= config.snmp_params.num_of_test_units) {
743 break;
894 } 744 }
895 eval_method[jj++] = CRIT_REGEX; 745 char *ptr = trim_whitespaces_and_check_quoting(optarg);
896 break; 746 if (ptr[0] == '\'') {
897 747 config.snmp_params.test_units[labels_counter].label = ptr + 1;
898 /* Format */ 748 } else {
899 case 'd': /* delimiter */ 749 config.snmp_params.test_units[labels_counter].label = ptr;
900 delimiter = strscpy (delimiter, optarg);
901 break;
902 case 'D': /* output-delimiter */
903 output_delim = strscpy (output_delim, optarg);
904 break;
905 case 'l': /* label */
906 nlabels++;
907 if (nlabels > labels_size) {
908 labels_size += 8;
909 labels = realloc (labels, labels_size * sizeof(*labels));
910 if (labels == NULL)
911 die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
912 } 750 }
913 labels[nlabels - 1] = optarg; 751
914 ptr = thisarg (optarg); 752 while (ptr && (ptr = get_next_argument(ptr))) {
915 labels[nlabels - 1] = ptr; 753 labels_counter++;
916 if (ptr[0] == '\'') 754 ptr = trim_whitespaces_and_check_quoting(ptr);
917 labels[nlabels - 1] = ptr + 1; 755 if (ptr[0] == '\'') {
918 while (ptr && (ptr = nextarg (ptr))) { 756 config.snmp_params.test_units[labels_counter].label = ptr + 1;
919 nlabels++; 757 } else {
920 if (nlabels > labels_size) { 758 config.snmp_params.test_units[labels_counter].label = ptr;
921 labels_size += 8;
922 labels = realloc (labels, labels_size * sizeof(*labels));
923 if (labels == NULL)
924 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
925 } 759 }
926 ptr = thisarg (ptr);
927 if (ptr[0] == '\'')
928 labels[nlabels - 1] = ptr + 1;
929 else
930 labels[nlabels - 1] = ptr;
931 } 760 }
932 break; 761 labels_counter++;
933 case 'u': /* units */ 762 } break;
934 units = optarg; 763 case 'u': /* units */
935 nunits++; 764 {
936 if (nunits > unitv_size) { 765 if (unitv_counter >= config.snmp_params.num_of_test_units) {
937 unitv_size += 8; 766 break;
938 unitv = realloc (unitv, unitv_size * sizeof(*unitv)); 767 }
939 if (unitv == NULL) 768 char *ptr = trim_whitespaces_and_check_quoting(optarg);
940 die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits); 769 if (ptr[0] == '\'') {
770 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
771 } else {
772 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
941 } 773 }
942 unitv[nunits - 1] = optarg; 774 while (ptr && (ptr = get_next_argument(ptr))) {
943 ptr = thisarg (optarg); 775 unitv_counter++;
944 unitv[nunits - 1] = ptr; 776 ptr = trim_whitespaces_and_check_quoting(ptr);
945 if (ptr[0] == '\'') 777 if (ptr[0] == '\'') {
946 unitv[nunits - 1] = ptr + 1; 778 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
947 while (ptr && (ptr = nextarg (ptr))) { 779 } else {
948 if (nunits > unitv_size) { 780 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
949 unitv_size += 8;
950 unitv = realloc (unitv, unitv_size * sizeof(*unitv));
951 if (units == NULL)
952 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
953 } 781 }
954 nunits++;
955 ptr = thisarg (ptr);
956 if (ptr[0] == '\'')
957 unitv[nunits - 1] = ptr + 1;
958 else
959 unitv[nunits - 1] = ptr;
960 } 782 }
783 unitv_counter++;
784 } break;
785 case offset_index:
786 config.evaluation_params.offset = strtod(optarg, NULL);
787 config.evaluation_params.offset_set = true;
961 break; 788 break;
962 case L_CALCULATE_RATE: 789 case invert_search_index:
963 if(calculate_rate==0) 790 config.evaluation_params.invert_search = false;
964 np_enable_state(NULL, 1);
965 calculate_rate = 1;
966 break;
967 case L_RATE_MULTIPLIER:
968 if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
969 usage2(_("Rate multiplier must be a positive integer"),optarg);
970 break;
971 case L_OFFSET:
972 offset=strtod(optarg,NULL);
973 break;
974 case L_INVERT_SEARCH:
975 invert_search=1;
976 break; 791 break;
977 case 'O': 792 case 'O':
978 perf_labels=0; 793 config.evaluation_params.use_oid_as_perf_data_label = true;
979 break; 794 break;
980 case '4': 795 case '4':
796 // The default, do something here to be exclusive to -6 instead of doing nothing?
797 connection_prefix = "udp";
981 break; 798 break;
982 case '6': 799 case '6':
983 xasprintf(&ip_version, "udp6:"); 800 connection_prefix = "udp6";
984 if(verbose>2) 801 break;
985 printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); 802 case connection_prefix_index:
803 connection_prefix = optarg;
986 break; 804 break;
987 case 'M': 805 case 'M':
988 if ( strspn( optarg, "0123456789.," ) == strlen( optarg ) ) { 806 if (strspn(optarg, "0123456789.,") == strlen(optarg)) {
989 multiplier=strtod(optarg,NULL); 807 config.evaluation_params.multiplier = strtod(optarg, NULL);
808 config.evaluation_params.multiplier_set = true;
990 } 809 }
991 break; 810 break;
992 case 'f': 811 case ignore_mib_parsing_errors_index:
993 if (multiplier != 1.0) { 812 config.snmp_params.ignore_mib_parsing_errors = true;
994 fmtstr=optarg; 813 break;
995 fmtstr_set = true; 814 case 'f': // Deprecated format option for floating point values
815 break;
816 case output_format_index: {
817 parsed_output_format parser = mp_parse_output_format(optarg);
818 if (!parser.parsing_success) {
819 // TODO List all available formats here, maybe add anothoer usage function
820 printf("Invalid output format: %s\n", optarg);
821 exit(STATE_UNKNOWN);
822 }
823
824 config.output_format_is_set = true;
825 config.output_format = parser.output_format;
826 break;
827 }
828 case calculate_rate:
829 config.evaluation_params.calculate_rate = true;
830 break;
831 case rate_multiplier:
832 if (!is_integer(optarg) ||
833 ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) {
834 usage2(_("Rate multiplier must be a positive integer"), optarg);
996 } 835 }
997 break; 836 break;
998 case L_IGNORE_MIB_PARSING_ERRORS: 837 default:
999 ignore_mib_parsing_errors = true; 838 die(STATE_UNKNOWN, "Unknown option");
1000 } 839 }
1001 } 840 }
1002 841
1003 if (server_address == NULL) 842 if (config.snmp_params.snmp_session.peername == NULL) {
1004 server_address = argv[optind]; 843 config.snmp_params.snmp_session.peername = argv[optind];
1005 844 }
1006 if (community == NULL)
1007 community = strdup (DEFAULT_COMMUNITY);
1008
1009 return validate_arguments ();
1010}
1011
1012
1013/******************************************************************************
1014
1015@@-
1016<sect3>
1017<title>validate_arguments</title>
1018
1019<para>&PROTO_validate_arguments;</para>
1020
1021<para>Checks to see if the default miblist needs to be loaded. Also verifies
1022the authentication and authorization combinations based on protocol version
1023selected.</para>
1024
1025<para></para>
1026
1027</sect3>
1028-@@
1029******************************************************************************/
1030
1031
1032 845
1033int 846 // Build true peername here if necessary
1034validate_arguments () 847 if (connection_prefix != NULL) {
1035{ 848 // We got something in the connection prefix
1036 /* check whether to load locally installed MIBS (CPU/disk intensive) */ 849 if (strcasecmp(connection_prefix, "udp") == 0) {
1037 if (miblist == NULL) { 850 // The default, do nothing
1038 if (needmibs) { 851 } else if (strcasecmp(connection_prefix, "tcp") == 0) {
1039 miblist = strdup (DEFAULT_MIBLIST); 852 // use tcp/ipv4
1040 }else{ 853 xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s",
1041 miblist = ""; /* don't read any mib files for numeric oids */ 854 config.snmp_params.snmp_session.peername);
855 } else if (strcasecmp(connection_prefix, "tcp6") == 0 ||
856 strcasecmp(connection_prefix, "tcpv6") == 0 ||
857 strcasecmp(connection_prefix, "tcpipv6") == 0 ||
858 strcasecmp(connection_prefix, "udp6") == 0 ||
859 strcasecmp(connection_prefix, "udpipv6") == 0 ||
860 strcasecmp(connection_prefix, "udpv6") == 0) {
861 // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it
862 // works anyway therefore do nothing here
863 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix,
864 config.snmp_params.snmp_session.peername);
865 } else if (strcmp(connection_prefix, "tls") == 0) {
866 // TODO: Anything else to do here?
867 xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s",
868 config.snmp_params.snmp_session.peername);
869 } else if (strcmp(connection_prefix, "dtls") == 0) {
870 // TODO: Anything else to do here?
871 xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s",
872 config.snmp_params.snmp_session.peername);
873 } else if (strcmp(connection_prefix, "unix") == 0) {
874 // TODO: Check whether this is a valid path?
875 xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s",
876 config.snmp_params.snmp_session.peername);
877 } else if (strcmp(connection_prefix, "ipx") == 0) {
878 xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s",
879 config.snmp_params.snmp_session.peername);
880 } else {
881 // Don't know that prefix, die here
882 die(STATE_UNKNOWN, "Unknown connection prefix");
1042 } 883 }
1043 } 884 }
1044 885
1045 /* Check server_address is given */ 886 /* Check server_address is given */
1046 if (server_address == NULL) 887 if (config.snmp_params.snmp_session.peername == NULL) {
1047 die(STATE_UNKNOWN, _("No host specified\n")); 888 die(STATE_UNKNOWN, _("No host specified\n"));
889 }
1048 890
1049 /* Check oid is given */ 891 if (port != NULL) {
1050 if (numoids == 0) 892 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s",
1051 die(STATE_UNKNOWN, _("No OIDs specified\n")); 893 config.snmp_params.snmp_session.peername, port);
1052
1053 if (proto == NULL)
1054 xasprintf(&proto, DEFAULT_PROTOCOL);
1055
1056 if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) { /* snmpv1 or snmpv2c */
1057 numauthpriv = 2;
1058 authpriv = calloc (numauthpriv, sizeof (char *));
1059 authpriv[0] = strdup ("-c");
1060 authpriv[1] = strdup (community);
1061 } 894 }
1062 else if ( strcmp (proto, "3") == 0 ) { /* snmpv3 args */ 895
1063 if (!(context == NULL)) { 896 /* check whether to load locally installed MIBS (CPU/disk intensive) */
1064 numcontext = 2; 897 if (miblist == NULL) {
1065 contextargs = calloc (numcontext, sizeof (char *)); 898 if (config.snmp_params.need_mibs) {
1066 contextargs[0] = strdup ("-n"); 899 setenv("MIBLS", DEFAULT_MIBLIST, 1);
1067 contextargs[1] = strdup (context); 900 } else {
901 setenv("MIBLS", "NONE", 1);
902 miblist = ""; /* don't read any mib files for numeric oids */
1068 } 903 }
904 } else {
905 // Blatantly stolen from snmplib/snmp_parse_args
906 setenv("MIBS", miblist, 1);
907 }
1069 908
1070 if (seclevel == NULL) 909 // Historical default is SNMP v2c
1071 xasprintf(&seclevel, "noAuthNoPriv"); 910 if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) {
911 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
912 }
1072 913
1073 if (secname == NULL) 914 if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) ||
915 (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */
916 /*
917 config.numauthpriv = 2;
918 config.authpriv = calloc(config.numauthpriv, sizeof(char *));
919 config.authpriv[0] = strdup("-c");
920 config.authpriv[1] = strdup(community);
921 */
922 } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */
923 // generate keys for priv and auth here (if demanded)
924
925 if (config.snmp_params.snmp_session.securityName == NULL) {
1074 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); 926 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
927 }
1075 928
1076 if (strcmp(seclevel, "noAuthNoPriv") == 0) { 929 switch (config.snmp_params.snmp_session.securityLevel) {
1077 numauthpriv = 4; 930 case SNMP_SEC_LEVEL_AUTHPRIV: {
1078 authpriv = calloc (numauthpriv, sizeof (char *)); 931 if (authpasswd == NULL) {
1079 authpriv[0] = strdup ("-l"); 932 die(STATE_UNKNOWN,
1080 authpriv[1] = strdup ("noAuthNoPriv"); 933 "No authentication passphrase was given, but authorization was requested");
1081 authpriv[2] = strdup ("-u");
1082 authpriv[3] = strdup (secname);
1083 } else {
1084 if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
1085 usage2 (_("Invalid seclevel"), seclevel);
1086 } 934 }
1087 935 // auth and priv
1088 if (authproto == NULL ) 936 int priv_key_generated = generate_Ku(
1089 xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); 937 config.snmp_params.snmp_session.securityPrivProto,
1090 938 (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd,
1091 if (authpasswd == NULL) 939 strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey,
1092 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); 940 &config.snmp_params.snmp_session.securityPrivKeyLen);
1093 941
1094 if ( strcmp(seclevel, "authNoPriv") == 0 ) { 942 if (priv_key_generated != SNMPERR_SUCCESS) {
1095 numauthpriv = 8; 943 die(STATE_UNKNOWN, "Failed to generate privacy key");
1096 authpriv = calloc (numauthpriv, sizeof (char *));
1097 authpriv[0] = strdup ("-l");
1098 authpriv[1] = strdup ("authNoPriv");
1099 authpriv[2] = strdup ("-a");
1100 authpriv[3] = strdup (authproto);
1101 authpriv[4] = strdup ("-u");
1102 authpriv[5] = strdup (secname);
1103 authpriv[6] = strdup ("-A");
1104 authpriv[7] = strdup (authpasswd);
1105 } else if ( strcmp(seclevel, "authPriv") == 0 ) {
1106 if (privproto == NULL )
1107 xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
1108
1109 if (privpasswd == NULL)
1110 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
1111
1112 numauthpriv = 12;
1113 authpriv = calloc (numauthpriv, sizeof (char *));
1114 authpriv[0] = strdup ("-l");
1115 authpriv[1] = strdup ("authPriv");
1116 authpriv[2] = strdup ("-a");
1117 authpriv[3] = strdup (authproto);
1118 authpriv[4] = strdup ("-u");
1119 authpriv[5] = strdup (secname);
1120 authpriv[6] = strdup ("-A");
1121 authpriv[7] = strdup (authpasswd);
1122 authpriv[8] = strdup ("-x");
1123 authpriv[9] = strdup (privproto);
1124 authpriv[10] = strdup ("-X");
1125 authpriv[11] = strdup (privpasswd);
1126 } 944 }
1127 } 945 }
1128 946 // fall through
1129 } 947 case SNMP_SEC_LEVEL_AUTHNOPRIV: {
1130 else { 948 if (privpasswd == NULL) {
1131 usage2 (_("Invalid SNMP version"), proto); 949 die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested");
950 }
951 int auth_key_generated = generate_Ku(
952 config.snmp_params.snmp_session.securityAuthProto,
953 (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd,
954 strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey,
955 &config.snmp_params.snmp_session.securityAuthKeyLen);
956
957 if (auth_key_generated != SNMPERR_SUCCESS) {
958 die(STATE_UNKNOWN, "Failed to generate privacy key");
959 }
960 } break;
961 case SNMP_SEC_LEVEL_NOAUTH:
962 // No auth, no priv, not much todo
963 break;
964 }
1132 } 965 }
1133 966
1134 return OK; 967 process_arguments_wrapper result = {
968 .config = config,
969 .errorcode = OK,
970 };
971 return result;
1135} 972}
1136 973
1137
1138
1139/* trim leading whitespace 974/* trim leading whitespace
1140 if there is a leading quote, make sure it balances */ 975 if there is a leading quote, make sure it balances */
1141 976char *trim_whitespaces_and_check_quoting(char *str) {
1142char * 977 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
1143thisarg (char *str) 978 if (str[0] == '\'') { /* handle SIMPLE quoted strings */
1144{ 979 if (strlen(str) == 1 || !strstr(str + 1, "'")) {
1145 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */ 980 die(STATE_UNKNOWN, _("Unbalanced quotes\n"));
1146 if (str[0] == '\'') { /* handle SIMPLE quoted strings */ 981 }
1147 if (strlen (str) == 1 || !strstr (str + 1, "'"))
1148 die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
1149 } 982 }
1150 return str; 983 return str;
1151} 984}
1152 985
1153
1154
1155/* if there's a leading quote, advance to the trailing quote 986/* if there's a leading quote, advance to the trailing quote
1156 set the trailing quote to '\x0' 987 set the trailing quote to '\x0'
1157 if the string continues, advance beyond the comma */ 988 if the string continues, advance beyond the comma */
1158 989
1159char * 990char *get_next_argument(char *str) {
1160nextarg (char *str)
1161{
1162 if (str[0] == '\'') { 991 if (str[0] == '\'') {
1163 str[0] = 0; 992 str[0] = 0;
1164 if (strlen (str) > 1) { 993 if (strlen(str) > 1) {
1165 str = strstr (str + 1, "'"); 994 str = strstr(str + 1, "'");
1166 return (++str); 995 return (++str);
1167 } 996 }
1168 else { 997 return NULL;
1169 return NULL;
1170 }
1171 } 998 }
1172 if (str[0] == ',') { 999 if (str[0] == ',') {
1173 str[0] = 0; 1000 str[0] = 0;
1174 if (strlen (str) > 1) { 1001 if (strlen(str) > 1) {
1175 return (++str); 1002 return (++str);
1176 } 1003 }
1177 else { 1004 return NULL;
1178 return NULL;
1179 }
1180 } 1005 }
1181 if ((str = strstr (str, ",")) && strlen (str) > 1) { 1006 if ((str = strstr(str, ",")) && strlen(str) > 1) {
1182 str[0] = 0; 1007 str[0] = 0;
1183 return (++str); 1008 return (++str);
1184 } 1009 }
1185 return NULL; 1010 return NULL;
1186} 1011}
1187 1012
1013void print_help(void) {
1014 print_revision(progname, NP_VERSION);
1188 1015
1016 printf(COPYRIGHT, copyright, email);
1189 1017
1190/* multiply result (values 0 < n < 1 work as divider) */ 1018 printf("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1191char *
1192multiply (char *str)
1193{
1194 char *endptr;
1195 double val;
1196 char *conv = "%f";
1197
1198 if(multiplier == 1)
1199 return(str);
1200
1201 if(verbose>2)
1202 printf(" multiply input: %s\n", str);
1203
1204 val = strtod (str, &endptr);
1205 if ((val == 0.0) && (endptr == str)) {
1206 die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, str);
1207 }
1208
1209 if(verbose>2)
1210 printf(" multiply extracted double: %f\n", val);
1211 val *= multiplier;
1212 if (fmtstr_set) {
1213 conv = fmtstr;
1214 }
1215 if (val == (int)val) {
1216 snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val);
1217 } else {
1218 if(verbose>2)
1219 printf(" multiply using format: %s\n", conv);
1220 snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val);
1221 }
1222 if(verbose>2)
1223 printf(" multiply result: %s\n", buffer);
1224 return buffer;
1225}
1226
1227
1228void
1229print_help (void)
1230{
1231 print_revision (progname, NP_VERSION);
1232
1233 printf (COPYRIGHT, copyright, email);
1234
1235 printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1236 1019
1237 printf ("\n\n"); 1020 printf("\n\n");
1238 1021
1239 print_usage (); 1022 print_usage();
1240 1023
1241 printf (UT_HELP_VRSN); 1024 printf(UT_HELP_VRSN);
1242 printf (UT_EXTRA_OPTS); 1025 printf(UT_EXTRA_OPTS);
1243 printf (UT_IPv46); 1026 printf(UT_HOST_PORT, 'p', DEFAULT_PORT);
1244
1245 printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1246 1027
1247 /* SNMP and Authentication Protocol */ 1028 /* SNMP and Authentication Protocol */
1248 printf (" %s\n", "-n, --next"); 1029 printf(" %s\n", "-n, --next");
1249 printf (" %s\n", _("Use SNMP GETNEXT instead of SNMP GET")); 1030 printf(" %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1250 printf (" %s\n", "-P, --protocol=[1|2c|3]"); 1031 printf(" %s\n", "-P, --protocol=[1|2c|3]");
1251 printf (" %s\n", _("SNMP protocol version")); 1032 printf(" %s\n", _("SNMP protocol version"));
1252 printf (" %s\n", "-N, --context=CONTEXT"); 1033 printf(" %s\n", "-N, --context=CONTEXT");
1253 printf (" %s\n", _("SNMPv3 context")); 1034 printf(" %s\n", _("SNMPv3 context"));
1254 printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); 1035 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1255 printf (" %s\n", _("SNMPv3 securityLevel")); 1036 printf(" %s\n", _("SNMPv3 securityLevel"));
1256 printf (" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); 1037 printf(" %s\n", "-a, --authproto=[MD5|SHA]");
1257 printf (" %s\n", _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); 1038 printf(" %s\n", _("SNMPv3 auth proto"));
1258 printf (" %s\n", _("if < 5.8 SHA (1) and MD5 should be available, if >= 5.8 additionally SHA-224, SHA-256, SHA-384 and SHA-512")); 1039#ifdef HAVE_USM_DES_PRIV_PROTOCOL
1259 printf (" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); 1040 printf(" %s\n", "-x, --privproto=[DES|AES]");
1260 printf (" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools")); 1041 printf(" %s\n", _("SNMPv3 priv proto (default DES)"));
1261 printf (" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); 1042#else
1043 printf(" %s\n", "-x, --privproto=[AES]");
1044 printf(" %s\n", _("SNMPv3 priv proto (default AES)"));
1045#endif
1262 1046
1263 /* Authentication Tokens*/ 1047 /* Authentication Tokens*/
1264 printf (" %s\n", "-C, --community=STRING"); 1048 printf(" %s\n", "-C, --community=STRING");
1265 printf (" %s ", _("Optional community string for SNMP communication")); 1049 printf(" %s ", _("Optional community string for SNMP communication"));
1266 printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY); 1050 printf("(%s \"%s\")\n", _("default is"), DEFAULT_COMMUNITY);
1267 printf (" %s\n", "-U, --secname=USERNAME"); 1051 printf(" %s\n", "-U, --secname=USERNAME");
1268 printf (" %s\n", _("SNMPv3 username")); 1052 printf(" %s\n", _("SNMPv3 username"));
1269 printf (" %s\n", "-A, --authpasswd=PASSWORD"); 1053 printf(" %s\n", "-A, --authpasswd=PASSWORD");
1270 printf (" %s\n", _("SNMPv3 authentication password")); 1054 printf(" %s\n", _("SNMPv3 authentication password"));
1271 printf (" %s\n", "-X, --privpasswd=PASSWORD"); 1055 printf(" %s\n", "-X, --privpasswd=PASSWORD");
1272 printf (" %s\n", _("SNMPv3 privacy password")); 1056 printf(" %s\n", _("SNMPv3 privacy password"));
1057 printf(" %s\n", "--connection-prefix");
1058 printf(" Connection prefix, may be one of udp, udp6, tcp, unix, ipx, udp6, udpv6, udpipv6, "
1059 "tcp6, tcpv6, tcpipv6, tls, dtls - "
1060 "default is \"udp\"\n");
1273 1061
1274 /* OID Stuff */ 1062 /* OID Stuff */
1275 printf (" %s\n", "-o, --oid=OID(s)"); 1063 printf(" %s\n", "-o, --oid=OID(s)");
1276 printf (" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); 1064 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1277 printf (" %s\n", "-m, --miblist=STRING"); 1065 printf(" %s\n", "-m, --miblist=STRING");
1278 printf (" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); 1066 printf(" %s\n",
1279 printf (" %s\n", _("for symbolic OIDs.)")); 1067 _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1280 printf (" %s\n", "-d, --delimiter=STRING"); 1068 printf(" %s\n", _("for symbolic OIDs.)"));
1281 printf (" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER); 1069 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1282 printf (" %s\n", _("Any data on the right hand side of the delimiter is considered")); 1070 printf(" %s\n", _("to be the data that should be used in the evaluation."));
1283 printf (" %s\n", _("to be the data that should be used in the evaluation.")); 1071 printf(" %s\n", "-z, --nulloid=#");
1284 printf (" %s\n", "-z, --nulloid=#"); 1072 printf(" %s\n", _("If the check returns a 0 length string or NULL value"));
1285 printf (" %s\n", _("If the check returns a 0 length string or NULL value")); 1073 printf(" %s\n", _("This option allows you to choose what status you want it to exit"));
1286 printf (" %s\n", _("This option allows you to choose what status you want it to exit")); 1074 printf(" %s\n", _("Excluding this option renders the default exit of 3(STATE_UNKNOWN)"));
1287 printf (" %s\n", _("Excluding this option renders the default exit of 3(STATE_UNKNOWN)")); 1075 printf(" %s\n", _("0 = OK"));
1288 printf (" %s\n", _("0 = OK")); 1076 printf(" %s\n", _("1 = WARNING"));
1289 printf (" %s\n", _("1 = WARNING")); 1077 printf(" %s\n", _("2 = CRITICAL"));
1290 printf (" %s\n", _("2 = CRITICAL")); 1078 printf(" %s\n", _("3 = UNKNOWN"));
1291 printf (" %s\n", _("3 = UNKNOWN"));
1292 1079
1293 /* Tests Against Integers */ 1080 /* Tests Against Integers */
1294 printf (" %s\n", "-w, --warning=THRESHOLD(s)"); 1081 printf(" %s\n", "-w, --warning=THRESHOLD(s)");
1295 printf (" %s\n", _("Warning threshold range(s)")); 1082 printf(" %s\n", _("Warning threshold range(s)"));
1296 printf (" %s\n", "-c, --critical=THRESHOLD(s)"); 1083 printf(" %s\n", "-c, --critical=THRESHOLD(s)");
1297 printf (" %s\n", _("Critical threshold range(s)")); 1084 printf(" %s\n", _("Critical threshold range(s)"));
1298 printf (" %s\n", "--rate"); 1085 printf(" %s\n", "--offset=OFFSET");
1299 printf (" %s\n", _("Enable rate calculation. See 'Rate Calculation' below")); 1086 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1300 printf (" %s\n", "--rate-multiplier");
1301 printf (" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1302 printf (" %s\n", "--offset=OFFSET");
1303 printf (" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1304 1087
1305 /* Tests Against Strings */ 1088 /* Tests Against Strings */
1306 printf (" %s\n", "-s, --string=STRING"); 1089 printf(" %s\n", "-s, --string=STRING");
1307 printf (" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); 1090 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1308 printf (" %s\n", "-r, --ereg=REGEX"); 1091 printf(" %s\n", "-r, --ereg=REGEX");
1309 printf (" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); 1092 printf(" %s\n",
1310 printf (" %s\n", "-R, --eregi=REGEX"); 1093 _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1311 printf (" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); 1094 printf(" %s\n", "-R, --eregi=REGEX");
1312 printf (" %s\n", "--invert-search"); 1095 printf(" %s\n",
1313 printf (" %s\n", _("Invert search result (CRITICAL if found)")); 1096 _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1097 printf(" %s\n", "--invert-search");
1098 printf(" %s\n", _("Invert search result (CRITICAL if found)"));
1314 1099
1315 /* Output Formatting */ 1100 /* Output Formatting */
1316 printf (" %s\n", "-l, --label=STRING"); 1101 printf(" %s\n", "-l, --label=STRING");
1317 printf (" %s\n", _("Prefix label for output from plugin")); 1102 printf(" %s\n", _("Prefix label for output from plugin"));
1318 printf (" %s\n", "-u, --units=STRING"); 1103 printf(" %s\n", "-u, --units=STRING");
1319 printf (" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); 1104 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1320 printf (" %s\n", "-D, --output-delimiter=STRING"); 1105 printf(" %s\n", "-M, --multiplier=FLOAT");
1321 printf (" %s\n", _("Separates output on multiple OID requests")); 1106 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1322 printf (" %s\n", "-M, --multiplier=FLOAT"); 1107 printf(UT_OUTPUT_FORMAT);
1323 printf (" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1324 printf (" %s\n", "-f, --fmtstr=STRING");
1325 printf (" %s\n", _("C-style format string for float values (see option -M)"));
1326
1327 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1328 printf (" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5"));
1329 printf (" %s\n", "-e, --retries=INTEGER");
1330 printf (" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES);
1331
1332 printf (" %s\n", "-O, --perf-oids");
1333 printf (" %s\n", _("Label performance data with OIDs instead of --label's"));
1334
1335 printf (" %s\n", "--ignore-mib-parsing-errors");
1336 printf (" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files"));
1337
1338 printf (UT_VERBOSE);
1339
1340 printf ("\n");
1341 printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1342 printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1343 printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1344
1345 printf ("\n");
1346 printf ("%s\n", _("Notes:"));
1347 printf (" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1348 printf (" %s\n", _("list (lists with internal spaces must be quoted)."));
1349 1108
1350 printf(" -%s", UT_THRESHOLDS_NOTES); 1109 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1110 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: "
1111 "timeout_interval * retries + 5"));
1112 printf(" %s\n", "-e, --retries=INTEGER");
1113 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "),
1114 DEFAULT_RETRIES);
1115
1116 printf(" %s\n", "-O, --perf-oids");
1117 printf(" %s\n", _("Label performance data with OIDs instead of --label's"));
1351 1118
1352 printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); 1119 printf(" %s\n", "--ignore-mib-parsing-errors");
1353 printf (" %s\n", _("- Note that only one string and one regex may be checked at present")); 1120 printf(" %s\n", _("Do to not print errors encountered when parsing MIB files"));
1354 printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); 1121
1355 printf (" %s\n", _("returned from the SNMP query is an unsigned integer.")); 1122 printf(UT_VERBOSE);
1356 1123
1357 printf("\n"); 1124 printf("\n");
1358 printf("%s\n", _("Rate Calculation:")); 1125 printf("%s\n", _("This plugin relies (links against) on the NET-SNMP libraries."));
1359 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when")); 1126 printf("%s\n",
1360 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp")); 1127 _("if you don't have the libraries installed, you will need to download them from"));
1361 printf(" %s\n", _("saves the last state information in a file so that the rate per second")); 1128 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1362 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1363 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1364 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1365 printf(" %s\n", _("changing the arguments will create a new state file."));
1366
1367 printf (UT_SUPPORT);
1368}
1369 1129
1130 printf("\n");
1131 printf("%s\n", _("Notes:"));
1132 printf(" %s\n",
1133 _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1134 printf(" %s\n", _("list (lists with internal spaces must be quoted)."));
1370 1135
1136 printf(" -%s", UT_THRESHOLDS_NOTES);
1137
1138 printf(" %s\n",
1139 _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1140 printf(" %s\n", _("- Note that only one string and one regex may be checked at present"));
1141 printf(" %s\n",
1142 _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1143 printf(" %s\n", _("returned from the SNMP query is an unsigned integer."));
1144
1145 printf(UT_SUPPORT);
1146}
1371 1147
1372void 1148void print_usage(void) {
1373print_usage (void) 1149 printf("%s\n", _("Usage:"));
1374{ 1150 printf("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n", progname);
1375 printf ("%s\n", _("Usage:")); 1151 printf("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1376 printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname); 1152 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1377 printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n"); 1153 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n");
1378 printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); 1154 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1379 printf ("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n"); 1155 printf("[-M multiplier]\n");
1380 printf ("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1381 printf ("[-M multiplier [-f format]]\n");
1382} 1156}
diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c
new file mode 100644
index 00000000..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 34ef37b7..f6c8d551 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -1,103 +1,107 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ssh plugin 3 * Monitoring check_ssh plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_ssh plugin 10 * This file contains the check_ssh plugin
11* 11 *
12* Try to connect to an SSH server at specified server and port 12 * Try to connect to an SSH server at specified server and port
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "output.h"
32#include "perfdata.h"
33#include "states.h"
31const char *progname = "check_ssh"; 34const char *progname = "check_ssh";
32const char *copyright = "2000-2007"; 35const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 36const char *email = "devel@monitoring-plugins.org";
34 37
35#include "./common.h" 38#include "./common.h"
36#include "./netutils.h" 39#include "./netutils.h"
37#include "utils.h" 40#include "utils.h"
41#include "./check_ssh.d/config.h"
38 42
39#ifndef MSG_DONTWAIT 43#ifndef MSG_DONTWAIT
40#define MSG_DONTWAIT 0 44# define MSG_DONTWAIT 0
41#endif 45#endif
42 46
43#define SSH_DFL_PORT 22 47#define BUFF_SZ 256
44#define BUFF_SZ 256
45
46int port = -1;
47char *server_name = NULL;
48char *remote_version = NULL;
49char *remote_protocol = NULL;
50bool verbose = false;
51 48
52int process_arguments (int, char **); 49static bool verbose = false;
53int validate_arguments (void);
54void print_help (void);
55void print_usage (void);
56 50
57int ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol); 51typedef struct process_arguments_wrapper {
52 int errorcode;
53 check_ssh_config config;
54} process_arguments_wrapper;
58 55
56static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57static void print_help(void);
58void print_usage(void);
59 59
60int 60static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version,
61main (int argc, char **argv) 61 char *remote_protocol);
62{
63 int result = STATE_UNKNOWN;
64 62
65 setlocale (LC_ALL, ""); 63int main(int argc, char **argv) {
66 bindtextdomain (PACKAGE, LOCALEDIR); 64 setlocale(LC_ALL, "");
67 textdomain (PACKAGE); 65 bindtextdomain(PACKAGE, LOCALEDIR);
66 textdomain(PACKAGE);
68 67
69 /* Parse extra opts if any */ 68 /* Parse extra opts if any */
70 argv=np_extra_opts (&argc, argv, progname); 69 argv = np_extra_opts(&argc, argv, progname);
71 70
72 if (process_arguments (argc, argv) == ERROR) 71 process_arguments_wrapper tmp_config = process_arguments(argc, argv);
73 usage4 (_("Could not parse arguments"));
74 72
75 /* initialize alarm signal handling */ 73 if (tmp_config.errorcode == ERROR) {
76 signal (SIGALRM, socket_timeout_alarm_handler); 74 usage4(_("Could not parse arguments"));
75 }
77 76
78 alarm (socket_timeout); 77 check_ssh_config config = tmp_config.config;
78
79 mp_check overall = mp_check_init();
80 if (config.output_format_is_set) {
81 mp_set_format(config.output_format);
82 }
83
84 /* initialize alarm signal handling */
85 signal(SIGALRM, socket_timeout_alarm_handler);
86 alarm(socket_timeout);
79 87
80 /* ssh_connect exits if error is found */ 88 /* ssh_connect exits if error is found */
81 result = ssh_connect (server_name, port, remote_version, remote_protocol); 89 ssh_connect(&overall, config.server_name, config.port, config.remote_version,
90 config.remote_protocol);
82 91
83 alarm (0); 92 alarm(0);
84 93
85 return (result); 94 mp_exit(overall);
86} 95}
87 96
88 97#define output_format_index CHAR_MAX + 1
89 98
90/* process command-line arguments */ 99/* process command-line arguments */
91int 100process_arguments_wrapper process_arguments(int argc, char **argv) {
92process_arguments (int argc, char **argv)
93{
94 int c;
95
96 int option = 0;
97 static struct option longopts[] = { 101 static struct option longopts[] = {
98 {"help", no_argument, 0, 'h'}, 102 {"help", no_argument, 0, 'h'},
99 {"version", no_argument, 0, 'V'}, 103 {"version", no_argument, 0, 'V'},
100 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 104 {"host", required_argument, 0, 'H'}, /* backward compatibility */
101 {"hostname", required_argument, 0, 'H'}, 105 {"hostname", required_argument, 0, 'H'},
102 {"port", required_argument, 0, 'p'}, 106 {"port", required_argument, 0, 'p'},
103 {"use-ipv4", no_argument, 0, '4'}, 107 {"use-ipv4", no_argument, 0, '4'},
@@ -106,39 +110,52 @@ process_arguments (int argc, char **argv)
106 {"verbose", no_argument, 0, 'v'}, 110 {"verbose", no_argument, 0, 'v'},
107 {"remote-version", required_argument, 0, 'r'}, 111 {"remote-version", required_argument, 0, 'r'},
108 {"remote-protocol", required_argument, 0, 'P'}, 112 {"remote-protocol", required_argument, 0, 'P'},
109 {0, 0, 0, 0} 113 {"output-format", required_argument, 0, output_format_index},
114 {0, 0, 0, 0}};
115
116 process_arguments_wrapper result = {
117 .config = check_ssh_config_init(),
118 .errorcode = OK,
110 }; 119 };
111 120
112 if (argc < 2) 121 if (argc < 2) {
113 return ERROR; 122 result.errorcode = ERROR;
123 return result;
124 }
114 125
115 for (c = 1; c < argc; c++) 126 for (int i = 1; i < argc; i++) {
116 if (strcmp ("-to", argv[c]) == 0) 127 if (strcmp("-to", argv[i]) == 0) {
117 strcpy (argv[c], "-t"); 128 strcpy(argv[i], "-t");
129 }
130 }
118 131
119 while (1) { 132 int option_char;
120 c = getopt_long (argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option); 133 while (true) {
134 int option = 0;
135 option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option);
121 136
122 if (c == -1 || c == EOF) 137 if (option_char == -1 || option_char == EOF) {
123 break; 138 break;
139 }
124 140
125 switch (c) { 141 switch (option_char) {
126 case '?': /* help */ 142 case '?': /* help */
127 usage5 (); 143 usage5();
128 case 'V': /* version */ 144 case 'V': /* version */
129 print_revision (progname, NP_VERSION); 145 print_revision(progname, NP_VERSION);
130 exit (STATE_UNKNOWN); 146 exit(STATE_UNKNOWN);
131 case 'h': /* help */ 147 case 'h': /* help */
132 print_help (); 148 print_help();
133 exit (STATE_UNKNOWN); 149 exit(STATE_UNKNOWN);
134 case 'v': /* verbose */ 150 case 'v': /* verbose */
135 verbose = true; 151 verbose = true;
136 break; 152 break;
137 case 't': /* timeout period */ 153 case 't': /* timeout period */
138 if (!is_integer (optarg)) 154 if (!is_intpos(optarg)) {
139 usage2 (_("Timeout interval must be a positive integer"), optarg); 155 usage2(_("Timeout interval must be a positive integer"), optarg);
140 else 156 } else {
141 socket_timeout = atoi (optarg); 157 socket_timeout = (unsigned int)atoi(optarg);
158 }
142 break; 159 break;
143 case '4': 160 case '4':
144 address_family = AF_INET; 161 address_family = AF_INET;
@@ -147,139 +164,153 @@ process_arguments (int argc, char **argv)
147#ifdef USE_IPV6 164#ifdef USE_IPV6
148 address_family = AF_INET6; 165 address_family = AF_INET6;
149#else 166#else
150 usage4 (_("IPv6 support not available")); 167 usage4(_("IPv6 support not available"));
151#endif 168#endif
152 break; 169 break;
153 case 'r': /* remote version */ 170 case 'r': /* remote version */
154 remote_version = optarg; 171 result.config.remote_version = optarg;
155 break; 172 break;
156 case 'P': /* remote version */ 173 case 'P': /* remote version */
157 remote_protocol = optarg; 174 result.config.remote_protocol = optarg;
158 break; 175 break;
159 case 'H': /* host */ 176 case 'H': /* host */
160 if (!is_host (optarg)) 177 if (!is_host(optarg)) {
161 usage2 (_("Invalid hostname/address"), optarg); 178 usage2(_("Invalid hostname/address"), optarg);
162 server_name = optarg; 179 }
180 result.config.server_name = optarg;
163 break; 181 break;
164 case 'p': /* port */ 182 case 'p': /* port */
165 if (is_intpos (optarg)) { 183 if (is_intpos(optarg)) {
166 port = atoi (optarg); 184 result.config.port = atoi(optarg);
185 } else {
186 usage2(_("Port number must be a positive integer"), optarg);
167 } 187 }
168 else { 188 break;
169 usage2 (_("Port number must be a positive integer"), optarg); 189 case output_format_index: {
190 parsed_output_format parser = mp_parse_output_format(optarg);
191 if (!parser.parsing_success) {
192 // TODO List all available formats here, maybe add anothoer usage function
193 printf("Invalid output format: %s\n", optarg);
194 exit(STATE_UNKNOWN);
170 } 195 }
196
197 result.config.output_format_is_set = true;
198 result.config.output_format = parser.output_format;
199 break;
200 }
171 } 201 }
172 } 202 }
173 203
174 c = optind; 204 option_char = optind;
175 if (server_name == NULL && c < argc) { 205 if (result.config.server_name == NULL && option_char < argc) {
176 if (is_host (argv[c])) { 206 if (is_host(argv[option_char])) {
177 server_name = argv[c++]; 207 result.config.server_name = argv[option_char++];
178 } 208 }
179 } 209 }
180 210
181 if (port == -1 && c < argc) { 211 if (result.config.port == -1 && option_char < argc) {
182 if (is_intpos (argv[c])) { 212 if (is_intpos(argv[option_char])) {
183 port = atoi (argv[c++]); 213 result.config.port = atoi(argv[option_char++]);
184 } 214 } else {
185 else { 215 print_usage();
186 print_usage (); 216 exit(STATE_UNKNOWN);
187 exit (STATE_UNKNOWN);
188 } 217 }
189 } 218 }
190 219
191 return validate_arguments (); 220 if (result.config.server_name == NULL) {
192} 221 result.errorcode = ERROR;
222 return result;
223 }
193 224
194int 225 return result;
195validate_arguments (void)
196{
197 if (server_name == NULL)
198 return ERROR;
199 if (port == -1) /* funky, but allows -p to override stray integer in args */
200 port = SSH_DFL_PORT;
201 return OK;
202} 226}
203 227
204
205/************************************************************************ 228/************************************************************************
206* 229 *
207* Try to connect to SSH server at specified server and port 230 * Try to connect to SSH server at specified server and port
208* 231 *
209*-----------------------------------------------------------------------*/ 232 *-----------------------------------------------------------------------*/
210
211
212int
213ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol)
214{
215 int sd;
216 int result;
217 int len = 0;
218 ssize_t recv_ret = 0;
219 char *version_control_string = NULL;
220 char *buffer = NULL;
221 char *ssh_proto = NULL;
222 char *ssh_server = NULL;
223 static char *rev_no = VERSION;
224 struct timeval tv;
225 double elapsed_time;
226 233
234int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version,
235 char *desired_remote_protocol) {
236 struct timeval tv;
227 gettimeofday(&tv, NULL); 237 gettimeofday(&tv, NULL);
228 238
229 result = my_tcp_connect (haddr, hport, &sd); 239 int socket;
240 int result = my_tcp_connect(haddr, hport, &socket);
230 241
231 if (result != STATE_OK) 242 mp_subcheck connection_sc = mp_subcheck_init();
243 if (result != STATE_OK) {
244 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
245 xasprintf(&connection_sc.output,
246 "Failed to establish TCP connection to Host %s and Port %d", haddr, hport);
247 mp_add_subcheck_to_check(overall, connection_sc);
232 return result; 248 return result;
249 }
233 250
234 char *output = (char *) calloc (BUFF_SZ + 1, sizeof(char)); 251 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char));
235 252 char *buffer = NULL;
236 unsigned int iteration = 0; 253 ssize_t recv_ret = 0;
237 ssize_t byte_offset = 0; 254 char *version_control_string = NULL;
238 255 size_t byte_offset = 0;
239 while ((version_control_string == NULL) && (recv_ret = recv(sd, output+byte_offset, BUFF_SZ - byte_offset, 0) > 0)) { 256 while ((version_control_string == NULL) &&
257 (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset),
258 0) > 0)) {
240 259
241 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*/
242 byte_offset = 0; 261 byte_offset = 0;
243 262
244 char *index = NULL; 263 char *index = NULL;
245 while ((index = strchr(output+byte_offset, '\n')) != NULL) { 264 while ((index = strchr(output + byte_offset, '\n')) != NULL) {
246 /*Partition the buffer so that this line is a separate string, 265 /*Partition the buffer so that this line is a separate string,
247 * by replacing the newline with NUL*/ 266 * by replacing the newline with NUL*/
248 output[(index - output)] = '\0'; 267 output[(index - output)] = '\0';
249 len = strlen(output + byte_offset); 268 size_t len = strlen(output + byte_offset);
250 269
251 if ((len >= 4) && (strncmp (output+byte_offset, "SSH-", 4) == 0)) { 270 if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) {
252 /*if the string starts with SSH-, this _should_ be a valid version control string*/ 271 /*if the string starts with SSH-, this _should_ be a valid version control
253 version_control_string = output+byte_offset; 272 * string*/
254 break; 273 version_control_string = output + byte_offset;
274 break;
255 } 275 }
256 276
257 /*the start of the next line (if one exists) will be after the current one (+ NUL)*/ 277 /*the start of the next line (if one exists) will be after the current one (+ NUL)*/
258 byte_offset += (len + 1); 278 byte_offset += (len + 1);
259 } 279 }
260 280
261 if(version_control_string == NULL) { 281 if (version_control_string == NULL) {
262 /* move unconsumed data to beginning of buffer, null rest */ 282 /* move unconsumed data to beginning of buffer */
263 memmove((void *)output, (void *)output+byte_offset+1, BUFF_SZ - len+1); 283 memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset);
264 memset(output+byte_offset+1, 0, BUFF_SZ-byte_offset+1);
265 284
266 /*start reading from end of current line chunk on next recv*/ 285 /*start reading from end of current line chunk on next recv*/
267 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);
268 } 290 }
269 } else { 291 } else {
270 byte_offset += recv_ret; 292 byte_offset += (size_t)recv_ret;
271 } 293 }
272 } 294 }
273 295
274 if (recv_ret < 0) { 296 if (recv_ret < 0) {
275 printf("SSH CRITICAL - %s", strerror(errno)); 297 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
276 exit(STATE_CRITICAL); 298 xasprintf(&connection_sc.output, "%s - %s", "SSH CRITICAL - ", strerror(errno));
299 mp_add_subcheck_to_check(overall, connection_sc);
300 return OK;
277 } 301 }
278 302
279 if (version_control_string == NULL) { 303 if (version_control_string == NULL) {
280 printf("SSH CRITICAL - No version control string received"); 304 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
281 exit(STATE_CRITICAL); 305 xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - No version control string received");
306 mp_add_subcheck_to_check(overall, connection_sc);
307 return OK;
282 } 308 }
309
310 connection_sc = mp_set_subcheck_state(connection_sc, STATE_OK);
311 xasprintf(&connection_sc.output, "%s", "Initial connection succeeded");
312 mp_add_subcheck_to_check(overall, connection_sc);
313
283 /* 314 /*
284 * "When the connection has been established, both sides MUST send an 315 * "When the connection has been established, both sides MUST send an
285 * identification string. This identification string MUST be 316 * identification string. This identification string MUST be
@@ -287,10 +318,12 @@ ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol
287 * SSH-protoversion-softwareversion SP comments CR LF" 318 * SSH-protoversion-softwareversion SP comments CR LF"
288 * - RFC 4253:4.2 319 * - RFC 4253:4.2
289 */ 320 */
290 strip (version_control_string); 321 strip(version_control_string);
291 if (verbose) 322 if (verbose) {
292 printf ("%s\n", version_control_string); 323 printf("%s\n", version_control_string);
293 ssh_proto = version_control_string + 4; 324 }
325
326 char *ssh_proto = version_control_string + 4;
294 327
295 /* 328 /*
296 * We assume the protoversion is of the form Major.Minor, although 329 * We assume the protoversion is of the form Major.Minor, although
@@ -308,7 +341,8 @@ ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol
308 * "1.x" (e.g., "1.5" or "1.3")." 341 * "1.x" (e.g., "1.5" or "1.3")."
309 * - RFC 4253:5 342 * - RFC 4253:5
310 */ 343 */
311 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) */
312 346
313 /* 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
314 * (which is NOT part of the server name/version)*/ 348 * (which is NOT part of the server name/version)*/
@@ -316,88 +350,105 @@ ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol
316 if (tmp) { 350 if (tmp) {
317 ssh_server[tmp - ssh_server] = '\0'; 351 ssh_server[tmp - ssh_server] = '\0';
318 } 352 }
353
354 mp_subcheck protocol_validity_sc = mp_subcheck_init();
319 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { 355 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) {
320 printf(_("SSH CRITICAL - Invalid protocol version control string %s\n"), version_control_string); 356 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL);
321 exit (STATE_CRITICAL); 357 xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s",
358 version_control_string);
359 mp_add_subcheck_to_check(overall, protocol_validity_sc);
360 return OK;
322 } 361 }
323 ssh_proto[strspn (ssh_proto, "0123456789. ")] = 0; 362
324 363 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK);
325 xasprintf (&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no); 364 xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s",
326 send (sd, buffer, strlen (buffer), MSG_DONTWAIT); 365 version_control_string);
327 if (verbose) 366 mp_add_subcheck_to_check(overall, protocol_validity_sc);
328 printf ("%s\n", buffer); 367
329 368 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0;
330 if (remote_version && strcmp(remote_version, ssh_server)) { 369
331 printf 370 static char *rev_no = VERSION;
332 (_("SSH CRITICAL - %s (protocol %s) version mismatch, expected '%s'\n"), 371 xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
333 ssh_server, ssh_proto, remote_version); 372 send(socket, buffer, strlen(buffer), MSG_DONTWAIT);
334 close(sd); 373 if (verbose) {
335 exit (STATE_CRITICAL); 374 printf("%s\n", buffer);
336 } 375 }
337 376
338 if (remote_protocol && strcmp(remote_protocol, ssh_proto)) { 377 if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) {
339 printf 378 mp_subcheck remote_version_sc = mp_subcheck_init();
340 (_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s' | %s\n"), 379 remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL);
341 ssh_server, ssh_proto, remote_protocol, fperfdata("time", elapsed_time, "s", 380 xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"),
342 false, 0, false, 0, true, 0, true, (int)socket_timeout)); 381 ssh_server, ssh_proto, desired_remote_version);
343 close(sd); 382 close(socket);
344 exit (STATE_CRITICAL); 383 mp_add_subcheck_to_check(overall, remote_version_sc);
384 return OK;
345 } 385 }
346 elapsed_time = (double)deltime(tv) / 1.0e6;
347
348 printf
349 (_("SSH OK - %s (protocol %s) | %s\n"),
350 ssh_server, ssh_proto, fperfdata("time", elapsed_time, "s",
351 false, 0, false, 0, true, 0, true, (int)socket_timeout));
352 close(sd);
353 exit (STATE_OK);
354}
355 386
387 double elapsed_time = (double)deltime(tv) / 1.0e6;
388 mp_perfdata time_pd = perfdata_init();
389 time_pd.value = mp_create_pd_value(elapsed_time);
390 time_pd.label = "time";
391 time_pd.max_present = true;
392 time_pd.max = mp_create_pd_value(socket_timeout);
393
394 mp_subcheck protocol_version_sc = mp_subcheck_init();
395 mp_add_perfdata_to_subcheck(&protocol_version_sc, time_pd);
396
397 if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) {
398 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL);
399 xasprintf(&protocol_version_sc.output,
400 _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server,
401 ssh_proto, desired_remote_protocol);
402 } else {
403 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK);
404 xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)",
405 ssh_server, ssh_proto);
406 }
356 407
408 mp_add_subcheck_to_check(overall, protocol_version_sc);
409 close(socket);
410 return OK;
411}
357 412
358void 413void print_help(void) {
359print_help (void)
360{
361 char *myport; 414 char *myport;
362 xasprintf (&myport, "%d", SSH_DFL_PORT); 415 xasprintf(&myport, "%d", default_ssh_port);
363 416
364 print_revision (progname, NP_VERSION); 417 print_revision(progname, NP_VERSION);
365 418
366 printf ("Copyright (c) 1999 Remi Paulmier <remi@sinfomic.fr>\n"); 419 printf("Copyright (c) 1999 Remi Paulmier <remi@sinfomic.fr>\n");
367 printf (COPYRIGHT, copyright, email); 420 printf(COPYRIGHT, copyright, email);
368 421
369 printf ("%s\n", _("Try to connect to an SSH server at specified server and port")); 422 printf("%s\n", _("Try to connect to an SSH server at specified server and port"));
370 423
371 printf ("\n\n"); 424 printf("\n\n");
372 425
373 print_usage (); 426 print_usage();
374 427
375 printf (UT_HELP_VRSN); 428 printf(UT_HELP_VRSN);
376 printf (UT_EXTRA_OPTS); 429 printf(UT_EXTRA_OPTS);
377 430
378 printf (UT_HOST_PORT, 'p', myport); 431 printf(UT_HOST_PORT, 'p', myport);
379 432
380 printf (UT_IPv46); 433 printf(UT_IPv46);
381 434
382 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 435 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
383 436
384 printf (" %s\n", "-r, --remote-version=STRING"); 437 printf(" %s\n", "-r, --remote-version=STRING");
385 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)"));
386 440
387 printf (" %s\n", "-P, --remote-protocol=STRING"); 441 printf(" %s\n", "-P, --remote-protocol=STRING");
388 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)"));
443 printf(UT_OUTPUT_FORMAT);
389 444
390 printf (UT_VERBOSE); 445 printf(UT_VERBOSE);
391 446
392 printf (UT_SUPPORT); 447 printf(UT_SUPPORT);
393} 448}
394 449
395 450void print_usage(void) {
396 451 printf("%s\n", _("Usage:"));
397void 452 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n",
398print_usage (void) 453 progname);
399{
400 printf ("%s\n", _("Usage:"));
401 printf ("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] <host>\n", progname);
402} 454}
403
diff --git a/plugins/check_ssh.d/config.h b/plugins/check_ssh.d/config.h
new file mode 100644
index 00000000..c150fd30
--- /dev/null
+++ b/plugins/check_ssh.d/config.h
@@ -0,0 +1,29 @@
1#pragma once
2
3#include <stddef.h>
4#include "../../lib/monitoringplug.h"
5
6const int default_ssh_port = 22;
7
8typedef struct check_ssh_config {
9 int port;
10 char *server_name;
11 char *remote_version;
12 char *remote_protocol;
13
14 bool output_format_is_set;
15 mp_output_format output_format;
16} check_ssh_config;
17
18check_ssh_config check_ssh_config_init(void) {
19 check_ssh_config tmp = {
20 .port = default_ssh_port,
21 .server_name = NULL,
22 .remote_version = NULL,
23 .remote_protocol = NULL,
24
25 .output_format_is_set = false,
26 };
27
28 return tmp;
29}
diff --git a/plugins/check_swap.c b/plugins/check_swap.c
index e7ee785d..dbf53a00 100644
--- a/plugins/check_swap.c
+++ b/plugins/check_swap.c
@@ -1,607 +1,415 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_swap plugin 3 * Monitoring check_swap plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net) 6 * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net)
7* Copyright (c) 2000-2024 Monitoring Plugins Development Team 7 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_swap plugin 11 * This file contains the check_swap plugin
12* 12 *
13* 13 *
14* This program is free software: you can redistribute it and/or modify 14 * This program is free software: you can redistribute it and/or modify
15* it under the terms of the GNU General Public License as published by 15 * it under the terms of the GNU General Public License as published by
16* the Free Software Foundation, either version 3 of the License, or 16 * the Free Software Foundation, either version 3 of the License, or
17* (at your option) any later version. 17 * (at your option) any later version.
18* 18 *
19* This program is distributed in the hope that it will be useful, 19 * This program is distributed in the hope that it will be useful,
20* but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22* GNU General Public License for more details. 22 * GNU General Public License for more details.
23* 23 *
24* You should have received a copy of the GNU General Public License 24 * You should have received a copy of the GNU General Public License
25* along with this program. If not, see <http://www.gnu.org/licenses/>. 25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26* 26 *
27* 27 *
28*****************************************************************************/ 28 *****************************************************************************/
29
30const char *progname = "check_swap";
31const char *copyright = "2000-2024";
32const char *email = "devel@monitoring-plugins.org";
33 29
34#include "common.h" 30#include "common.h"
35#include "popen.h" 31#include "output.h"
36#include "utils.h" 32#include "states.h"
37 33#include <limits.h>
38#ifdef HAVE_DECL_SWAPCTL 34#ifdef HAVE_DECL_SWAPCTL
39# ifdef HAVE_SYS_PARAM_H 35# ifdef HAVE_SYS_PARAM_H
40# include <sys/param.h> 36# include <sys/param.h>
41# endif 37# endif
42# ifdef HAVE_SYS_SWAP_H 38# ifdef HAVE_SYS_SWAP_H
43# include <sys/swap.h> 39# include <sys/swap.h>
44# endif 40# endif
45# ifdef HAVE_SYS_STAT_H 41# ifdef HAVE_SYS_STAT_H
46# include <sys/stat.h> 42# include <sys/stat.h>
47# endif 43# endif
48#endif 44#endif
49 45
50#ifndef SWAP_CONVERSION 46#include <stdint.h>
51# define SWAP_CONVERSION 1 47#include "./check_swap.d/check_swap.h"
52#endif 48#include "./utils.h"
53 49
54typedef struct { 50typedef struct {
55 bool is_percentage; 51 int errorcode;
56 uint64_t value; 52 swap_config config;
57} threshold; 53} swap_config_wrapper;
58 54
59int check_swap (float free_swap_mb, float total_swap_mb); 55static swap_config_wrapper process_arguments(int argc, char **argv);
60int process_arguments (int argc, char **argv); 56void print_usage(void);
61int validate_arguments (void); 57static void print_help(swap_config /*config*/);
62void print_usage (void); 58
63void print_help (void);
64
65threshold warn;
66threshold crit;
67int verbose; 59int verbose;
68bool allswaps = false;
69int no_swap_state = STATE_CRITICAL;
70
71int
72main (int argc, char **argv)
73{
74 unsigned int percent_used, percent;
75 uint64_t total_swap_mb = 0, used_swap_mb = 0, free_swap_mb = 0;
76 uint64_t dsktotal_mb = 0, dskused_mb = 0, dskfree_mb = 0;
77 uint64_t tmp_KB = 0;
78 int result = STATE_UNKNOWN;
79 char input_buffer[MAX_INPUT_BUFFER];
80#ifdef HAVE_PROC_MEMINFO
81 FILE *fp;
82#else
83 int conv_factor = SWAP_CONVERSION;
84# ifdef HAVE_SWAP
85 char *temp_buffer;
86 char *swap_command;
87 char *swap_format;
88# else
89# ifdef HAVE_DECL_SWAPCTL
90 int i=0, nswaps=0, swapctl_res=0;
91# ifdef CHECK_SWAP_SWAPCTL_SVR4
92 swaptbl_t *tbl=NULL;
93 swapent_t *ent=NULL;
94# else
95# ifdef CHECK_SWAP_SWAPCTL_BSD
96 struct swapent *ent;
97# endif /* CHECK_SWAP_SWAPCTL_BSD */
98# endif /* CHECK_SWAP_SWAPCTL_SVR4 */
99# endif /* HAVE_DECL_SWAPCTL */
100# endif
101#endif
102 char str[32];
103 char *status;
104 60
105 setlocale (LC_ALL, ""); 61#define HUNDRED_PERCENT 100
106 bindtextdomain (PACKAGE, LOCALEDIR); 62
107 textdomain (PACKAGE); 63#define BYTES_TO_KiB(number) (number / 1024)
64#define BYTES_TO_MiB(number) (BYTES_TO_KiB(number) / 1024)
65
66const char *progname = "check_swap";
67const char *copyright = "2000-2024";
68const char *email = "devel@monitoring-plugins.org";
108 69
109 status = strdup (""); 70int main(int argc, char **argv) {
71 setlocale(LC_ALL, "");
72 bindtextdomain(PACKAGE, LOCALEDIR);
73 textdomain(PACKAGE);
110 74
111 /* Parse extra opts if any */ 75 /* Parse extra opts if any */
112 argv=np_extra_opts (&argc, argv, progname); 76 argv = np_extra_opts(&argc, argv, progname);
113 77
114 if (process_arguments (argc, argv) == ERROR) 78 swap_config_wrapper tmp = process_arguments(argc, argv);
115 usage4 (_("Could not parse arguments"));
116 79
117#ifdef HAVE_PROC_MEMINFO 80 if (tmp.errorcode != OK) {
118 if (verbose >= 3) { 81 usage4(_("Could not parse arguments"));
119 printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO);
120 } 82 }
121 fp = fopen (PROC_MEMINFO, "r");
122 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) {
123 /*
124 * The following sscanf call looks for a line looking like: "Swap: 123 123 123"
125 * On which kind of system this format exists, I can not say, but I wanted to
126 * document this for people who are not adapt with sscanf anymore, like me
127 */
128 if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &dsktotal_mb, &dskused_mb, &dskfree_mb) == 3) {
129 dsktotal_mb = dsktotal_mb / (1024 * 1024); /* Apply conversion */
130 dskused_mb = dskused_mb / (1024 * 1024);
131 dskfree_mb = dskfree_mb / (1024 * 1024);
132 total_swap_mb += dsktotal_mb;
133 used_swap_mb += dskused_mb;
134 free_swap_mb += dskfree_mb;
135 if (allswaps) {
136 if (dsktotal_mb == 0)
137 percent=100.0;
138 else
139 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
140 result = max_state (result, check_swap (dskfree_mb, dsktotal_mb));
141 if (verbose)
142 xasprintf (&status, "%s [%lu (%d%%)]", status, dskfree_mb, 100 - percent);
143 }
144 }
145 83
146 /* 84 swap_config config = tmp.config;
147 * The following sscanf call looks for lines looking like: "SwapTotal: 123" and "SwapFree: 123"
148 * This format exists at least on Debian Linux with a 5.* kernel
149 */
150 else if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu %*[k]%*[B]", str, &tmp_KB)) {
151 if (verbose >= 3) {
152 printf("Got %s with %lu\n", str, tmp_KB);
153 }
154 /* I think this part is always in Kb, so convert to mb */
155 if (strcmp ("Total", str) == 0) {
156 dsktotal_mb = tmp_KB / 1024;
157 }
158 else if (strcmp ("Free", str) == 0) {
159 dskfree_mb = dskfree_mb + tmp_KB / 1024;
160 }
161 else if (strcmp ("Cached", str) == 0) {
162 dskfree_mb = dskfree_mb + tmp_KB / 1024;
163 }
164 }
165 }
166 fclose(fp);
167 dskused_mb = dsktotal_mb - dskfree_mb;
168 total_swap_mb = dsktotal_mb;
169 used_swap_mb = dskused_mb;
170 free_swap_mb = dskfree_mb;
171#else
172# ifdef HAVE_SWAP
173 xasprintf(&swap_command, "%s", SWAP_COMMAND);
174 xasprintf(&swap_format, "%s", SWAP_FORMAT);
175
176/* These override the command used if a summary (and thus ! allswaps) is required */
177/* The summary flag returns more accurate information about swap usage on these OSes */
178# ifdef _AIX
179 if (!allswaps) {
180 xasprintf(&swap_command, "%s", "/usr/sbin/lsps -s");
181 xasprintf(&swap_format, "%s", "%lu%*s %lu");
182 conv_factor = 1;
183 }
184# endif
185 85
186 if (verbose >= 2) 86 swap_result data = get_swap_data(config);
187 printf (_("Command: %s\n"), swap_command);
188 if (verbose >= 3)
189 printf (_("Format: %s\n"), swap_format);
190 87
191 child_process = spopen (swap_command); 88 if (data.errorcode != STATE_OK) {
192 if (child_process == NULL) { 89 puts("SWAP UNKNOWN - Failed to retrieve Swap usage");
193 printf (_("Could not open pipe: %s\n"), swap_command); 90 exit(STATE_UNKNOWN);
194 return STATE_UNKNOWN;
195 } 91 }
196 92
197 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 93 if (verbose) {
198 if (child_stderr == NULL) 94 printf("Swap retrieval result:\n"
199 printf (_("Could not open stderr for %s\n"), swap_command); 95 "\tFree: %llu\n"
200 96 "\tUsed: %llu\n"
201 sprintf (str, "%s", ""); 97 "\tTotal: %llu\n",
202 /* read 1st line */ 98 data.metrics.free, data.metrics.used, data.metrics.total);
203 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
204 if (strcmp (swap_format, "") == 0) {
205 temp_buffer = strtok (input_buffer, " \n");
206 while (temp_buffer) {
207 if (strstr (temp_buffer, "blocks"))
208 sprintf (str, "%s %s", str, "%lu");
209 else if (strstr (temp_buffer, "dskfree"))
210 sprintf (str, "%s %s", str, "%lu");
211 else
212 sprintf (str, "%s %s", str, "%*s");
213 temp_buffer = strtok (NULL, " \n");
214 }
215 } 99 }
216 100
217/* If different swap command is used for summary switch, need to read format differently */ 101 double percent_used;
218# ifdef _AIX 102 mp_check overall = mp_check_init();
219 if (!allswaps) { 103 if (config.output_format_is_set) {
220 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */ 104 mp_set_format(config.output_format);
221 sscanf (input_buffer, swap_format, &total_swap_mb, &used_swap_mb);
222 free_swap_mb = total_swap_mb * (100 - used_swap_mb) /100;
223 used_swap_mb = total_swap_mb - free_swap_mb;
224 if (verbose >= 3)
225 printf (_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb);
226 } else {
227# endif
228 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
229 sscanf (input_buffer, swap_format, &dsktotal_mb, &dskfree_mb);
230
231 dsktotal_mb = dsktotal_mb / conv_factor;
232 /* AIX lists percent used, so this converts to dskfree in MBs */
233# ifdef _AIX
234 dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100;
235# else
236 dskfree_mb = dskfree_mb / conv_factor;
237# endif
238 if (verbose >= 3)
239 printf (_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb);
240
241 dskused_mb = dsktotal_mb - dskfree_mb;
242 total_swap_mb += dsktotal_mb;
243 used_swap_mb += dskused_mb;
244 free_swap_mb += dskfree_mb;
245 if (allswaps) {
246 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
247 result = max_state (result, check_swap (dskfree_mb, dsktotal_mb));
248 if (verbose)
249 xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
250 }
251 }
252# ifdef _AIX
253 } 105 }
254# endif 106 mp_subcheck sc1 = mp_subcheck_init();
255 107 sc1 = mp_set_subcheck_default_state(sc1, STATE_OK);
256 /* If we get anything on STDERR, at least set warning */
257 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr))
258 result = max_state (result, STATE_WARNING);
259
260 /* close stderr */
261 (void) fclose (child_stderr);
262 108
263 /* close the pipe */ 109 /* if total_swap_mb == 0, let's not divide by 0 */
264 if (spclose (child_process)) 110 if (data.metrics.total != 0) {
265 result = max_state (result, STATE_WARNING); 111 percent_used = HUNDRED_PERCENT * ((double)data.metrics.used) / ((double)data.metrics.total);
266# else 112 } else {
267# ifdef CHECK_SWAP_SWAPCTL_SVR4 113 sc1 = mp_set_subcheck_state(sc1, config.no_swap_state);
114 sc1.output = (char *)_("Swap is either disabled, not present, or of zero size.");
268 115
269 /* get the number of active swap devices */ 116 mp_add_subcheck_to_check(&overall, sc1);
270 if((nswaps=swapctl(SC_GETNSWP, NULL))== -1) 117 mp_exit(overall);
271 die(STATE_UNKNOWN, _("Error getting swap devices\n") ); 118 }
272 119
273 if(nswaps == 0) 120 if (verbose) {
274 die(STATE_OK, _("SWAP OK: No swap devices defined\n")); 121 printf("Computed usage percentage: %g\n", percent_used);
122 }
275 123
276 if(verbose >= 3) 124 mp_perfdata pd = perfdata_init();
277 printf("Found %d swap device(s)\n", nswaps); 125 pd.label = "swap";
126 pd = mp_set_pd_value(pd, data.metrics.free);
127 pd.uom = "B";
278 128
279 /* initialize swap table + entries */ 129 if (config.warn_is_set) {
280 tbl=(swaptbl_t*)malloc(sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps)); 130 uint64_t warn_print = config.warn.value;
131 if (config.warn.is_percentage) {
132 warn_print = config.warn.value * (data.metrics.total / HUNDRED_PERCENT);
133 }
281 134
282 if(tbl==NULL) 135 mp_perfdata_value warn_pd = mp_create_pd_value(warn_print);
283 die(STATE_UNKNOWN, _("malloc() failed!\n"));
284 136
285 memset(tbl, 0, sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps)); 137 mp_range warn_range = mp_range_init();
286 tbl->swt_n=nswaps; 138 warn_range.end_infinity = false;
287 for(i=0;i<nswaps;i++){ 139 warn_range.end = warn_pd;
288 if((tbl->swt_ent[i].ste_path=(char*)malloc(sizeof(char)*MAXPATHLEN)) == NULL)
289 die(STATE_UNKNOWN, _("malloc() failed!\n"));
290 }
291 140
292 /* and now, tally 'em up */ 141 pd.warn = warn_range;
293 swapctl_res=swapctl(SC_LIST, tbl); 142 pd.warn_present = true;
294 if(swapctl_res < 0){
295 perror(_("swapctl failed: "));
296 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
297 } 143 }
298 144
299 for(i=0;i<nswaps;i++){ 145 if (config.crit_is_set) {
300 dsktotal_mb = (float) tbl->swt_ent[i].ste_pages / SWAP_CONVERSION; 146 uint64_t crit_print = config.crit.value;
301 dskfree_mb = (float) tbl->swt_ent[i].ste_free / SWAP_CONVERSION; 147 if (config.crit.is_percentage) {
302 dskused_mb = ( dsktotal_mb - dskfree_mb ); 148 crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT);
303
304 if (verbose >= 3)
305 printf ("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb);
306
307 if(allswaps && dsktotal_mb > 0){
308 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
309 result = max_state (result, check_swap (dskfree_mb, dsktotal_mb));
310 if (verbose) {
311 xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
312 }
313 } 149 }
314 150
315 total_swap_mb += dsktotal_mb; 151 mp_perfdata_value crit_pd = mp_create_pd_value(crit_print);
316 free_swap_mb += dskfree_mb;
317 used_swap_mb += dskused_mb;
318 }
319 152
320 /* and clean up after ourselves */ 153 mp_range crit_range = mp_range_init();
321 for(i=0;i<nswaps;i++){ 154 crit_range.end_infinity = false;
322 free(tbl->swt_ent[i].ste_path); 155 crit_range.end = crit_pd;
156
157 pd.crit = crit_range;
158 pd.crit_present = true;
323 } 159 }
324 free(tbl);
325# else
326# ifdef CHECK_SWAP_SWAPCTL_BSD
327 160
328 /* get the number of active swap devices */ 161 mp_perfdata_value max = mp_create_pd_value(data.metrics.total);
329 nswaps=swapctl(SWAP_NSWAP, NULL, 0); 162 pd.max = max;
163 pd.max_present = true;
330 164
331 /* initialize swap table + entries */ 165 mp_perfdata_value min = mp_create_pd_value(0);
332 ent=(struct swapent*)malloc(sizeof(struct swapent)*nswaps); 166 pd.min = min;
167 pd.min_present = true;
333 168
334 /* and now, tally 'em up */ 169 mp_add_perfdata_to_subcheck(&sc1, pd);
335 swapctl_res=swapctl(SWAP_STATS, ent, nswaps); 170 if (verbose > 1) {
336 if(swapctl_res < 0){ 171 printf("Warn threshold value: %" PRIu64 "\n", config.warn.value);
337 perror(_("swapctl failed: "));
338 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
339 } 172 }
340 173
341 for(i=0;i<nswaps;i++){ 174 if (config.warn_is_set) {
342 dsktotal_mb = (float) ent[i].se_nblks / conv_factor; 175 if ((config.warn.is_percentage && (percent_used >= (100 - (double)config.warn.value))) ||
343 dskused_mb = (float) ent[i].se_inuse / conv_factor; 176 config.warn.value >= data.metrics.free) {
344 dskfree_mb = ( dsktotal_mb - dskused_mb ); 177 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
345
346 if(allswaps && dsktotal_mb > 0){
347 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
348 result = max_state (result, check_swap(dskfree_mb, dsktotal_mb));
349 if (verbose) {
350 xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
351 }
352 } 178 }
353
354 total_swap_mb += dsktotal_mb;
355 free_swap_mb += dskfree_mb;
356 used_swap_mb += dskused_mb;
357 } 179 }
358 180
359 /* and clean up after ourselves */ 181 if (verbose > 1) {
360 free(ent); 182 printf("Crit threshold value: %" PRIu64 "\n", config.crit.value);
361
362# endif /* CHECK_SWAP_SWAPCTL_BSD */
363# endif /* CHECK_SWAP_SWAPCTL_SVR4 */
364# endif /* HAVE_SWAP */
365#endif /* HAVE_PROC_MEMINFO */
366
367 /* if total_swap_mb == 0, let's not divide by 0 */
368 if(total_swap_mb) {
369 percent_used = 100 * ((double) used_swap_mb) / ((double) total_swap_mb);
370 } else {
371 percent_used = 100;
372 status = "- Swap is either disabled, not present, or of zero size. ";
373 } 183 }
374 184
375 result = max_state (result, check_swap(free_swap_mb, total_swap_mb)); 185 if (config.crit_is_set) {
376 printf (_("SWAP %s - %d%% free (%dMB out of %dMB) %s|"), 186 if ((config.crit.is_percentage && (percent_used >= (100 - (double)config.crit.value))) ||
377 state_text (result), 187 config.crit.value >= data.metrics.free) {
378 (100 - percent_used), (int) free_swap_mb, (int) total_swap_mb, status); 188 sc1 = mp_set_subcheck_state(sc1, STATE_CRITICAL);
189 }
190 }
379 191
380 uint64_t warn_print = warn.value; 192 xasprintf(&sc1.output, _("%g%% free (%lluMiB out of %lluMiB)"), (100 - percent_used),
381 if (warn.is_percentage) warn_print = warn.value * (total_swap_mb *1024 *1024/100); 193 data.metrics.free >> 20, data.metrics.total >> 20);
382 uint64_t crit_print = crit.value;
383 if (crit.is_percentage) crit_print = crit.value * (total_swap_mb *1024 *1024/100);
384 194
385 puts (perfdata_uint64 ("swap", free_swap_mb *1024 *1024, "B", 195 overall.summary = "Swap";
386 true, warn_print, 196 mp_add_subcheck_to_check(&overall, sc1);
387 true, crit_print,
388 true, 0,
389 true, (long) total_swap_mb * 1024 * 1024));
390 197
391 return result; 198 mp_exit(overall);
392} 199}
393 200
201int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
202 if (total_swap_mb == 0) {
203 return config.no_swap_state;
204 }
394 205
395int 206 uint64_t free_swap =
396check_swap(float free_swap_mb, float total_swap_mb) 207 (uint64_t)(free_swap_mb *
397{ 208 (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */
398
399 if (!total_swap_mb) return no_swap_state;
400 209
401 uint64_t free_swap = free_swap_mb * (1024 * 1024); /* Convert back to bytes as warn and crit specified in bytes */ 210 if (!config.crit.is_percentage && config.crit.value >= free_swap) {
402 uint64_t usage_percentage = ((total_swap_mb - free_swap_mb) / total_swap_mb) * 100; 211 return STATE_CRITICAL;
212 }
213 if (!config.warn.is_percentage && config.warn.value >= free_swap) {
214 return STATE_WARNING;
215 }
403 216
404 if (warn.value || crit.value) { /* Thresholds defined */ 217 uint64_t usage_percentage =
405 if (!crit.is_percentage && crit.value >= free_swap) return STATE_CRITICAL; 218 (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT;
406 if (!warn.is_percentage && warn.value >= free_swap) return STATE_WARNING;
407 219
408 if (crit.is_percentage && 220 if (config.crit.is_percentage && config.crit.value != 0 &&
409 crit.value != 0 && 221 usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) {
410 usage_percentage >= (100 - crit.value)) 222 return STATE_CRITICAL;
411 { 223 }
412 return STATE_CRITICAL;
413 }
414 224
415 if (warn.is_percentage && 225 if (config.warn.is_percentage && config.warn.value != 0 &&
416 warn.value != 0 && 226 usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) {
417 usage_percentage >= (100 - warn.value)) 227 return STATE_WARNING;
418 { 228 }
419 return STATE_WARNING;
420 }
421 229
422 return STATE_OK; 230 return STATE_OK;
423 } else { /* Without thresholds */
424 return STATE_OK;
425 }
426} 231}
427 232
428 233#define output_format_index CHAR_MAX + 1
429 234
430/* process command-line arguments */ 235/* process command-line arguments */
431int 236swap_config_wrapper process_arguments(int argc, char **argv) {
432process_arguments (int argc, char **argv) 237 swap_config_wrapper conf_wrapper = {.errorcode = OK};
433{ 238 conf_wrapper.config = swap_config_init();
434 int c = 0; /* option character */ 239
435 240 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
436 int option = 0; 241 {"critical", required_argument, 0, 'c'},
437 static struct option longopts[] = { 242 {"allswaps", no_argument, 0, 'a'},
438 {"warning", required_argument, 0, 'w'}, 243 {"no-swap", required_argument, 0, 'n'},
439 {"critical", required_argument, 0, 'c'}, 244 {"verbose", no_argument, 0, 'v'},
440 {"allswaps", no_argument, 0, 'a'}, 245 {"version", no_argument, 0, 'V'},
441 {"no-swap", required_argument, 0, 'n'}, 246 {"help", no_argument, 0, 'h'},
442 {"verbose", no_argument, 0, 'v'}, 247 {"output-format", required_argument, 0, output_format_index},
443 {"version", no_argument, 0, 'V'}, 248 {0, 0, 0, 0}};
444 {"help", no_argument, 0, 'h'}, 249
445 {0, 0, 0, 0} 250 while (true) {
446 }; 251 int option = 0;
447 252 int option_char = getopt_long(argc, argv, "+?Vvhac:w:n:", longopts, &option);
448 while (1) { 253
449 c = getopt_long (argc, argv, "+?Vvhac:w:n:", longopts, &option); 254 if (option_char == -1 || option_char == EOF) {
450
451 if (c == -1 || c == EOF)
452 break; 255 break;
256 }
453 257
454 switch (c) { 258 switch (option_char) {
455 case 'w': /* warning size threshold */ 259 case 'w': /* warning size threshold */
456 { 260 {
457 /* 261 /*
458 * We expect either a positive integer value without a unit, which means 262 * We expect either a positive integer value without a unit, which
459 * the unit is Bytes or a positive integer value and a percentage sign (%), 263 * means the unit is Bytes or a positive integer value and a
460 * which means the value must be with 0 and 100 and is relative to the total swap 264 * percentage sign (%), which means the value must be with 0 and 100
461 */ 265 * and is relative to the total swap
462 size_t length; 266 */
463 length = strlen(optarg); 267 size_t length;
464 268 length = strlen(optarg);
465 if (optarg[length - 1] == '%') { 269 conf_wrapper.config.warn_is_set = true;
466 /* It's percentage */ 270
467 warn.is_percentage = true; 271 if (optarg[length - 1] == '%') {
468 optarg[length - 1] = '\0'; 272 /* It's percentage */
469 if (is_uint64(optarg, &warn.value)) { 273 conf_wrapper.config.warn.is_percentage = true;
470 if (warn.value > 100) { 274 optarg[length - 1] = '\0';
471 usage4 (_("Warning threshold percentage must be <= 100!")); 275 if (is_uint64(optarg, &conf_wrapper.config.warn.value)) {
472 } 276 if (conf_wrapper.config.warn.value > HUNDRED_PERCENT) {
473 } 277 usage4(_("Warning threshold percentage must be <= 100!"));
474 break;
475 } else {
476 /* It's Bytes */
477 warn.is_percentage = false;
478 if (is_uint64(optarg, &warn.value)) {
479 break;
480 } else {
481 usage4 (_("Warning threshold be positive integer or percentage!"));
482 } 278 }
483 } 279 }
280 break;
281 } /* It's Bytes */
282 conf_wrapper.config.warn.is_percentage = false;
283 if (is_uint64(optarg, &conf_wrapper.config.warn.value)) {
284 break;
484 } 285 }
286 usage4(_("Warning threshold be positive integer or "
287 "percentage!"));
288 }
485 case 'c': /* critical size threshold */ 289 case 'c': /* critical size threshold */
486 { 290 {
487 /* 291 /*
488 * We expect either a positive integer value without a unit, which means 292 * We expect either a positive integer value without a unit, which
489 * the unit is Bytes or a positive integer value and a percentage sign (%), 293 * means the unit is Bytes or a positive integer value and a
490 * which means the value must be with 0 and 100 and is relative to the total swap 294 * percentage sign (%), which means the value must be with 0 and 100
491 */ 295 * and is relative to the total swap
492 size_t length; 296 */
493 length = strlen(optarg); 297 size_t length;
494 298 length = strlen(optarg);
495 if (optarg[length - 1] == '%') { 299 conf_wrapper.config.crit_is_set = true;
496 /* It's percentage */ 300
497 crit.is_percentage = true; 301 if (optarg[length - 1] == '%') {
498 optarg[length - 1] = '\0'; 302 /* It's percentage */
499 if (is_uint64(optarg, &crit.value)) { 303 conf_wrapper.config.crit.is_percentage = true;
500 if (crit.value> 100) { 304 optarg[length - 1] = '\0';
501 usage4 (_("Critical threshold percentage must be <= 100!")); 305 if (is_uint64(optarg, &conf_wrapper.config.crit.value)) {
502 } 306 if (conf_wrapper.config.crit.value > HUNDRED_PERCENT) {
503 } 307 usage4(_("Critical threshold percentage must be <= 100!"));
504 break;
505 } else {
506 /* It's Bytes */
507 crit.is_percentage = false;
508 if (is_uint64(optarg, &crit.value)) {
509 break;
510 } else {
511 usage4 (_("Critical threshold be positive integer or percentage!"));
512 } 308 }
513 } 309 }
514 } 310 break;
515 case 'a': /* all swap */ 311 } /* It's Bytes */
516 allswaps = true; 312 conf_wrapper.config.crit.is_percentage = false;
313 if (is_uint64(optarg, &conf_wrapper.config.crit.value)) {
314 break;
315 }
316 usage4(_("Critical threshold be positive integer or "
317 "percentage!"));
318 }
319 case 'a': /* all swap */
320 conf_wrapper.config.allswaps = true;
517 break; 321 break;
518 case 'n': 322 case 'n':
519 if ((no_swap_state = mp_translate_state(optarg)) == ERROR) { 323 if ((conf_wrapper.config.no_swap_state = mp_translate_state(optarg)) == ERROR) {
520 usage4 (_("no-swap result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 324 usage4(_("no-swap result must be a valid state name (OK, "
325 "WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
521 } 326 }
522 break; 327 break;
523 case 'v': /* verbose */ 328 case 'v': /* verbose */
524 verbose++; 329 verbose++;
525 break; 330 break;
526 case 'V': /* version */ 331 case output_format_index: {
527 print_revision (progname, NP_VERSION); 332 parsed_output_format parser = mp_parse_output_format(optarg);
528 exit (STATE_UNKNOWN); 333 if (!parser.parsing_success) {
529 case 'h': /* help */ 334 // TODO List all available formats here, maybe add anothoer usage function
530 print_help (); 335 printf("Invalid output format: %s\n", optarg);
531 exit (STATE_UNKNOWN); 336 exit(STATE_UNKNOWN);
532 case '?': /* error */ 337 }
533 usage5 (); 338
339 conf_wrapper.config.output_format_is_set = true;
340 conf_wrapper.config.output_format = parser.output_format;
341 break;
342 }
343 case 'V': /* version */
344 print_revision(progname, NP_VERSION);
345 exit(STATE_UNKNOWN);
346 case 'h': /* help */
347 print_help(conf_wrapper.config);
348 exit(STATE_UNKNOWN);
349 case '?': /* error */
350 usage5();
534 } 351 }
535 } 352 }
536 353
537 c = optind; 354 if ((conf_wrapper.config.warn.is_percentage == conf_wrapper.config.crit.is_percentage) &&
538 if (c == argc) 355 (conf_wrapper.config.warn.value < conf_wrapper.config.crit.value)) {
539 return validate_arguments (); 356 /* This is NOT triggered if warn and crit are different units, e.g warn
540 357 * is percentage and crit is absolute. We cannot determine the condition
541 return validate_arguments (); 358 * at this point since we dont know the value of total swap yet
542}
543
544
545
546int
547validate_arguments (void)
548{
549 if ((warn.is_percentage == crit.is_percentage) && (warn.value < crit.value)) {
550 /* This is NOT triggered if warn and crit are different units, e.g warn is percentage
551 * and crit is absolute. We cannot determine the condition at this point since we
552 * dont know the value of total swap yet
553 */ 359 */
554 usage4(_("Warning should be more than critical")); 360 usage4(_("Warning should be more than critical"));
555 } 361 }
556 return OK;
557}
558
559 362
560 363 return conf_wrapper;
561void
562print_help (void)
563{
564 print_revision (progname, NP_VERSION);
565
566 printf (_(COPYRIGHT), copyright, email);
567
568 printf ("%s\n", _("Check swap space on local machine."));
569
570 printf ("\n\n");
571
572 print_usage ();
573
574 printf (UT_HELP_VRSN);
575 printf (UT_EXTRA_OPTS);
576
577 printf (" %s\n", "-w, --warning=INTEGER");
578 printf (" %s\n", _("Exit with WARNING status if less than INTEGER bytes of swap space are free"));
579 printf (" %s\n", "-w, --warning=PERCENT%");
580 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of swap space is free"));
581 printf (" %s\n", "-c, --critical=INTEGER");
582 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER bytes of swap space are free"));
583 printf (" %s\n", "-c, --critical=PERCENT%");
584 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of swap space is free"));
585 printf (" %s\n", "-a, --allswaps");
586 printf (" %s\n", _("Conduct comparisons for all swap partitions, one by one"));
587 printf (" %s\n", "-n, --no-swap=<ok|warning|critical|unknown>");
588 printf (" %s %s\n", _("Resulting state when there is no swap regardless of thresholds. Default:"), state_text(no_swap_state));
589 printf (UT_VERBOSE);
590
591 printf ("\n");
592 printf ("%s\n", _("Notes:"));
593 printf (" %s\n", _("Both INTEGER and PERCENT thresholds can be specified, they are all checked."));
594 printf (" %s\n", _("Without thresholds, the plugin shows free swap space and performance data, but always returns OK."));
595 printf (" %s\n", _("On AIX, if -a is specified, uses lsps -a, otherwise uses lsps -s."));
596
597 printf (UT_SUPPORT);
598} 364}
599 365
366void print_help(swap_config config) {
367 print_revision(progname, NP_VERSION);
368
369 printf(_(COPYRIGHT), copyright, email);
370
371 printf("%s\n", _("Check swap space on local machine."));
372
373 printf("\n\n");
374
375 print_usage();
376
377 printf(UT_HELP_VRSN);
378 printf(UT_EXTRA_OPTS);
379
380 printf(" %s\n", "-w, --warning=INTEGER");
381 printf(" %s\n", _("Exit with WARNING status if less than INTEGER bytes "
382 "of swap space are free"));
383 printf(" %s\n", "-w, --warning=PERCENT%");
384 printf(" %s\n", _("Exit with WARNING status if less than PERCENT of "
385 "swap space is free"));
386 printf(" %s\n", "-c, --critical=INTEGER");
387 printf(" %s\n", _("Exit with CRITICAL status if less than INTEGER bytes "
388 "of swap space are free"));
389 printf(" %s\n", "-c, --critical=PERCENT%");
390 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of "
391 "swap space is free"));
392 printf(" %s\n", "-a, --allswaps");
393 printf(" %s\n", _("Conduct comparisons for all swap partitions, one by one"));
394 printf(" %s\n", "-n, --no-swap=<ok|warning|critical|unknown>");
395 printf(" %s %s\n",
396 _("Resulting state when there is no swap regardless of thresholds. "
397 "Default:"),
398 state_text(config.no_swap_state));
399 printf(UT_OUTPUT_FORMAT);
400 printf(UT_VERBOSE);
401
402 printf("\n");
403 printf("%s\n", _("Notes:"));
404 printf(" %s\n", _("Both INTEGER and PERCENT thresholds can be specified, "
405 "they are all checked."));
406 printf(" %s\n", _("On AIX, if -a is specified, uses lsps -a, otherwise uses lsps -s."));
407
408 printf(UT_SUPPORT);
409}
600 410
601void 411void print_usage(void) {
602print_usage (void) 412 printf("%s\n", _("Usage:"));
603{ 413 printf(" %s [-av] -w <percent_free>%% -c <percent_free>%%\n", progname);
604 printf ("%s\n", _("Usage:")); 414 printf(" -w <bytes_free> -c <bytes_free> [-n <state>]\n");
605 printf (" %s [-av] [-w <percent_free>%%] [-c <percent_free>%%]\n",progname);
606 printf (" [-w <bytes_free>] [-c <bytes_free>] [-n <state>]\n");
607} 415}
diff --git a/plugins/check_swap.d/check_swap.h b/plugins/check_swap.d/check_swap.h
new file mode 100644
index 00000000..8d3c7fcf
--- /dev/null
+++ b/plugins/check_swap.d/check_swap.h
@@ -0,0 +1,49 @@
1#pragma once
2
3#include "../common.h"
4#include "../../lib/output.h"
5#include "../../lib/states.h"
6
7#ifndef SWAP_CONVERSION
8# define SWAP_CONVERSION 1
9#endif
10
11typedef struct {
12 bool is_percentage;
13 uint64_t value;
14} check_swap_threshold;
15
16typedef struct {
17 unsigned long long free; // Free swap in Bytes!
18 unsigned long long used; // Used swap in Bytes!
19 unsigned long long total; // Total swap size, you guessed it, in Bytes!
20} swap_metrics;
21
22typedef struct {
23 int errorcode;
24 int statusCode;
25 swap_metrics metrics;
26} swap_result;
27
28typedef struct {
29 bool allswaps;
30 mp_state_enum no_swap_state;
31 bool warn_is_set;
32 check_swap_threshold warn;
33 bool crit_is_set;
34 check_swap_threshold crit;
35 bool on_aix;
36 int conversion_factor;
37
38 bool output_format_is_set;
39 mp_output_format output_format;
40} swap_config;
41
42swap_config swap_config_init(void);
43
44swap_result get_swap_data(swap_config config);
45swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]);
46swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[],
47 const char swap_format[]);
48swap_result getSwapFromSwapctl_BSD(swap_config config);
49swap_result getSwapFromSwap_SRV4(swap_config config);
diff --git a/plugins/check_swap.d/swap.c b/plugins/check_swap.d/swap.c
new file mode 100644
index 00000000..58213a3c
--- /dev/null
+++ b/plugins/check_swap.d/swap.c
@@ -0,0 +1,471 @@
1#include "./check_swap.d/check_swap.h"
2#include "../popen.h"
3#include "../utils.h"
4#include "common.h"
5
6extern int verbose;
7
8swap_config swap_config_init(void) {
9 swap_config tmp = {0};
10 tmp.allswaps = false;
11 tmp.no_swap_state = STATE_CRITICAL;
12 tmp.conversion_factor = SWAP_CONVERSION;
13
14 tmp.warn_is_set = false;
15 tmp.crit_is_set = false;
16
17 tmp.output_format_is_set = false;
18
19#ifdef _AIX
20 tmp.on_aix = true;
21#else
22 tmp.on_aix = false;
23#endif
24
25 return tmp;
26}
27
28swap_result get_swap_data(swap_config config) {
29#ifdef HAVE_PROC_MEMINFO
30 if (verbose >= 3) {
31 printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO);
32 }
33
34 return getSwapFromProcMeminfo(PROC_MEMINFO);
35#else // HAVE_PROC_MEMINFO
36# ifdef HAVE_SWAP
37 if (verbose >= 3) {
38 printf("Using swap command %s with format: %s\n", SWAP_COMMAND, SWAP_FORMAT);
39 }
40
41 /* These override the command used if a summary (and thus ! allswaps) is
42 * required
43 * The summary flag returns more accurate information about swap usage on these
44 * OSes */
45 if (config.on_aix && !config.allswaps) {
46
47 config.conversion_factor = 1;
48
49 return getSwapFromSwapCommand(config, "/usr/sbin/lsps -s", "%lu%*s %lu");
50 } else {
51 return getSwapFromSwapCommand(config, SWAP_COMMAND, SWAP_FORMAT);
52 }
53# else // HAVE_SWAP
54# ifdef CHECK_SWAP_SWAPCTL_SVR4
55 return getSwapFromSwapctl_SRV4(config);
56# else // CHECK_SWAP_SWAPCTL_SVR4
57# ifdef CHECK_SWAP_SWAPCTL_BSD
58 return getSwapFromSwapctl_BSD(config);
59# else // CHECK_SWAP_SWAPCTL_BSD
60# error No way found to retrieve swap
61# endif /* CHECK_SWAP_SWAPCTL_BSD */
62# endif /* CHECK_SWAP_SWAPCTL_SVR4 */
63# endif /* HAVE_SWAP */
64#endif /* HAVE_PROC_MEMINFO */
65}
66
67swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
68 FILE *meminfo_file_ptr;
69 meminfo_file_ptr = fopen(proc_meminfo, "r");
70
71 swap_result result = {};
72 result.errorcode = STATE_UNKNOWN;
73
74 if (meminfo_file_ptr == NULL) {
75 // failed to open meminfo file
76 // errno should contain an error
77 result.errorcode = STATE_UNKNOWN;
78 return result;
79 }
80
81 unsigned long swap_total = 0;
82 unsigned long swap_used = 0;
83 unsigned long swap_free = 0;
84
85 bool found_total = false;
86 bool found_free = false;
87
88 char input_buffer[MAX_INPUT_BUFFER];
89 char str[32];
90
91 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) {
92
93 /*
94 * The following sscanf call looks for a line looking like: "Swap: 123
95 * 123 123" which exists on NetBSD (at least),
96 * The unit should be Bytes
97 */
98 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used,
99 &swap_free) == 3) {
100 found_total = true;
101 found_free = true;
102 // Set error
103 result.errorcode = STATE_OK;
104 // Break out of fgets here, since both scanf expressions might match (NetBSD for
105 // example)
106 break;
107 }
108
109 /*
110 * The following sscanf call looks for lines looking like:
111 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least
112 * on Debian Linux with a 5.* kernel
113 */
114 unsigned long tmp_KB = 0;
115 int sscanf_result = sscanf(input_buffer,
116 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu "
117 "%*[k]%*[B]",
118 str, &tmp_KB);
119
120 if (sscanf_result == 2) {
121
122 if (verbose >= 3) {
123 printf("Got %s with %lu\n", str, tmp_KB);
124 }
125
126 /* I think this part is always in Kb, so convert to bytes */
127 if (strcmp("Total", str) == 0) {
128 swap_total = tmp_KB * 1000;
129 found_total = true;
130 } else if (strcmp("Free", str) == 0) {
131 swap_free += tmp_KB * 1000;
132 found_free = true;
133 } else if (strcmp("Cached", str) == 0) {
134 swap_free += tmp_KB * 1000;
135 }
136
137 result.errorcode = STATE_OK;
138 }
139 }
140
141 fclose(meminfo_file_ptr);
142
143 result.metrics.total = swap_total;
144 result.metrics.free = swap_free;
145 result.metrics.used = swap_total - swap_free;
146
147 if (!found_free || !found_total) {
148 result.errorcode = STATE_UNKNOWN;
149 }
150
151 return result;
152}
153
154swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[],
155 const char swap_format[]) {
156 swap_result result = {0};
157
158 char *temp_buffer;
159
160 if (verbose >= 2) {
161 printf(_("Command: %s\n"), swap_command);
162 }
163 if (verbose >= 3) {
164 printf(_("Format: %s\n"), swap_format);
165 }
166
167 child_process = spopen(swap_command);
168 if (child_process == NULL) {
169 printf(_("Could not open pipe: %s\n"), swap_command);
170 swap_result tmp = {
171 .errorcode = STATE_UNKNOWN,
172 };
173 return tmp;
174 }
175
176 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
177 if (child_stderr == NULL) {
178 printf(_("Could not open stderr for %s\n"), swap_command);
179 }
180
181 char str[32] = {0};
182 char input_buffer[MAX_INPUT_BUFFER];
183
184 /* read 1st line */
185 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process);
186 if (strcmp(swap_format, "") == 0) {
187 temp_buffer = strtok(input_buffer, " \n");
188 while (temp_buffer) {
189 if (strstr(temp_buffer, "blocks")) {
190 sprintf(str, "%s %s", str, "%lu");
191 } else if (strstr(temp_buffer, "dskfree")) {
192 sprintf(str, "%s %s", str, "%lu");
193 } else {
194 sprintf(str, "%s %s", str, "%*s");
195 }
196 temp_buffer = strtok(NULL, " \n");
197 }
198 }
199
200 double total_swap_mb = 0;
201 double free_swap_mb = 0;
202 double used_swap_mb = 0;
203 double dsktotal_mb = 0;
204 double dskused_mb = 0;
205 double dskfree_mb = 0;
206
207 /*
208 * If different swap command is used for summary switch, need to read format
209 * differently
210 */
211 if (config.on_aix && !config.allswaps) {
212 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */
213 sscanf(input_buffer, swap_format, &total_swap_mb, &used_swap_mb);
214 free_swap_mb = total_swap_mb * (100 - used_swap_mb) / 100;
215 used_swap_mb = total_swap_mb - free_swap_mb;
216
217 if (verbose >= 3) {
218 printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb,
219 free_swap_mb);
220 }
221 } else {
222 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
223 sscanf(input_buffer, swap_format, &dsktotal_mb, &dskfree_mb);
224
225 dsktotal_mb = dsktotal_mb / config.conversion_factor;
226 /* AIX lists percent used, so this converts to dskfree in MBs */
227
228 if (config.on_aix) {
229 dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100;
230 } else {
231 dskfree_mb = dskfree_mb / config.conversion_factor;
232 }
233
234 if (verbose >= 3) {
235 printf(_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb);
236 }
237
238 dskused_mb = dsktotal_mb - dskfree_mb;
239 total_swap_mb += dsktotal_mb;
240 used_swap_mb += dskused_mb;
241 free_swap_mb += dskfree_mb;
242 }
243 }
244
245 result.metrics.free = free_swap_mb * 1024 * 1024;
246 result.metrics.used = used_swap_mb * 1024 * 1024;
247 result.metrics.total = free_swap_mb * 1024 * 1024;
248
249 /* If we get anything on STDERR, at least set warning */
250 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
251 result.statusCode = max_state(result.statusCode, STATE_WARNING);
252 // TODO Set error here
253 }
254
255 /* close stderr */
256 (void)fclose(child_stderr);
257
258 /* close the pipe */
259 if (spclose(child_process)) {
260 result.statusCode = max_state(result.statusCode, STATE_WARNING);
261 // TODO set error here
262 }
263
264 return result;
265}
266
267#ifndef CHECK_SWAP_SWAPCTL_BSD
268# define CHECK_SWAP_SWAPCTL_BSD
269
270// Stub functionality for BSD stuff, so the compiler always sees the following BSD code
271
272# define SWAP_NSWAP 0
273# define SWAP_STATS 1
274
275int bsd_swapctl(int cmd, const void *arg, int misc) {
276 (void)cmd;
277 (void)arg;
278 (void)misc;
279 return 512;
280}
281
282struct swapent {
283 dev_t se_dev; /* device id */
284 int se_flags; /* entry flags */
285 int se_nblks; /* total blocks */
286 int se_inuse; /* blocks in use */
287 int se_priority; /* priority */
288 char se_path[PATH_MAX]; /* path to entry */
289};
290
291#else
292
293// Includes for NetBSD
294# include <unistd.h>
295# include <sys/swap.h>
296
297# define bsd_swapctl swapctl
298
299#endif // CHECK_SWAP_SWAPCTL_BSD
300
301swap_result getSwapFromSwapctl_BSD(swap_config config) {
302 /* get the number of active swap devices */
303 int nswaps = bsd_swapctl(SWAP_NSWAP, NULL, 0);
304
305 /* initialize swap table + entries */
306 struct swapent *ent = (struct swapent *)malloc(sizeof(struct swapent) * (unsigned long)nswaps);
307
308 /* and now, tally 'em up */
309 int swapctl_res = bsd_swapctl(SWAP_STATS, ent, nswaps);
310 if (swapctl_res < 0) {
311 perror(_("swapctl failed: "));
312 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
313 }
314
315 double dsktotal_mb = 0.0;
316 double dskfree_mb = 0.0;
317 double dskused_mb = 0.0;
318 unsigned long long total_swap_mb = 0;
319 unsigned long long free_swap_mb = 0;
320 unsigned long long used_swap_mb = 0;
321
322 for (int i = 0; i < nswaps; i++) {
323 dsktotal_mb = (double)ent[i].se_nblks / (double)config.conversion_factor;
324 dskused_mb = (double)ent[i].se_inuse / (double)config.conversion_factor;
325 dskfree_mb = (dsktotal_mb - dskused_mb);
326
327 if (config.allswaps && dsktotal_mb > 0) {
328 double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb));
329
330 if (verbose) {
331 printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent);
332 }
333 }
334
335 total_swap_mb += (unsigned long long)dsktotal_mb;
336 free_swap_mb += (unsigned long long)dskfree_mb;
337 used_swap_mb += (unsigned long long)dskused_mb;
338 }
339
340 /* and clean up after ourselves */
341 free(ent);
342
343 swap_result result = {0};
344
345 result.statusCode = OK;
346 result.errorcode = OK;
347
348 result.metrics.total = total_swap_mb * 1024 * 1024;
349 result.metrics.free = free_swap_mb * 1024 * 1024;
350 result.metrics.used = used_swap_mb * 1024 * 1024;
351
352 return result;
353}
354
355#ifndef CHECK_SWAP_SWAPCTL_SVR4
356int srv4_swapctl(int cmd, void *arg) {
357 (void)cmd;
358 (void)arg;
359 return 512;
360}
361
362typedef struct srv4_swapent {
363 char *ste_path; /* name of the swap file */
364 off_t ste_start; /* starting block for swapping */
365 off_t ste_length; /* length of swap area */
366 long ste_pages; /* number of pages for swapping */
367 long ste_free; /* number of ste_pages free */
368 long ste_flags; /* ST_INDEL bit set if swap file */
369 /* is now being deleted */
370} swapent_t;
371
372typedef struct swaptbl {
373 int swt_n; /* number of swapents following */
374 struct srv4_swapent swt_ent[]; /* array of swt_n swapents */
375} swaptbl_t;
376
377# define SC_LIST 2
378# define SC_GETNSWP 3
379
380# ifndef MAXPATHLEN
381# define MAXPATHLEN 2048
382# endif
383
384#else
385# define srv4_swapctl swapctl
386#endif
387
388swap_result getSwapFromSwap_SRV4(swap_config config) {
389 int nswaps = 0;
390
391 /* get the number of active swap devices */
392 if ((nswaps = srv4_swapctl(SC_GETNSWP, NULL)) == -1) {
393 die(STATE_UNKNOWN, _("Error getting swap devices\n"));
394 }
395
396 if (nswaps == 0) {
397 die(STATE_OK, _("SWAP OK: No swap devices defined\n"));
398 }
399
400 if (verbose >= 3) {
401 printf("Found %d swap device(s)\n", nswaps);
402 }
403
404 /* initialize swap table + entries */
405 swaptbl_t *tbl =
406 (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
407
408 if (tbl == NULL) {
409 die(STATE_UNKNOWN, _("malloc() failed!\n"));
410 }
411
412 memset(tbl, 0, sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
413 tbl->swt_n = nswaps;
414
415 for (int i = 0; i < nswaps; i++) {
416 if ((tbl->swt_ent[i].ste_path = (char *)malloc(sizeof(char) * MAXPATHLEN)) == NULL) {
417 die(STATE_UNKNOWN, _("malloc() failed!\n"));
418 }
419 }
420
421 /* and now, tally 'em up */
422 int swapctl_res = srv4_swapctl(SC_LIST, tbl);
423 if (swapctl_res < 0) {
424 perror(_("swapctl failed: "));
425 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
426 }
427
428 double dsktotal_mb = 0.0;
429 double dskfree_mb = 0.0;
430 double dskused_mb = 0.0;
431 unsigned long long total_swap_mb = 0;
432 unsigned long long free_swap_mb = 0;
433 unsigned long long used_swap_mb = 0;
434
435 for (int i = 0; i < nswaps; i++) {
436 dsktotal_mb = (float)tbl->swt_ent[i].ste_pages / SWAP_CONVERSION;
437 dskfree_mb = (float)tbl->swt_ent[i].ste_free / SWAP_CONVERSION;
438 dskused_mb = (dsktotal_mb - dskfree_mb);
439
440 if (verbose >= 3) {
441 printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb,
442 dskused_mb);
443 }
444
445 if (config.allswaps && dsktotal_mb > 0) {
446 double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb));
447
448 if (verbose) {
449 printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent);
450 }
451 }
452
453 total_swap_mb += (unsigned long long)dsktotal_mb;
454 free_swap_mb += (unsigned long long)dskfree_mb;
455 used_swap_mb += (unsigned long long)dskused_mb;
456 }
457
458 /* and clean up after ourselves */
459 for (int i = 0; i < nswaps; i++) {
460 free(tbl->swt_ent[i].ste_path);
461 }
462 free(tbl);
463
464 swap_result result = {0};
465 result.errorcode = OK;
466 result.metrics.total = total_swap_mb * 1024 * 1024;
467 result.metrics.free = free_swap_mb * 1024 * 1024;
468 result.metrics.used = used_swap_mb * 1024 * 1024;
469
470 return result;
471}
diff --git a/plugins/check_tcp.c b/plugins/check_tcp.c
index 01dd35eb..09806373 100644
--- a/plugins/check_tcp.c
+++ b/plugins/check_tcp.c
@@ -1,415 +1,496 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_tcp plugin 3 * Monitoring check_tcp plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2013 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2025 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_tcp plugin 10 * This file contains the check_tcp plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 13 * This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14 * it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 23 * You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* $Id$ 26 * $Id$
27* 27 *
28*****************************************************************************/ 28 *****************************************************************************/
29 29
30/* progname "check_tcp" changes depending on symlink called */ 30/* progname "check_tcp" changes depending on symlink called */
31char *progname; 31char *progname;
32const char *copyright = "1999-2008"; 32const char *copyright = "1999-2025";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "./common.h"
36#include "netutils.h" 36#include "./netutils.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_tcp.h" 38#include "./check_tcp.d/config.h"
39#include "output.h"
40#include "states.h"
39 41
42#include <sys/types.h>
40#include <ctype.h> 43#include <ctype.h>
41#include <sys/select.h> 44#include <sys/select.h>
42 45
46ssize_t my_recv(int socket_descriptor, char *buf, size_t len, bool use_tls) {
43#ifdef HAVE_SSL 47#ifdef HAVE_SSL
44static bool check_cert = false; 48 if (use_tls) {
45static int days_till_exp_warn, days_till_exp_crit; 49 return np_net_ssl_read(buf, (int)len);
46# define my_recv(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) 50 }
47# define my_send(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
48#else
49# define my_recv(buf, len) read(sd, buf, len)
50# define my_send(buf, len) send(sd, buf, len, 0)
51#endif 51#endif
52 return read(socket_descriptor, buf, len);
53}
52 54
53/* int my_recv(char *, size_t); */ 55ssize_t my_send(int socket_descriptor, char *buf, size_t len, bool use_tls) {
54static int process_arguments (int, char **);
55void print_help (void);
56void print_usage (void);
57
58#define EXPECT server_expect[0]
59static char *SERVICE = "TCP";
60static char *SEND = NULL;
61static char *QUIT = NULL;
62static int PROTOCOL = IPPROTO_TCP; /* most common is default */
63static int PORT = 0;
64static int READ_TIMEOUT = 2;
65
66static int server_port = 0;
67static char *server_address = NULL;
68static bool host_specified = false;
69static char *server_send = NULL;
70static char *server_quit = NULL;
71static char **server_expect;
72static size_t server_expect_count = 0;
73static ssize_t maxbytes = 0;
74static char **warn_codes = NULL;
75static size_t warn_codes_count = 0;
76static char **crit_codes = NULL;
77static size_t crit_codes_count = 0;
78static unsigned int delay = 0;
79static double warning_time = 0;
80static double critical_time = 0;
81static double elapsed_time = 0;
82static long microsec;
83static int sd = 0;
84#define MAXBUF 1024
85static char buffer[MAXBUF];
86static int expect_mismatch_state = STATE_WARNING;
87static int match_flags = NP_MATCH_EXACT;
88
89#ifdef HAVE_SSL 56#ifdef HAVE_SSL
90static char *sni = NULL; 57 if (use_tls) {
91static bool sni_specified = false; 58 return np_net_ssl_write(buf, (int)len);
59 }
92#endif 60#endif
61 return write(socket_descriptor, buf, len);
62}
93 63
94#define FLAG_SSL 0x01 64typedef struct {
95#define FLAG_VERBOSE 0x02 65 int errorcode;
96#define FLAG_TIME_WARN 0x04 66 check_tcp_config config;
97#define FLAG_TIME_CRIT 0x08 67} check_tcp_config_wrapper;
98#define FLAG_HIDE_OUTPUT 0x10 68static check_tcp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/,
99static size_t flags; 69 check_tcp_config /*config*/);
100 70void print_help(const char *service);
101int 71void print_usage(void);
102main (int argc, char **argv) 72
103{ 73int verbosity = 0;
104 int result = STATE_UNKNOWN; 74
105 char *status = NULL; 75static const int READ_TIMEOUT = 2;
106 struct timeval tv; 76
107 struct timeval timeout; 77const int MAXBUF = 1024;
108 int match = -1; 78
109 fd_set rfds; 79const int DEFAULT_FTP_PORT = 21;
110 80const int DEFAULT_POP_PORT = 110;
111 FD_ZERO(&rfds); 81const int DEFAULT_SPOP_PORT = 995;
112 82const int DEFAULT_SMTP_PORT = 25;
113 setlocale (LC_ALL, ""); 83const int DEFAULT_SSMTP_PORT = 465;
114 bindtextdomain (PACKAGE, LOCALEDIR); 84const int DEFAULT_IMAP_PORT = 143;
115 textdomain (PACKAGE); 85const int DEFAULT_SIMAP_PORT = 993;
86const int DEFAULT_XMPP_C2S_PORT = 5222;
87const int DEFAULT_NNTP_PORT = 119;
88const int DEFAULT_NNTPS_PORT = 563;
89const int DEFAULT_CLAMD_PORT = 3310;
90
91int main(int argc, char **argv) {
92 setlocale(LC_ALL, "");
93 bindtextdomain(PACKAGE, LOCALEDIR);
94 textdomain(PACKAGE);
116 95
117 /* determine program- and service-name quickly */ 96 /* determine program- and service-name quickly */
118 progname = strrchr(argv[0], '/'); 97 progname = strrchr(argv[0], '/');
119 if(progname != NULL) progname++; 98 if (progname != NULL) {
120 else progname = argv[0]; 99 progname++;
100 } else {
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();
121 107
122 size_t prog_name_len = strlen(progname); 108 size_t prog_name_len = strlen(progname);
123 if(prog_name_len > 6 && !memcmp(progname, "check_", 6)) { 109 const size_t prefix_length = strlen("check_");
124 SERVICE = strdup(progname + 6); 110
125 for(size_t i = 0; i < prog_name_len - 6; i++) 111 if (prog_name_len <= prefix_length) {
126 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 }
127 } 124 }
128 125
129 /* 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
130 * user specifies other options) */ 127 * user specifies other options) */
131 server_expect = calloc(sizeof(char *), 2); 128 config.server_expect = calloc(2, sizeof(char *));
132 129
133 /* determine defaults for this service's protocol */ 130 if (config.server_expect == NULL) {
134 if (!strncmp(SERVICE, "UDP", 3)) { 131 die(STATE_UNKNOWN, _("Allocation failed"));
135 PROTOCOL = IPPROTO_UDP;
136 }
137 else if (!strncmp(SERVICE, "FTP", 3)) {
138 EXPECT = "220";
139 QUIT = "QUIT\r\n";
140 PORT = 21;
141 }
142 else if (!strncmp(SERVICE, "POP", 3) || !strncmp(SERVICE, "POP3", 4)) {
143 EXPECT = "+OK";
144 QUIT = "QUIT\r\n";
145 PORT = 110;
146 }
147 else if (!strncmp(SERVICE, "SMTP", 4)) {
148 EXPECT = "220";
149 QUIT = "QUIT\r\n";
150 PORT = 25;
151 } 132 }
152 else if (!strncmp(SERVICE, "IMAP", 4)) { 133
153 EXPECT = "* OK"; 134 /* determine defaults for this service's protocol */
154 QUIT = "a1 LOGOUT\r\n"; 135 if (!strncmp(config.service, "UDP", strlen("UDP"))) {
155 PORT = 143; 136 config.protocol = IPPROTO_UDP;
137 } else if (!strncmp(config.service, "FTP", strlen("FTP"))) {
138 config.server_expect[0] = "220";
139 config.quit = "QUIT\r\n";
140 config.server_port = DEFAULT_FTP_PORT;
141 } else if (!strncmp(config.service, "POP", strlen("POP")) ||
142 !strncmp(config.service, "POP3", strlen("POP3"))) {
143 config.server_expect[0] = "+OK";
144 config.quit = "QUIT\r\n";
145 config.server_port = DEFAULT_POP_PORT;
146 } else if (!strncmp(config.service, "SMTP", strlen("SMTP"))) {
147 config.server_expect[0] = "220";
148 config.quit = "QUIT\r\n";
149 config.server_port = DEFAULT_SMTP_PORT;
150 } else if (!strncmp(config.service, "IMAP", strlen("IMAP"))) {
151 config.server_expect[0] = "* OK";
152 config.quit = "a1 LOGOUT\r\n";
153 config.server_port = DEFAULT_IMAP_PORT;
156 } 154 }
157#ifdef HAVE_SSL 155#ifdef HAVE_SSL
158 else if (!strncmp(SERVICE, "SIMAP", 5)) { 156 else if (!strncmp(config.service, "SIMAP", strlen("SIMAP"))) {
159 EXPECT = "* OK"; 157 config.server_expect[0] = "* OK";
160 QUIT = "a1 LOGOUT\r\n"; 158 config.quit = "a1 LOGOUT\r\n";
161 flags |= FLAG_SSL; 159 config.use_tls = true;
162 PORT = 993; 160 config.server_port = DEFAULT_SIMAP_PORT;
163 } 161 } else if (!strncmp(config.service, "SPOP", strlen("SPOP"))) {
164 else if (!strncmp(SERVICE, "SPOP", 4)) { 162 config.server_expect[0] = "+OK";
165 EXPECT = "+OK"; 163 config.quit = "QUIT\r\n";
166 QUIT = "QUIT\r\n"; 164 config.use_tls = true;
167 flags |= FLAG_SSL; 165 config.server_port = DEFAULT_SPOP_PORT;
168 PORT = 995; 166 } else if (!strncmp(config.service, "SSMTP", strlen("SSMTP"))) {
169 } 167 config.server_expect[0] = "220";
170 else if (!strncmp(SERVICE, "SSMTP", 5)) { 168 config.quit = "QUIT\r\n";
171 EXPECT = "220"; 169 config.use_tls = true;
172 QUIT = "QUIT\r\n"; 170 config.server_port = DEFAULT_SSMTP_PORT;
173 flags |= FLAG_SSL; 171 } else if (!strncmp(config.service, "JABBER", strlen("JABBER"))) {
174 PORT = 465; 172 config.send = "<stream:stream to=\'host\' xmlns=\'jabber:client\' "
175 } 173 "xmlns:stream=\'http://etherx.jabber.org/streams\'>\n";
176 else if (!strncmp(SERVICE, "JABBER", 6)) { 174 config.server_expect[0] = "<?xml version=\'1.0\'";
177 SEND = "<stream:stream to=\'host\' xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'>\n"; 175 config.quit = "</stream:stream>\n";
178 EXPECT = "<?xml version=\'1.0\'"; 176 config.hide_output = true;
179 QUIT = "</stream:stream>\n"; 177 config.server_port = DEFAULT_XMPP_C2S_PORT;
180 flags |= FLAG_HIDE_OUTPUT; 178 } else if (!strncmp(config.service, "NNTPS", strlen("NNTPS"))) {
181 PORT = 5222; 179 config.server_expect_count = 2;
182 } 180 config.server_expect[0] = "200";
183 else if (!strncmp (SERVICE, "NNTPS", 5)) { 181 config.server_expect[1] = "201";
184 server_expect_count = 2; 182 config.quit = "QUIT\r\n";
185 server_expect[0] = "200"; 183 config.use_tls = true;
186 server_expect[1] = "201"; 184 config.server_port = DEFAULT_NNTPS_PORT;
187 QUIT = "QUIT\r\n";
188 flags |= FLAG_SSL;
189 PORT = 563;
190 } 185 }
191#endif 186#endif
192 else if (!strncmp (SERVICE, "NNTP", 4)) { 187 else if (!strncmp(config.service, "NNTP", strlen("NNTP"))) {
193 server_expect_count = 2; 188 config.server_expect_count = 2;
194 server_expect = malloc(sizeof(char *) * server_expect_count); 189 char **tmp = realloc(config.server_expect, config.server_expect_count * sizeof(char *));
195 server_expect[0] = strdup("200"); 190 if (tmp == NULL) {
196 server_expect[1] = strdup("201"); 191 free(config.server_expect);
197 QUIT = "QUIT\r\n"; 192 die(STATE_UNKNOWN, _("Allocation failed"));
198 PORT = 119; 193 }
199 } 194 config.server_expect = tmp;
200 else if (!strncmp(SERVICE, "CLAMD", 5)) { 195
201 SEND = "PING"; 196 config.server_expect[0] = strdup("200");
202 EXPECT = "PONG"; 197 config.server_expect[1] = strdup("201");
203 QUIT = NULL; 198 config.quit = "QUIT\r\n";
204 PORT = 3310; 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;
205 } 205 }
206 /* fallthrough check, so it's supposed to use reverse matching */ 206 /* fallthrough check, so it's supposed to use reverse matching */
207 else if (strcmp (SERVICE, "TCP")) 207 else if (strcmp(config.service, "TCP")) {
208 usage (_("CRITICAL - Generic check_tcp called with unknown service\n")); 208 usage(_("CRITICAL - Generic check_tcp called with unknown service\n"));
209 209 }
210 server_address = "127.0.0.1";
211 server_port = PORT;
212 server_send = SEND;
213 server_quit = QUIT;
214 status = NULL;
215 210
216 /* Parse extra opts if any */ 211 /* Parse extra opts if any */
217 argv=np_extra_opts (&argc, argv, progname); 212 argv = np_extra_opts(&argc, argv, progname);
213
214 check_tcp_config_wrapper paw = process_arguments(argc, argv, config);
215 if (paw.errorcode == ERROR) {
216 usage4(_("Could not parse arguments"));
217 }
218 218
219 if (process_arguments (argc, argv) == ERROR) 219 config = paw.config;
220 usage4 (_("Could not parse arguments"));
221 220
222 if(flags & FLAG_VERBOSE) { 221 if (verbosity > 0) {
223 printf("Using service %s\n", SERVICE); 222 printf("Using service %s\n", config.service);
224 printf("Port: %d\n", server_port); 223 printf("Port: %d\n", config.server_port);
225 printf("flags: 0x%x\n", (int)flags);
226 } 224 }
227 225
228 if(EXPECT && !server_expect_count) 226 if ((config.server_expect_count == 0) && config.server_expect[0]) {
229 server_expect_count++; 227 config.server_expect_count++;
228 }
230 229
231 if(PROTOCOL==IPPROTO_UDP && !(server_expect_count && server_send)){ 230 if (config.protocol == IPPROTO_UDP && !(config.server_expect_count && config.send)) {
232 usage(_("With UDP checks, a send/expect string must be specified.")); 231 usage(_("With UDP checks, a send/expect string must be specified."));
233 } 232 }
234 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
235 /* set up the timer */ 240 /* set up the timer */
236 signal (SIGALRM, socket_timeout_alarm_handler); 241 signal(SIGALRM, socket_timeout_alarm_handler);
237 alarm (socket_timeout); 242 alarm(socket_timeout);
238 243
239 /* try to connect to the host at the given port number */ 244 /* try to connect to the host at the given port number */
240 gettimeofday (&tv, NULL); 245 struct timeval start_time;
241 246 gettimeofday(&start_time, NULL);
242 result = np_net_connect (server_address, server_port, &sd, PROTOCOL); 247
243 if (result == STATE_CRITICAL) return econn_refuse_state; 248 int socket_descriptor = 0;
249 mp_subcheck inital_connect_result = mp_subcheck_init();
250
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 }
244 267
245#ifdef HAVE_SSL 268#ifdef HAVE_SSL
246 if (flags & FLAG_SSL){ 269 if (config.use_tls) {
247 result = np_net_ssl_init_with_hostname(sd, (sni_specified ? sni : NULL)); 270 mp_subcheck tls_connection_result = mp_subcheck_init();
248 if (result == STATE_OK && check_cert) { 271 mp_state_enum result = np_net_ssl_init_with_hostname(
249 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 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);
250 } 317 }
251 } 318 }
252 if(result != STATE_OK){
253 if(sd) close(sd);
254 np_net_ssl_cleanup();
255 return result;
256 }
257#endif /* HAVE_SSL */ 319#endif /* HAVE_SSL */
258 320
259 if (server_send != NULL) { /* Something to send? */ 321 if (config.send != NULL) { /* Something to send? */
260 my_send(server_send, strlen(server_send)); 322 my_send(socket_descriptor, config.send, strlen(config.send), config.use_tls);
261 } 323 }
262 324
263 if (delay > 0) { 325 if (config.delay > 0) {
264 tv.tv_sec += delay; 326 start_time.tv_sec += config.delay;
265 sleep (delay); 327 sleep(config.delay);
266 } 328 }
267 329
268 if(flags & FLAG_VERBOSE) { 330 if (verbosity > 0) {
269 if (server_send) { 331 if (config.send) {
270 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);
271 } 336 }
272 if (server_quit) { 337 printf("server_expect_count: %d\n", (int)config.server_expect_count);
273 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]);
274 } 340 }
275 printf("server_expect_count: %d\n", (int)server_expect_count);
276 for(size_t i = 0; i < server_expect_count; i++)
277 printf("\t%zd: %s\n", i, server_expect[i]);
278 } 341 }
279 342
280 /* 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 */
281 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();
282 348
283 if (server_expect_count) { 349 if (config.server_expect_count) {
284 ssize_t received = 0; 350 ssize_t received = 0;
351 char buffer[MAXBUF];
285 352
286 /* watch for the expect string */ 353 /* watch for the expect string */
287 while ((received = my_recv(buffer, sizeof(buffer))) > 0) { 354 while ((received = my_recv(socket_descriptor, buffer, sizeof(buffer), config.use_tls)) >
288 status = realloc(status, len + received + 1); 355 0) {
289 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);
290 len += received; 363 len += received;
291 status[len] = '\0'; 364 received_buffer[len] = '\0';
292 365
293 /* stop reading if user-forced */ 366 /* stop reading if user-forced */
294 if (maxbytes && len >= maxbytes) 367 if (config.maxbytes && len >= config.maxbytes) {
295 break; 368 break;
369 }
296 370
297 if ((match = np_expect_match(status, 371 if ((match = np_expect_match(received_buffer, config.server_expect,
298 server_expect, 372 config.server_expect_count, config.match_flags)) !=
299 server_expect_count, 373 NP_MATCH_RETRY) {
300 match_flags)) != NP_MATCH_RETRY)
301 break; 374 break;
375 }
376
377 fd_set rfds;
378 FD_ZERO(&rfds);
379 FD_SET(socket_descriptor, &rfds);
302 380
303 /* 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 */
304 FD_SET(sd, &rfds); 382 struct timeval timeout;
305 timeout.tv_sec = READ_TIMEOUT; 383 timeout.tv_sec = READ_TIMEOUT;
306 timeout.tv_usec = 0; 384 timeout.tv_usec = 0;
307 if(select(sd + 1, &rfds, NULL, NULL, &timeout) <= 0) 385
386 if (select(socket_descriptor + 1, &rfds, NULL, NULL, &timeout) <= 0) {
308 break; 387 break;
388 }
309 } 389 }
310 390
311 if (match == NP_MATCH_RETRY) 391 if (match == NP_MATCH_RETRY) {
312 match = NP_MATCH_FAILURE; 392 match = NP_MATCH_FAILURE;
393 }
313 394
314 /* no data when expected, so return critical */ 395 /* no data when expected, so return critical */
315 if (len == 0) 396 if (len == 0) {
316 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 }
317 402
318 /* print raw output if we're debugging */ 403 /* print raw output if we're debugging */
319 if(flags & FLAG_VERBOSE) 404 if (verbosity > 0) {
320 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n", 405 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n",
321 (int)len + 1, status); 406 (int)len + 1, received_buffer);
407 }
322 /* strip whitespace from end of output */ 408 /* strip whitespace from end of output */
323 while(--len > 0 && isspace(status[len])) 409 while (--len > 0 && isspace(received_buffer[len])) {
324 status[len] = '\0'; 410 received_buffer[len] = '\0';
411 }
325 } 412 }
326 413
327 if (server_quit != NULL) { 414 if (config.quit != NULL) {
328 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);
329 } 420 }
330 if (sd) close (sd);
331#ifdef HAVE_SSL 421#ifdef HAVE_SSL
332 np_net_ssl_cleanup(); 422 np_net_ssl_cleanup();
333#endif 423#endif
334 424
335 microsec = deltime (tv); 425 long microsec = deltime(start_time);
336 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 }
337 465
338 if (flags & FLAG_TIME_CRIT && elapsed_time > critical_time) 466 mp_add_perfdata_to_subcheck(&elapsed_time_result, time_pd);
339 result = STATE_CRITICAL; 467 mp_add_subcheck_to_check(&overall, elapsed_time_result);
340 else if (flags & FLAG_TIME_WARN && elapsed_time > warning_time)
341 result = STATE_WARNING;
342 468
343 /* did we get the response we hoped? */ 469 /* did we get the response we hoped? */
344 if(match == NP_MATCH_FAILURE && result != STATE_CRITICAL) 470 if (match == NP_MATCH_FAILURE) {
345 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 }
346 480
347 /* reset the alarm */ 481 /* reset the alarm */
348 alarm (0); 482 alarm(0);
349
350 /* this is a bit stupid, because we don't want to print the
351 * response time (which can look ok to the user) if we didn't get
352 * the response we were looking for. if-else */
353 printf("%s %s - ", SERVICE, state_text(result));
354
355 if(match == NP_MATCH_FAILURE && len && !(flags & FLAG_HIDE_OUTPUT))
356 printf("Unexpected response from host/socket: %s", status);
357 else {
358 if(match == NP_MATCH_FAILURE)
359 printf("Unexpected response from host/socket on ");
360 else
361 printf("%.3f second response time on ", elapsed_time);
362 if(server_address[0] != '/') {
363 if (host_specified)
364 printf("%s port %d",
365 server_address, server_port);
366 else
367 printf("port %d", server_port);
368 }
369 else
370 printf("socket %s", server_address);
371 }
372 483
373 if (match != NP_MATCH_FAILURE && !(flags & FLAG_HIDE_OUTPUT) && len) 484 mp_exit(overall);
374 printf (" [%s]", status);
375
376 /* perf-data doesn't apply when server doesn't talk properly,
377 * so print all zeroes on warn and crit. Use fperfdata since
378 * localisation settings can make different outputs */
379 if(match == NP_MATCH_FAILURE)
380 printf ("|%s",
381 fperfdata ("time", elapsed_time, "s",
382 (flags & FLAG_TIME_WARN ? true : false), 0,
383 (flags & FLAG_TIME_CRIT ? true : false), 0,
384 true, 0,
385 true, socket_timeout)
386 );
387 else
388 printf("|%s",
389 fperfdata ("time", elapsed_time, "s",
390 (flags & FLAG_TIME_WARN ? true : false), warning_time,
391 (flags & FLAG_TIME_CRIT ? true : false), critical_time,
392 true, 0,
393 true, socket_timeout)
394 );
395
396 putchar('\n');
397 return result;
398} 485}
399 486
400
401
402/* process command-line arguments */ 487/* process command-line arguments */
403static int process_arguments (int argc, char **argv) { 488static check_tcp_config_wrapper process_arguments(int argc, char **argv, check_tcp_config config) {
404 int c;
405 bool escape = false;
406 char *temp;
407
408 enum { 489 enum {
409 SNI_OPTION = CHAR_MAX + 1 490 SNI_OPTION = CHAR_MAX + 1,
491 output_format_index,
410 }; 492 };
411 493
412 int option = 0;
413 static struct option longopts[] = { 494 static struct option longopts[] = {
414 {"hostname", required_argument, 0, 'H'}, 495 {"hostname", required_argument, 0, 'H'},
415 {"critical", required_argument, 0, 'c'}, 496 {"critical", required_argument, 0, 'c'},
@@ -437,278 +518,311 @@ static int process_arguments (int argc, char **argv) {
437 {"ssl", no_argument, 0, 'S'}, 518 {"ssl", no_argument, 0, 'S'},
438 {"sni", required_argument, 0, SNI_OPTION}, 519 {"sni", required_argument, 0, SNI_OPTION},
439 {"certificate", required_argument, 0, 'D'}, 520 {"certificate", required_argument, 0, 'D'},
440 {0, 0, 0, 0} 521 {"output-format", required_argument, 0, output_format_index},
441 }; 522 {0, 0, 0, 0}};
442 523
443 if (argc < 2) 524 if (argc < 2) {
444 usage4 (_("No arguments found")); 525 usage4(_("No arguments found"));
526 }
445 527
446 /* backwards compatibility */ 528 /* backwards compatibility */
447 for (c = 1; c < argc; c++) { 529 for (int i = 1; i < argc; i++) {
448 if (strcmp ("-to", argv[c]) == 0) 530 if (strcmp("-to", argv[i]) == 0) {
449 strcpy (argv[c], "-t"); 531 strcpy(argv[i], "-t");
450 else if (strcmp ("-wt", argv[c]) == 0) 532 } else if (strcmp("-wt", argv[i]) == 0) {
451 strcpy (argv[c], "-w"); 533 strcpy(argv[i], "-w");
452 else if (strcmp ("-ct", argv[c]) == 0) 534 } else if (strcmp("-ct", argv[i]) == 0) {
453 strcpy (argv[c], "-c"); 535 strcpy(argv[i], "-c");
536 }
454 } 537 }
455 538
456 if (!is_option (argv[1])) { 539 if (!is_option(argv[1])) {
457 server_address = argv[1]; 540 config.server_address = argv[1];
458 argv[1] = argv[0]; 541 argv[1] = argv[0];
459 argv = &argv[1]; 542 argv = &argv[1];
460 argc--; 543 argc--;
461 } 544 }
462 545
463 while (1) { 546 bool escape = false;
464 c = getopt_long (argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", 547
465 longopts, &option); 548 while (true) {
549 int option = 0;
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);
466 552
467 if (c == -1 || c == EOF || c == 1) 553 if (option_index == -1 || option_index == EOF || option_index == 1) {
468 break; 554 break;
555 }
469 556
470 switch (c) { 557 switch (option_index) {
471 case '?': /* print short usage statement if args not parsable */ 558 case '?': /* print short usage statement if args not parsable */
472 usage5 (); 559 usage5();
473 case 'h': /* help */ 560 case 'h': /* help */
474 print_help (); 561 print_help(config.service);
475 exit (STATE_UNKNOWN); 562 exit(STATE_UNKNOWN);
476 case 'V': /* version */ 563 case 'V': /* version */
477 print_revision (progname, NP_VERSION); 564 print_revision(progname, NP_VERSION);
478 exit (STATE_UNKNOWN); 565 exit(STATE_UNKNOWN);
479 case 'v': /* verbose mode */ 566 case 'v': /* verbose mode */
480 flags |= FLAG_VERBOSE; 567 verbosity++;
481 match_flags |= NP_MATCH_VERBOSE; 568 config.match_flags |= NP_MATCH_VERBOSE;
482 break; 569 break;
483 case '4': 570 case '4': // Apparently unused TODO
484 address_family = AF_INET; 571 address_family = AF_INET;
485 break; 572 break;
486 case '6': 573 case '6': // Apparently unused TODO
487#ifdef USE_IPV6 574#ifdef USE_IPV6
488 address_family = AF_INET6; 575 address_family = AF_INET6;
489#else 576#else
490 usage4 (_("IPv6 support not available")); 577 usage4(_("IPv6 support not available"));
491#endif 578#endif
492 break; 579 break;
493 case 'H': /* hostname */ 580 case 'H': /* hostname */
494 host_specified = true; 581 config.host_specified = true;
495 server_address = optarg; 582 config.server_address = optarg;
496 break;
497 case 'c': /* critical */
498 critical_time = strtod (optarg, NULL);
499 flags |= FLAG_TIME_CRIT;
500 break; 583 break;
501 case 'j': /* hide output */ 584 case 'c': /* critical */
502 flags |= FLAG_HIDE_OUTPUT; 585 config.critical_time = strtod(optarg, NULL);
586 config.critical_time_set = true;
503 break; 587 break;
504 case 'w': /* warning */ 588 case 'j': /* hide output */
505 warning_time = strtod (optarg, NULL); 589 config.hide_output = true;
506 flags |= FLAG_TIME_WARN;
507 break; 590 break;
508 case 'C': 591 case 'w': /* warning */
509 crit_codes = realloc (crit_codes, ++crit_codes_count); 592 config.warning_time = strtod(optarg, NULL);
510 crit_codes[crit_codes_count - 1] = optarg; 593 config.warning_time_set = true;
511 break; 594 break;
512 case 'W': 595 case 't': /* timeout */
513 warn_codes = realloc (warn_codes, ++warn_codes_count); 596 if (!is_intpos(optarg)) {
514 warn_codes[warn_codes_count - 1] = optarg; 597 usage4(_("Timeout interval must be a positive integer"));
515 break; 598 } else {
516 case 't': /* timeout */ 599 socket_timeout = atoi(optarg);
517 if (!is_intpos (optarg)) 600 }
518 usage4 (_("Timeout interval must be a positive integer"));
519 else
520 socket_timeout = atoi (optarg);
521 break; 601 break;
522 case 'p': /* port */ 602 case 'p': /* port */
523 if (!is_intpos (optarg)) 603 if (!is_intpos(optarg)) {
524 usage4 (_("Port must be a positive integer")); 604 usage4(_("Port must be a positive integer"));
525 else 605 } else {
526 server_port = atoi (optarg); 606 config.server_port = atoi(optarg);
607 }
527 break; 608 break;
528 case 'E': 609 case 'E':
529 escape = true; 610 escape = true;
530 break; 611 break;
531 case 's': 612 case 's':
532 if (escape) 613 if (escape) {
533 server_send = np_escaped_string(optarg); 614 config.send = np_escaped_string(optarg);
534 else 615 } else {
535 xasprintf(&server_send, "%s", optarg); 616 xasprintf(&config.send, "%s", optarg);
617 }
536 break; 618 break;
537 case 'e': /* expect string (may be repeated) */ 619 case 'e': /* expect string (may be repeated) */
538 match_flags &= ~NP_MATCH_EXACT; 620 config.match_flags &= ~NP_MATCH_EXACT;
539 if (server_expect_count == 0) 621 if (config.server_expect_count == 0) {
540 server_expect = malloc (sizeof (char *) * (++server_expect_count)); 622 config.server_expect = malloc(sizeof(char *) * (++config.server_expect_count));
541 else 623 } else {
542 server_expect = realloc (server_expect, sizeof (char *) * (++server_expect_count)); 624 config.server_expect =
543 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;
544 break; 632 break;
545 case 'm': 633 case 'm':
546 if (!is_intpos (optarg)) 634 if (!is_intpos(optarg)) {
547 usage4 (_("Maxbytes must be a positive integer")); 635 usage4(_("Maxbytes must be a positive integer"));
548 else 636 } else {
549 maxbytes = strtol (optarg, NULL, 0); 637 config.maxbytes = strtol(optarg, NULL, 0);
638 }
550 break; 639 break;
551 case 'q': 640 case 'q':
552 if (escape) 641 if (escape) {
553 server_quit = np_escaped_string(optarg); 642 config.quit = np_escaped_string(optarg);
554 else 643 } else {
555 xasprintf(&server_quit, "%s\r\n", optarg); 644 xasprintf(&config.quit, "%s\r\n", optarg);
645 }
556 break; 646 break;
557 case 'r': 647 case 'r':
558 if (!strncmp(optarg,"ok",2)) 648 if (!strncmp(optarg, "ok", 2)) {
559 econn_refuse_state = STATE_OK; 649 config.econn_refuse_state = STATE_OK;
560 else if (!strncmp(optarg,"warn",4)) 650 } else if (!strncmp(optarg, "warn", 4)) {
561 econn_refuse_state = STATE_WARNING; 651 config.econn_refuse_state = STATE_WARNING;
562 else if (!strncmp(optarg,"crit",4)) 652 } else if (!strncmp(optarg, "crit", 4)) {
563 econn_refuse_state = STATE_CRITICAL; 653 config.econn_refuse_state = STATE_CRITICAL;
564 else 654 } else {
565 usage4 (_("Refuse must be one of ok, warn, crit")); 655 usage4(_("Refuse must be one of ok, warn, crit"));
656 }
566 break; 657 break;
567 case 'M': 658 case 'M':
568 if (!strncmp(optarg,"ok",2)) 659 if (!strncmp(optarg, "ok", 2)) {
569 expect_mismatch_state = STATE_OK; 660 config.expect_mismatch_state = STATE_OK;
570 else if (!strncmp(optarg,"warn",4)) 661 } else if (!strncmp(optarg, "warn", 4)) {
571 expect_mismatch_state = STATE_WARNING; 662 config.expect_mismatch_state = STATE_WARNING;
572 else if (!strncmp(optarg,"crit",4)) 663 } else if (!strncmp(optarg, "crit", 4)) {
573 expect_mismatch_state = STATE_CRITICAL; 664 config.expect_mismatch_state = STATE_CRITICAL;
574 else 665 } else {
575 usage4 (_("Mismatch must be one of ok, warn, crit")); 666 usage4(_("Mismatch must be one of ok, warn, crit"));
667 }
576 break; 668 break;
577 case 'd': 669 case 'd':
578 if (is_intpos (optarg)) 670 if (is_intpos(optarg)) {
579 delay = atoi (optarg); 671 config.delay = atoi(optarg);
580 else 672 } else {
581 usage4 (_("Delay must be a positive integer")); 673 usage4(_("Delay must be a positive integer"));
674 }
582 break; 675 break;
583 case 'D': /* Check SSL cert validity - days 'til certificate expiration */ 676 case 'D': /* Check SSL cert validity - days 'til certificate expiration */
584#ifdef HAVE_SSL 677#ifdef HAVE_SSL
585# ifdef USE_OPENSSL /* XXX */ 678# ifdef USE_OPENSSL /* XXX */
586 if ((temp=strchr(optarg,','))!=NULL) { 679 {
587 *temp='\0'; 680 char *temp;
588 if (!is_intnonneg (optarg)) 681 if ((temp = strchr(optarg, ',')) != NULL) {
589 usage2 (_("Invalid certificate expiration period"), optarg); 682 *temp = '\0';
590 days_till_exp_warn = atoi (optarg); 683 if (!is_intnonneg(optarg)) {
591 *temp=','; 684 usage2(_("Invalid certificate expiration period"), optarg);
592 temp++; 685 }
593 if (!is_intnonneg (temp)) 686 config.days_till_exp_warn = atoi(optarg);
594 usage2 (_("Invalid certificate expiration period"), temp); 687 *temp = ',';
595 days_till_exp_crit = atoi (temp); 688 temp++;
689 if (!is_intnonneg(temp)) {
690 usage2(_("Invalid certificate expiration period"), temp);
691 }
692 config.days_till_exp_crit = atoi(temp);
693 } else {
694 config.days_till_exp_crit = 0;
695 if (!is_intnonneg(optarg)) {
696 usage2(_("Invalid certificate expiration period"), optarg);
697 }
698 config.days_till_exp_warn = atoi(optarg);
596 } 699 }
597 else { 700 config.check_cert = true;
598 days_till_exp_crit=0; 701 config.use_tls = true;
599 if (!is_intnonneg (optarg)) 702 } break;
600 usage2 (_("Invalid certificate expiration period"), optarg); 703# endif /* USE_OPENSSL */
601 days_till_exp_warn = atoi (optarg);
602 }
603 check_cert = true;
604 flags |= FLAG_SSL;
605 break;
606# endif /* USE_OPENSSL */
607#endif 704#endif
608 /* fallthrough if we don't have ssl */ 705 /* fallthrough if we don't have ssl */
609 case 'S': 706 case 'S':
610#ifdef HAVE_SSL 707#ifdef HAVE_SSL
611 flags |= FLAG_SSL; 708 config.use_tls = true;
612#else 709#else
613 die (STATE_UNKNOWN, _("Invalid option - SSL is not available")); 710 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
614#endif 711#endif
615 break; 712 break;
616 case SNI_OPTION: 713 case SNI_OPTION:
617#ifdef HAVE_SSL 714#ifdef HAVE_SSL
618 flags |= FLAG_SSL; 715 config.use_tls = true;
619 sni_specified = true; 716 config.sni_specified = true;
620 sni = optarg; 717 config.sni = optarg;
621#else 718#else
622 die (STATE_UNKNOWN, _("Invalid option - SSL is not available")); 719 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
623#endif 720#endif
624 break; 721 break;
625 case 'A': 722 case 'A':
626 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;
627 break; 735 break;
628 } 736 }
737 }
629 } 738 }
630 739
631 c = optind; 740 int index = optind;
632 if(!host_specified && c < argc) 741 if (!config.host_specified && index < argc) {
633 server_address = strdup (argv[c++]); 742 config.server_address = strdup(argv[index++]);
743 }
634 744
635 if (server_address == NULL) 745 if (config.server_address == NULL) {
636 usage4 (_("You must provide a server address")); 746 usage4(_("You must provide a server address"));
637 else if (server_address[0] != '/' && !is_host(server_address)) 747 } else if (config.server_address[0] != '/' && !is_host(config.server_address)) {
638 die (STATE_CRITICAL, "%s %s - %s: %s\n", SERVICE, state_text(STATE_CRITICAL), _("Invalid hostname, address or socket"), server_address); 748 die(STATE_CRITICAL, "%s %s - %s: %s\n", config.service, state_text(STATE_CRITICAL),
749 _("Invalid hostname, address or socket"), config.server_address);
750 }
639 751
640 return OK; 752 check_tcp_config_wrapper result = {
753 .config = config,
754 .errorcode = OK,
755 };
756 return result;
641} 757}
642 758
643 759void print_help(const char *service) {
644void 760 print_revision(progname, NP_VERSION);
645print_help (void) 761
646{ 762 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
647 print_revision (progname, NP_VERSION); 763 printf(COPYRIGHT, copyright, email);
648 764
649 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 765 printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"),
650 printf (COPYRIGHT, copyright, email); 766 service);
651 767
652 printf (_("This plugin tests %s connections with the specified host (or unix socket).\n\n"), 768 print_usage();
653 SERVICE); 769
654 770 printf(UT_HELP_VRSN);
655 print_usage (); 771 printf(UT_EXTRA_OPTS);
656 772
657 printf (UT_HELP_VRSN); 773 printf(UT_HOST_PORT, 'p', "none");
658 printf (UT_EXTRA_OPTS); 774
659 775 printf(UT_IPv46);
660 printf (UT_HOST_PORT, 'p', "none"); 776
661 777 printf(" %s\n", "-E, --escape");
662 printf (UT_IPv46); 778 printf(" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before "
663 779 "send or quit option"));
664 printf (" %s\n", "-E, --escape"); 780 printf(" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit"));
665 printf (" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before send or quit option")); 781 printf(" %s\n", "-s, --send=STRING");
666 printf (" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit")); 782 printf(" %s\n", _("String to send to the server"));
667 printf (" %s\n", "-s, --send=STRING"); 783 printf(" %s\n", "-e, --expect=STRING");
668 printf (" %s\n", _("String to send to the server")); 784 printf(" %s %s\n", _("String to expect in server response"), _("(may be repeated)"));
669 printf (" %s\n", "-e, --expect=STRING"); 785 printf(" %s\n", "-A, --all");
670 printf (" %s %s\n", _("String to expect in server response"), _("(may be repeated)")); 786 printf(" %s\n", _("All expect strings need to occur in server response. Default is any"));
671 printf (" %s\n", "-A, --all"); 787 printf(" %s\n", "-q, --quit=STRING");
672 printf (" %s\n", _("All expect strings need to occur in server response. Default is any")); 788 printf(" %s\n", _("String to send server to initiate a clean close of the connection"));
673 printf (" %s\n", "-q, --quit=STRING"); 789 printf(" %s\n", "-r, --refuse=ok|warn|crit");
674 printf (" %s\n", _("String to send server to initiate a clean close of the connection")); 790 printf(" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)"));
675 printf (" %s\n", "-r, --refuse=ok|warn|crit"); 791 printf(" %s\n", "-M, --mismatch=ok|warn|crit");
676 printf (" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)")); 792 printf(" %s\n",
677 printf (" %s\n", "-M, --mismatch=ok|warn|crit"); 793 _("Accept expected string mismatches with states ok, warn, crit (default: warn)"));
678 printf (" %s\n", _("Accept expected string mismatches with states ok, warn, crit (default: warn)")); 794 printf(" %s\n", "-j, --jail");
679 printf (" %s\n", "-j, --jail"); 795 printf(" %s\n", _("Hide output from TCP socket"));
680 printf (" %s\n", _("Hide output from TCP socket")); 796 printf(" %s\n", "-m, --maxbytes=INTEGER");
681 printf (" %s\n", "-m, --maxbytes=INTEGER"); 797 printf(" %s\n", _("Close connection once more than this number of bytes are received"));
682 printf (" %s\n", _("Close connection once more than this number of bytes are received")); 798 printf(" %s\n", "-d, --delay=INTEGER");
683 printf (" %s\n", "-d, --delay=INTEGER"); 799 printf(" %s\n", _("Seconds to wait between sending string and polling for response"));
684 printf (" %s\n", _("Seconds to wait between sending string and polling for response"));
685 800
686#ifdef HAVE_SSL 801#ifdef HAVE_SSL
687 printf (" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); 802 printf(" %s\n", "-D, --certificate=INTEGER[,INTEGER]");
688 printf (" %s\n", _("Minimum number of days a certificate has to be valid.")); 803 printf(" %s\n", _("Minimum number of days a certificate has to be valid."));
689 printf (" %s\n", _("1st is #days for warning, 2nd is critical (if not specified - 0).")); 804 printf(" %s\n", _("1st is #days for warning, 2nd is critical (if not specified - 0)."));
690 printf (" %s\n", "-S, --ssl"); 805 printf(" %s\n", "-S, --ssl");
691 printf (" %s\n", _("Use SSL for the connection.")); 806 printf(" %s\n", _("Use SSL for the connection."));
692 printf (" %s\n", "--sni=STRING"); 807 printf(" %s\n", "--sni=STRING");
693 printf (" %s\n", _("SSL server_name")); 808 printf(" %s\n", _("SSL server_name"));
694#endif 809#endif
695 810
696 printf (UT_WARN_CRIT); 811 printf(UT_WARN_CRIT);
697 812
698 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 813 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
699 814
700 printf (UT_VERBOSE); 815 printf(UT_OUTPUT_FORMAT);
816 printf(UT_VERBOSE);
701 817
702 printf (UT_SUPPORT); 818 printf(UT_SUPPORT);
703} 819}
704 820
705 821void print_usage(void) {
706void 822 printf("%s\n", _("Usage:"));
707print_usage (void) 823 printf("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n",
708{ 824 progname);
709 printf ("%s\n", _("Usage:")); 825 printf("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n");
710 printf ("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n",progname); 826 printf("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n");
711 printf ("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n"); 827 printf("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n");
712 printf ("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n");
713 printf ("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n");
714} 828}
diff --git a/plugins/check_tcp.d/config.h b/plugins/check_tcp.d/config.h
new file mode 100644
index 00000000..dc25d79e
--- /dev/null
+++ b/plugins/check_tcp.d/config.h
@@ -0,0 +1,84 @@
1#pragma once
2
3#include "../../lib/utils_tcp.h"
4#include "output.h"
5#include "states.h"
6#include <netinet/in.h>
7
8typedef struct {
9 char *server_address;
10 bool host_specified;
11 int server_port; // TODO can this be a uint16?
12
13 int protocol; /* most common is default */
14 char *service;
15 char *send;
16 char *quit;
17 char **server_expect;
18 size_t server_expect_count;
19 bool use_tls;
20#ifdef HAVE_SSL
21 char *sni;
22 bool sni_specified;
23 bool check_cert;
24 int days_till_exp_warn;
25 int days_till_exp_crit;
26#endif // HAVE_SSL
27 int match_flags;
28 mp_state_enum expect_mismatch_state;
29 unsigned int delay;
30
31 bool warning_time_set;
32 double warning_time;
33 bool critical_time_set;
34 double critical_time;
35
36 mp_state_enum econn_refuse_state;
37
38 ssize_t maxbytes;
39
40 bool hide_output;
41
42 bool output_format_set;
43 mp_output_format output_format;
44} check_tcp_config;
45
46check_tcp_config check_tcp_config_init() {
47 check_tcp_config result = {
48 .server_address = "127.0.0.1",
49 .host_specified = false,
50 .server_port = 0,
51
52 .protocol = IPPROTO_TCP,
53 .service = "TCP",
54 .send = NULL,
55 .quit = NULL,
56 .server_expect = NULL,
57 .server_expect_count = 0,
58 .use_tls = false,
59#ifdef HAVE_SSL
60 .sni = NULL,
61 .sni_specified = false,
62 .check_cert = false,
63 .days_till_exp_warn = 0,
64 .days_till_exp_crit = 0,
65#endif // HAVE_SSL
66 .match_flags = NP_MATCH_EXACT,
67 .expect_mismatch_state = STATE_WARNING,
68 .delay = 0,
69
70 .warning_time_set = false,
71 .warning_time = 0,
72 .critical_time_set = false,
73 .critical_time = 0,
74
75 .econn_refuse_state = STATE_CRITICAL,
76
77 .maxbytes = 0,
78
79 .hide_output = false,
80
81 .output_format_set = false,
82 };
83 return result;
84}
diff --git a/plugins/check_time.c b/plugins/check_time.c
index f50ea427..fc9ba3f9 100644
--- a/plugins/check_time.c
+++ b/plugins/check_time.c
@@ -1,374 +1,355 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_time plugin 3 * Monitoring check_time plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_time plugin 10 * This file contains the check_time plugin
11* 11 *
12* This plugin will check the time difference with the specified host. 12 * This plugin will check the time difference with the specified host.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
31const char *progname = "check_time"; 32const char *progname = "check_time";
32const char *copyright = "1999-2007"; 33const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
34 35
35#include "common.h" 36#include "common.h"
36#include "netutils.h" 37#include "netutils.h"
37#include "utils.h" 38#include "utils.h"
39#include "check_time.d/config.h"
40
41#define UNIX_EPOCH 2208988800UL
42
43typedef struct {
44 int errorcode;
45 check_time_config config;
46} check_time_config_wrapper;
47static check_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
48static void print_help(void);
49void print_usage(void);
38 50
39enum { 51int main(int argc, char **argv) {
40 TIME_PORT = 37 52 setlocale(LC_ALL, "");
41}; 53 bindtextdomain(PACKAGE, LOCALEDIR);
42 54 textdomain(PACKAGE);
43#define UNIX_EPOCH 2208988800UL
44
45uint32_t raw_server_time;
46unsigned long server_time, diff_time;
47int warning_time = 0;
48bool check_warning_time = false;
49int critical_time = 0;
50bool check_critical_time = false;
51unsigned long warning_diff = 0;
52bool check_warning_diff = false;
53unsigned long critical_diff = 0;
54bool check_critical_diff = false;
55int server_port = TIME_PORT;
56char *server_address = NULL;
57bool use_udp = false;
58
59int process_arguments (int, char **);
60void print_help (void);
61void print_usage (void);
62
63int
64main (int argc, char **argv)
65{
66 int sd;
67 int result = STATE_UNKNOWN;
68 time_t conntime;
69
70 setlocale (LC_ALL, "");
71 bindtextdomain (PACKAGE, LOCALEDIR);
72 textdomain (PACKAGE);
73 55
74 /* Parse extra opts if any */ 56 /* Parse extra opts if any */
75 argv=np_extra_opts (&argc, argv, progname); 57 argv = np_extra_opts(&argc, argv, progname);
58
59 check_time_config_wrapper tmp_config = process_arguments(argc, argv);
60 if (tmp_config.errorcode == ERROR) {
61 usage4(_("Could not parse arguments"));
62 }
76 63
77 if (process_arguments (argc, argv) == ERROR) 64 const check_time_config config = tmp_config.config;
78 usage4 (_("Could not parse arguments"));
79 65
80 /* initialize alarm signal handling */ 66 /* initialize alarm signal handling */
81 signal (SIGALRM, socket_timeout_alarm_handler); 67 signal(SIGALRM, socket_timeout_alarm_handler);
82 68
83 /* set socket timeout */ 69 /* set socket timeout */
84 alarm (socket_timeout); 70 alarm(socket_timeout);
85 time (&start_time); 71 time(&start_time);
86 72
73 int socket;
74 mp_state_enum result = STATE_UNKNOWN;
87 /* try to connect to the host at the given port number */ 75 /* try to connect to the host at the given port number */
88 if (use_udp) { 76 if (config.use_udp) {
89 result = my_udp_connect (server_address, server_port, &sd); 77 result = my_udp_connect(config.server_address, config.server_port, &socket);
90 } else { 78 } else {
91 result = my_tcp_connect (server_address, server_port, &sd); 79 result = my_tcp_connect(config.server_address, config.server_port, &socket);
92 } 80 }
93 81
94 if (result != STATE_OK) { 82 if (result != STATE_OK) {
95 if (check_critical_time) 83 if (config.check_critical_time) {
96 result = STATE_CRITICAL; 84 result = STATE_CRITICAL;
97 else if (check_warning_time) 85 } else if (config.check_warning_time) {
98 result = STATE_WARNING; 86 result = STATE_WARNING;
99 else 87 } else {
100 result = STATE_UNKNOWN; 88 result = STATE_UNKNOWN;
101 die (result, 89 }
102 _("TIME UNKNOWN - could not connect to server %s, port %d\n"), 90 die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"),
103 server_address, server_port); 91 config.server_address, config.server_port);
104 } 92 }
105 93
106 if (use_udp) { 94 if (config.use_udp) {
107 if (send (sd, "", 0, 0) < 0) { 95 if (send(socket, "", 0, 0) < 0) {
108 if (check_critical_time) 96 if (config.check_critical_time) {
109 result = STATE_CRITICAL; 97 result = STATE_CRITICAL;
110 else if (check_warning_time) 98 } else if (config.check_warning_time) {
111 result = STATE_WARNING; 99 result = STATE_WARNING;
112 else 100 } else {
113 result = STATE_UNKNOWN; 101 result = STATE_UNKNOWN;
114 die (result, 102 }
115 _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"), 103 die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"),
116 server_address, server_port); 104 config.server_address, config.server_port);
117 } 105 }
118 } 106 }
119 107
120 /* watch for the connection string */ 108 /* watch for the connection string */
121 result = recv (sd, (void *)&raw_server_time, sizeof (raw_server_time), 0); 109 uint32_t raw_server_time;
110 result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0);
122 111
123 /* close the connection */ 112 /* close the connection */
124 close (sd); 113 close(socket);
125 114
126 /* reset the alarm */ 115 /* reset the alarm */
127 time (&end_time); 116 time(&end_time);
128 alarm (0); 117 alarm(0);
129 118
130 /* return a WARNING status if we couldn't read any data */ 119 /* return a WARNING status if we couldn't read any data */
131 if (result <= 0) { 120 if (result <= 0) {
132 if (check_critical_time) 121 if (config.check_critical_time) {
133 result = STATE_CRITICAL; 122 result = STATE_CRITICAL;
134 else if (check_warning_time) 123 } else if (config.check_warning_time) {
135 result = STATE_WARNING; 124 result = STATE_WARNING;
136 else 125 } else {
137 result = STATE_UNKNOWN; 126 result = STATE_UNKNOWN;
138 die (result, 127 }
139 _("TIME UNKNOWN - no data received from server %s, port %d\n"), 128 die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"),
140 server_address, server_port); 129 config.server_address, config.server_port);
141 } 130 }
142 131
143 result = STATE_OK; 132 result = STATE_OK;
144 133
145 conntime = (end_time - start_time); 134 time_t conntime = (end_time - start_time);
146 if (check_critical_time&& conntime > critical_time) 135 if (config.check_critical_time && conntime > config.critical_time) {
147 result = STATE_CRITICAL; 136 result = STATE_CRITICAL;
148 else if (check_warning_time && conntime > warning_time) 137 } else if (config.check_warning_time && conntime > config.warning_time) {
149 result = STATE_WARNING; 138 result = STATE_WARNING;
139 }
150 140
151 if (result != STATE_OK) 141 if (result != STATE_OK) {
152 die (result, _("TIME %s - %d second response time|%s\n"), 142 die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime,
153 state_text (result), (int)conntime, 143 perfdata("time", (long)conntime, "s", config.check_warning_time,
154 perfdata ("time", (long)conntime, "s", 144 (long)config.warning_time, config.check_critical_time,
155 check_warning_time, (long)warning_time, 145 (long)config.critical_time, true, 0, false, 0));
156 check_critical_time, (long)critical_time, 146 }
157 true, 0, false, 0));
158 147
159 server_time = ntohl (raw_server_time) - UNIX_EPOCH; 148 unsigned long server_time;
160 if (server_time > (unsigned long)end_time) 149 unsigned long diff_time;
150 server_time = ntohl(raw_server_time) - UNIX_EPOCH;
151 if (server_time > (unsigned long)end_time) {
161 diff_time = server_time - (unsigned long)end_time; 152 diff_time = server_time - (unsigned long)end_time;
162 else 153 } else {
163 diff_time = (unsigned long)end_time - server_time; 154 diff_time = (unsigned long)end_time - server_time;
155 }
164 156
165 if (check_critical_diff&& diff_time > critical_diff) 157 if (config.check_critical_diff && diff_time > config.critical_diff) {
166 result = STATE_CRITICAL; 158 result = STATE_CRITICAL;
167 else if (check_warning_diff&& diff_time > warning_diff) 159 } else if (config.check_warning_diff && diff_time > config.warning_diff) {
168 result = STATE_WARNING; 160 result = STATE_WARNING;
161 }
169 162
170 printf (_("TIME %s - %lu second time difference|%s %s\n"), 163 printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time,
171 state_text (result), diff_time, 164 perfdata("time", (long)conntime, "s", config.check_warning_time,
172 perfdata ("time", (long)conntime, "s", 165 (long)config.warning_time, config.check_critical_time,
173 check_warning_time, (long)warning_time, 166 (long)config.critical_time, true, 0, false, 0),
174 check_critical_time, (long)critical_time, 167 perfdata("offset", diff_time, "s", config.check_warning_diff, config.warning_diff,
175 true, 0, false, 0), 168 config.check_critical_diff, config.critical_diff, true, 0, false, 0));
176 perfdata ("offset", diff_time, "s",
177 check_warning_diff, warning_diff,
178 check_critical_diff, critical_diff,
179 true, 0, false, 0));
180 return result; 169 return result;
181} 170}
182 171
183
184
185/* process command-line arguments */ 172/* process command-line arguments */
186int 173check_time_config_wrapper process_arguments(int argc, char **argv) {
187process_arguments (int argc, char **argv) 174 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
188{ 175 {"warning-variance", required_argument, 0, 'w'},
189 int c; 176 {"critical-variance", required_argument, 0, 'c'},
190 177 {"warning-connect", required_argument, 0, 'W'},
191 int option = 0; 178 {"critical-connect", required_argument, 0, 'C'},
192 static struct option longopts[] = { 179 {"port", required_argument, 0, 'p'},
193 {"hostname", required_argument, 0, 'H'}, 180 {"udp", no_argument, 0, 'u'},
194 {"warning-variance", required_argument, 0, 'w'}, 181 {"timeout", required_argument, 0, 't'},
195 {"critical-variance", required_argument, 0, 'c'}, 182 {"version", no_argument, 0, 'V'},
196 {"warning-connect", required_argument, 0, 'W'}, 183 {"help", no_argument, 0, 'h'},
197 {"critical-connect", required_argument, 0, 'C'}, 184 {0, 0, 0, 0}};
198 {"port", required_argument, 0, 'p'}, 185
199 {"udp", no_argument, 0, 'u'}, 186 if (argc < 2) {
200 {"timeout", required_argument, 0, 't'}, 187 usage("\n");
201 {"version", no_argument, 0, 'V'}, 188 }
202 {"help", no_argument, 0, 'h'},
203 {0, 0, 0, 0}
204 };
205 189
206 if (argc < 2) 190 for (int i = 1; i < argc; i++) {
207 usage ("\n"); 191 if (strcmp("-to", argv[i]) == 0) {
208 192 strcpy(argv[i], "-t");
209 for (c = 1; c < argc; c++) { 193 } else if (strcmp("-wd", argv[i]) == 0) {
210 if (strcmp ("-to", argv[c]) == 0) 194 strcpy(argv[i], "-w");
211 strcpy (argv[c], "-t"); 195 } else if (strcmp("-cd", argv[i]) == 0) {
212 else if (strcmp ("-wd", argv[c]) == 0) 196 strcpy(argv[i], "-c");
213 strcpy (argv[c], "-w"); 197 } else if (strcmp("-wt", argv[i]) == 0) {
214 else if (strcmp ("-cd", argv[c]) == 0) 198 strcpy(argv[i], "-W");
215 strcpy (argv[c], "-c"); 199 } else if (strcmp("-ct", argv[i]) == 0) {
216 else if (strcmp ("-wt", argv[c]) == 0) 200 strcpy(argv[i], "-C");
217 strcpy (argv[c], "-W"); 201 }
218 else if (strcmp ("-ct", argv[c]) == 0)
219 strcpy (argv[c], "-C");
220 } 202 }
221 203
204 check_time_config_wrapper result = {
205 .errorcode = OK,
206 .config = check_time_config_init(),
207 };
208
209 int option_char;
222 while (true) { 210 while (true) {
223 c = getopt_long (argc, argv, "hVH:w:c:W:C:p:t:u", longopts, 211 int option = 0;
224 &option); 212 option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option);
225 213
226 if (c == -1 || c == EOF) 214 if (option_char == -1 || option_char == EOF) {
227 break; 215 break;
216 }
228 217
229 switch (c) { 218 switch (option_char) {
230 case '?': /* print short usage statement if args not parsable */ 219 case '?': /* print short usage statement if args not parsable */
231 usage5 (); 220 usage5();
232 case 'h': /* help */ 221 case 'h': /* help */
233 print_help (); 222 print_help();
234 exit (STATE_UNKNOWN); 223 exit(STATE_UNKNOWN);
235 case 'V': /* version */ 224 case 'V': /* version */
236 print_revision (progname, NP_VERSION); 225 print_revision(progname, NP_VERSION);
237 exit (STATE_UNKNOWN); 226 exit(STATE_UNKNOWN);
238 case 'H': /* hostname */ 227 case 'H': /* hostname */
239 if (!is_host (optarg)) 228 if (!is_host(optarg)) {
240 usage2 (_("Invalid hostname/address"), optarg); 229 usage2(_("Invalid hostname/address"), optarg);
241 server_address = optarg;
242 break;
243 case 'w': /* warning-variance */
244 if (is_intnonneg (optarg)) {
245 warning_diff = strtoul (optarg, NULL, 10);
246 check_warning_diff = true;
247 } 230 }
248 else if (strspn (optarg, "0123456789:,") > 0) { 231 result.config.server_address = optarg;
249 if (sscanf (optarg, "%lu%*[:,]%d", &warning_diff, &warning_time) == 2) { 232 break;
250 check_warning_diff = true; 233 case 'w': /* warning-variance */
251 check_warning_time = true; 234 if (is_intnonneg(optarg)) {
252 } 235 result.config.warning_diff = strtoul(optarg, NULL, 10);
253 else { 236 result.config.check_warning_diff = true;
254 usage4 (_("Warning thresholds must be a positive integer")); 237 } else if (strspn(optarg, "0123456789:,") > 0) {
238 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.warning_diff,
239 &result.config.warning_time) == 2) {
240 result.config.check_warning_diff = true;
241 result.config.check_warning_time = true;
242 } else {
243 usage4(_("Warning thresholds must be a positive integer"));
255 } 244 }
256 } 245 } else {
257 else { 246 usage4(_("Warning threshold must be a positive integer"));
258 usage4 (_("Warning threshold must be a positive integer"));
259 } 247 }
260 break; 248 break;
261 case 'c': /* critical-variance */ 249 case 'c': /* critical-variance */
262 if (is_intnonneg (optarg)) { 250 if (is_intnonneg(optarg)) {
263 critical_diff = strtoul (optarg, NULL, 10); 251 result.config.critical_diff = strtoul(optarg, NULL, 10);
264 check_critical_diff = true; 252 result.config.check_critical_diff = true;
265 } 253 } else if (strspn(optarg, "0123456789:,") > 0) {
266 else if (strspn (optarg, "0123456789:,") > 0) { 254 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.critical_diff,
267 if (sscanf (optarg, "%lu%*[:,]%d", &critical_diff, &critical_time) == 255 &result.config.critical_time) == 2) {
268 2) { 256 result.config.check_critical_diff = true;
269 check_critical_diff = true; 257 result.config.check_critical_time = true;
270 check_critical_time = true; 258 } else {
271 } 259 usage4(_("Critical thresholds must be a positive integer"));
272 else {
273 usage4 (_("Critical thresholds must be a positive integer"));
274 } 260 }
275 } 261 } else {
276 else { 262 usage4(_("Critical threshold must be a positive integer"));
277 usage4 (_("Critical threshold must be a positive integer"));
278 } 263 }
279 break; 264 break;
280 case 'W': /* warning-connect */ 265 case 'W': /* warning-connect */
281 if (!is_intnonneg (optarg)) 266 if (!is_intnonneg(optarg)) {
282 usage4 (_("Warning threshold must be a positive integer")); 267 usage4(_("Warning threshold must be a positive integer"));
283 else 268 } else {
284 warning_time = atoi (optarg); 269 result.config.warning_time = atoi(optarg);
285 check_warning_time = true; 270 }
271 result.config.check_warning_time = true;
286 break; 272 break;
287 case 'C': /* critical-connect */ 273 case 'C': /* critical-connect */
288 if (!is_intnonneg (optarg)) 274 if (!is_intnonneg(optarg)) {
289 usage4 (_("Critical threshold must be a positive integer")); 275 usage4(_("Critical threshold must be a positive integer"));
290 else 276 } else {
291 critical_time = atoi (optarg); 277 result.config.critical_time = atoi(optarg);
292 check_critical_time = true; 278 }
279 result.config.check_critical_time = true;
293 break; 280 break;
294 case 'p': /* port */ 281 case 'p': /* port */
295 if (!is_intnonneg (optarg)) 282 if (!is_intnonneg(optarg)) {
296 usage4 (_("Port must be a positive integer")); 283 usage4(_("Port must be a positive integer"));
297 else 284 } else {
298 server_port = atoi (optarg); 285 result.config.server_port = atoi(optarg);
286 }
299 break; 287 break;
300 case 't': /* timeout */ 288 case 't': /* timeout */
301 if (!is_intnonneg (optarg)) 289 if (!is_intnonneg(optarg)) {
302 usage2 (_("Timeout interval must be a positive integer"), optarg); 290 usage2(_("Timeout interval must be a positive integer"), optarg);
303 else 291 } else {
304 socket_timeout = atoi (optarg); 292 socket_timeout = atoi(optarg);
293 }
305 break; 294 break;
306 case 'u': /* udp */ 295 case 'u': /* udp */
307 use_udp = true; 296 result.config.use_udp = true;
308 } 297 }
309 } 298 }
310 299
311 c = optind; 300 option_char = optind;
312 if (server_address == NULL) { 301 if (result.config.server_address == NULL) {
313 if (argc > c) { 302 if (argc > option_char) {
314 if (!is_host (argv[c])) 303 if (!is_host(argv[option_char])) {
315 usage2 (_("Invalid hostname/address"), optarg); 304 usage2(_("Invalid hostname/address"), optarg);
316 server_address = argv[c]; 305 }
317 } 306 result.config.server_address = argv[option_char];
318 else { 307 } else {
319 usage4 (_("Hostname was not supplied")); 308 usage4(_("Hostname was not supplied"));
320 } 309 }
321 } 310 }
322 311
323 return OK; 312 return result;
324} 313}
325 314
326 315void print_help(void) {
327
328void
329print_help (void)
330{
331 char *myport; 316 char *myport;
332 xasprintf (&myport, "%d", TIME_PORT); 317 xasprintf(&myport, "%d", TIME_PORT);
333 318
334 print_revision (progname, NP_VERSION); 319 print_revision(progname, NP_VERSION);
335 320
336 printf ("Copyright (c) 1999 Ethan Galstad\n"); 321 printf("Copyright (c) 1999 Ethan Galstad\n");
337 printf (COPYRIGHT, copyright, email); 322 printf(COPYRIGHT, copyright, email);
338 323
339 printf ("%s\n", _("This plugin will check the time on the specified host.")); 324 printf("%s\n", _("This plugin will check the time on the specified host."));
340 325
341 printf ("\n\n"); 326 printf("\n\n");
342 327
343 print_usage (); 328 print_usage();
344 329
345 printf (UT_HELP_VRSN); 330 printf(UT_HELP_VRSN);
346 printf (UT_EXTRA_OPTS); 331 printf(UT_EXTRA_OPTS);
347 332
348 printf (UT_HOST_PORT, 'p', myport); 333 printf(UT_HOST_PORT, 'p', myport);
349 334
350 printf (" %s\n", "-u, --udp"); 335 printf(" %s\n", "-u, --udp");
351 printf (" %s\n", _("Use UDP to connect, not TCP")); 336 printf(" %s\n", _("Use UDP to connect, not TCP"));
352 printf (" %s\n", "-w, --warning-variance=INTEGER"); 337 printf(" %s\n", "-w, --warning-variance=INTEGER");
353 printf (" %s\n", _("Time difference (sec.) necessary to result in a warning status")); 338 printf(" %s\n", _("Time difference (sec.) necessary to result in a warning status"));
354 printf (" %s\n", "-c, --critical-variance=INTEGER"); 339 printf(" %s\n", "-c, --critical-variance=INTEGER");
355 printf (" %s\n", _("Time difference (sec.) necessary to result in a critical status")); 340 printf(" %s\n", _("Time difference (sec.) necessary to result in a critical status"));
356 printf (" %s\n", "-W, --warning-connect=INTEGER"); 341 printf(" %s\n", "-W, --warning-connect=INTEGER");
357 printf (" %s\n", _("Response time (sec.) necessary to result in warning status")); 342 printf(" %s\n", _("Response time (sec.) necessary to result in warning status"));
358 printf (" %s\n", "-C, --critical-connect=INTEGER"); 343 printf(" %s\n", "-C, --critical-connect=INTEGER");
359 printf (" %s\n", _("Response time (sec.) necessary to result in critical status")); 344 printf(" %s\n", _("Response time (sec.) necessary to result in critical status"));
360 345
361 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 346 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
362 347
363 printf (UT_SUPPORT); 348 printf(UT_SUPPORT);
364} 349}
365 350
366 351void print_usage(void) {
367 352 printf("%s\n", _("Usage:"));
368void 353 printf("%s -H <host_address> [-p port] [-u] [-w variance] [-c variance]\n", progname);
369print_usage (void) 354 printf(" [-W connect_time] [-C connect_time] [-t timeout]\n");
370{
371 printf ("%s\n", _("Usage:"));
372 printf ("%s -H <host_address> [-p port] [-u] [-w variance] [-c variance]\n",progname);
373 printf (" [-W connect_time] [-C connect_time] [-t timeout]\n");
374} 355}
diff --git a/plugins/check_time.d/config.h b/plugins/check_time.d/config.h
new file mode 100644
index 00000000..09bd7c45
--- /dev/null
+++ b/plugins/check_time.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 TIME_PORT = 37
8};
9
10typedef struct {
11 char *server_address;
12 int server_port;
13 bool use_udp;
14
15 int warning_time;
16 bool check_warning_time;
17 int critical_time;
18 bool check_critical_time;
19 unsigned long warning_diff;
20 bool check_warning_diff;
21 unsigned long critical_diff;
22 bool check_critical_diff;
23} check_time_config;
24
25check_time_config check_time_config_init() {
26 check_time_config tmp = {
27 .server_address = NULL,
28 .server_port = TIME_PORT,
29 .use_udp = false,
30
31 .warning_time = 0,
32 .check_warning_time = false,
33 .critical_time = 0,
34 .check_critical_time = false,
35
36 .warning_diff = 0,
37 .check_warning_diff = false,
38 .critical_diff = 0,
39 .check_critical_diff = false,
40 };
41 return tmp;
42}
diff --git a/plugins/check_ups.c b/plugins/check_ups.c
index 380ff3bc..54decce3 100644
--- a/plugins/check_ups.c
+++ b/plugins/check_ups.c
@@ -6,7 +6,7 @@
6 * Copyright (c) 2000 Tom Shields 6 * Copyright (c) 2000 Tom Shields
7 * 2004 Alain Richard <alain.richard@equation.fr> 7 * 2004 Alain Richard <alain.richard@equation.fr>
8 * 2004 Arnaud Quette <arnaud.quette@mgeups.com> 8 * 2004 Arnaud Quette <arnaud.quette@mgeups.com>
9 * Copyright (c) 2002-2023 Monitoring Plugins Development Team 9 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
10 * 10 *
11 * Description: 11 * Description:
12 * 12 *
@@ -33,100 +33,52 @@
33 *****************************************************************************/ 33 *****************************************************************************/
34 34
35const char *progname = "check_ups"; 35const char *progname = "check_ups";
36const char *copyright = "2000-2023"; 36const char *copyright = "2000-2024";
37const char *email = "devel@monitoring-plugins.org"; 37const char *email = "devel@monitoring-plugins.org";
38 38
39#include "common.h" 39#include "common.h"
40#include "netutils.h" 40#include "netutils.h"
41#include "utils.h" 41#include "utils.h"
42#include "check_ups.d/config.h"
43#include "states.h"
42 44
43enum { PORT = 3493 }; 45enum {
44 46 NOSUCHVAR = ERROR - 1
45#define UPS_NONE 0 /* no supported options */ 47};
46#define UPS_UTILITY 1 /* supports utility line */
47#define UPS_BATTPCT 2 /* supports percent battery remaining */
48#define UPS_STATUS 4 /* supports UPS status */
49#define UPS_TEMP 8 /* supports UPS temperature */
50#define UPS_LOADPCT 16 /* supports load percent */
51#define UPS_REALPOWER 32 /* supports real power */
52
53#define UPSSTATUS_NONE 0
54#define UPSSTATUS_OFF 1
55#define UPSSTATUS_OL 2
56#define UPSSTATUS_OB 4
57#define UPSSTATUS_LB 8
58#define UPSSTATUS_CAL 16
59#define UPSSTATUS_RB 32 /*Replace Battery */
60#define UPSSTATUS_BYPASS 64
61#define UPSSTATUS_OVER 128
62#define UPSSTATUS_TRIM 256
63#define UPSSTATUS_BOOST 512
64#define UPSSTATUS_CHRG 1024
65#define UPSSTATUS_DISCHRG 2048
66#define UPSSTATUS_UNKNOWN 4096
67#define UPSSTATUS_ALARM 8192
68
69enum { NOSUCHVAR = ERROR - 1 };
70
71typedef struct ups_config {
72 unsigned int server_port;
73 char *server_address;
74 char *ups_name;
75 double warning_value;
76 double critical_value;
77 bool check_warn;
78 bool check_crit;
79 int check_variable;
80 int status;
81 bool temp_output_c;
82} ups_config;
83
84ups_config ups_config_init(void) {
85 ups_config tmp = {0};
86 tmp.server_port = PORT;
87 tmp.server_address = NULL;
88 tmp.ups_name = NULL;
89 tmp.check_variable = UPS_NONE;
90 tmp.status = UPSSTATUS_NONE;
91
92 return tmp;
93}
94 48
95// Forward declarations 49// Forward declarations
96int determine_status(ups_config *, int *supported_options); 50typedef struct {
97int get_ups_variable(const char *, char *, const ups_config config); 51 int errorcode;
98 52 int ups_status;
99int process_arguments(int, char **, ups_config *); 53 int supported_options;
100int validate_arguments(ups_config); 54} determine_status_result;
101void print_help(void); 55static determine_status_result determine_status(check_ups_config /*config*/);
56static int get_ups_variable(const char * /*varname*/, char * /*buf*/, check_ups_config config);
57
58typedef struct {
59 int errorcode;
60 check_ups_config config;
61} check_ups_config_wrapper;
62static check_ups_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
63static check_ups_config_wrapper validate_arguments(check_ups_config_wrapper /*config_wrapper*/);
64
65static void print_help(void);
102void print_usage(void); 66void print_usage(void);
103 67
104int main(int argc, char **argv) { 68int main(int argc, char **argv) {
105 setlocale(LC_ALL, ""); 69 setlocale(LC_ALL, "");
106 bindtextdomain(PACKAGE, LOCALEDIR); 70 bindtextdomain(PACKAGE, LOCALEDIR);
107 textdomain(PACKAGE); 71 textdomain(PACKAGE);
108
109 char *ups_status;
110 ups_status = strdup("N/A");
111
112 char *data;
113 data = strdup("");
114
115 char *message;
116 message = strdup("");
117
118 // Exit result
119 int result = STATE_UNKNOWN;
120
121 /* Parse extra opts if any */ 72 /* Parse extra opts if any */
122 argv = np_extra_opts(&argc, argv, progname); 73 argv = np_extra_opts(&argc, argv, progname);
123 74
124 // Config from commandline 75 check_ups_config_wrapper tmp_config = process_arguments(argc, argv);
125 ups_config config = ups_config_init();
126 76
127 if (process_arguments(argc, argv, &config) == ERROR) { 77 if (tmp_config.errorcode == ERROR) {
128 usage4(_("Could not parse arguments")); 78 usage4(_("Could not parse arguments"));
129 } 79 }
80 // Config from commandline
81 check_ups_config config = tmp_config.config;
130 82
131 /* initialize alarm signal handling */ 83 /* initialize alarm signal handling */
132 signal(SIGALRM, socket_timeout_alarm_handler); 84 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -134,74 +86,76 @@ int main(int argc, char **argv) {
134 /* set socket timeout */ 86 /* set socket timeout */
135 alarm(socket_timeout); 87 alarm(socket_timeout);
136 88
137 int supported_options = UPS_NONE;
138
139 /* get the ups status if possible */ 89 /* get the ups status if possible */
140 if (determine_status(&config, &supported_options) != OK) { 90 determine_status_result query_result = determine_status(config);
91 if (query_result.errorcode != OK) {
141 return STATE_CRITICAL; 92 return STATE_CRITICAL;
142 } 93 }
143 94
95 int ups_status_flags = query_result.ups_status;
96 int supported_options = query_result.supported_options;
144 97
145 if (supported_options & UPS_STATUS) { 98 // Exit result
146 99 mp_state_enum result = STATE_UNKNOWN;
147 ups_status = strdup(""); 100 char *message = NULL;
148 101
102 if (supported_options & UPS_STATUS) {
103 char *ups_status = strdup("");
149 result = STATE_OK; 104 result = STATE_OK;
150 105
151 if (config.status & UPSSTATUS_OFF) { 106 if (ups_status_flags & UPSSTATUS_OFF) {
152 xasprintf(&ups_status, "Off"); 107 xasprintf(&ups_status, "Off");
153 result = STATE_CRITICAL; 108 result = STATE_CRITICAL;
154 } else if ((config.status & (UPSSTATUS_OB | UPSSTATUS_LB)) == 109 } else if ((ups_status_flags & (UPSSTATUS_OB | UPSSTATUS_LB)) ==
155 (UPSSTATUS_OB | UPSSTATUS_LB)) { 110 (UPSSTATUS_OB | UPSSTATUS_LB)) {
156 xasprintf(&ups_status, _("On Battery, Low Battery")); 111 xasprintf(&ups_status, _("On Battery, Low Battery"));
157 result = STATE_CRITICAL; 112 result = STATE_CRITICAL;
158 } else { 113 } else {
159 if (config.status & UPSSTATUS_OL) { 114 if (ups_status_flags & UPSSTATUS_OL) {
160 xasprintf(&ups_status, "%s%s", ups_status, _("Online")); 115 xasprintf(&ups_status, "%s%s", ups_status, _("Online"));
161 } 116 }
162 if (config.status & UPSSTATUS_OB) { 117 if (ups_status_flags & UPSSTATUS_OB) {
163 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery")); 118 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery"));
164 result = max_state(result, STATE_WARNING); 119 result = max_state(result, STATE_WARNING);
165 } 120 }
166 if (config.status & UPSSTATUS_LB) { 121 if (ups_status_flags & UPSSTATUS_LB) {
167 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery")); 122 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery"));
168 result = max_state(result, STATE_WARNING); 123 result = max_state(result, STATE_WARNING);
169 } 124 }
170 if (config.status & UPSSTATUS_CAL) { 125 if (ups_status_flags & UPSSTATUS_CAL) {
171 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating")); 126 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating"));
172 } 127 }
173 if (config.status & UPSSTATUS_RB) { 128 if (ups_status_flags & UPSSTATUS_RB) {
174 xasprintf(&ups_status, "%s%s", ups_status, 129 xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery"));
175 _(", Replace Battery"));
176 result = max_state(result, STATE_WARNING); 130 result = max_state(result, STATE_WARNING);
177 } 131 }
178 if (config.status & UPSSTATUS_BYPASS) { 132 if (ups_status_flags & UPSSTATUS_BYPASS) {
179 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass")); 133 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass"));
180 // Bypassing the battery is likely a bad thing 134 // Bypassing the battery is likely a bad thing
181 result = STATE_CRITICAL; 135 result = STATE_CRITICAL;
182 } 136 }
183 if (config.status & UPSSTATUS_OVER) { 137 if (ups_status_flags & UPSSTATUS_OVER) {
184 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload")); 138 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload"));
185 result = max_state(result, STATE_WARNING); 139 result = max_state(result, STATE_WARNING);
186 } 140 }
187 if (config.status & UPSSTATUS_TRIM) { 141 if (ups_status_flags & UPSSTATUS_TRIM) {
188 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming")); 142 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming"));
189 } 143 }
190 if (config.status & UPSSTATUS_BOOST) { 144 if (ups_status_flags & UPSSTATUS_BOOST) {
191 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting")); 145 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting"));
192 } 146 }
193 if (config.status & UPSSTATUS_CHRG) { 147 if (ups_status_flags & UPSSTATUS_CHRG) {
194 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging")); 148 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging"));
195 } 149 }
196 if (config.status & UPSSTATUS_DISCHRG) { 150 if (ups_status_flags & UPSSTATUS_DISCHRG) {
197 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging")); 151 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging"));
198 result = max_state(result, STATE_WARNING); 152 result = max_state(result, STATE_WARNING);
199 } 153 }
200 if (config.status & UPSSTATUS_ALARM) { 154 if (ups_status_flags & UPSSTATUS_ALARM) {
201 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM")); 155 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM"));
202 result = STATE_CRITICAL; 156 result = STATE_CRITICAL;
203 } 157 }
204 if (config.status & UPSSTATUS_UNKNOWN) { 158 if (ups_status_flags & UPSSTATUS_UNKNOWN) {
205 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown")); 159 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown"));
206 } 160 }
207 } 161 }
@@ -210,7 +164,7 @@ int main(int argc, char **argv) {
210 164
211 int res; 165 int res;
212 char temp_buffer[MAX_INPUT_BUFFER]; 166 char temp_buffer[MAX_INPUT_BUFFER];
213 167 char *performance_data = strdup("");
214 /* get the ups utility voltage if possible */ 168 /* get the ups utility voltage if possible */
215 res = get_ups_variable("input.voltage", temp_buffer, config); 169 res = get_ups_variable("input.voltage", temp_buffer, config);
216 if (res == NOSUCHVAR) { 170 if (res == NOSUCHVAR) {
@@ -233,24 +187,20 @@ int main(int argc, char **argv) {
233 } 187 }
234 188
235 if (config.check_variable == UPS_UTILITY) { 189 if (config.check_variable == UPS_UTILITY) {
236 if (config.check_crit && 190 if (config.check_crit && ups_utility_deviation >= config.critical_value) {
237 ups_utility_deviation >= config.critical_value) {
238 result = STATE_CRITICAL; 191 result = STATE_CRITICAL;
239 } else if (config.check_warn && 192 } else if (config.check_warn && ups_utility_deviation >= config.warning_value) {
240 ups_utility_deviation >= config.warning_value) {
241 result = max_state(result, STATE_WARNING); 193 result = max_state(result, STATE_WARNING);
242 } 194 }
243 xasprintf(&data, "%s", 195 xasprintf(&performance_data, "%s",
244 perfdata("voltage", (long)(1000 * ups_utility_voltage), 196 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV",
245 "mV", config.check_warn, 197 config.check_warn, (long)(1000 * config.warning_value),
246 (long)(1000 * config.warning_value), 198 config.check_crit, (long)(1000 * config.critical_value), true, 0,
247 config.check_crit,
248 (long)(1000 * config.critical_value), true, 0,
249 false, 0)); 199 false, 0));
250 } else { 200 } else {
251 xasprintf(&data, "%s", 201 xasprintf(&performance_data, "%s",
252 perfdata("voltage", (long)(1000 * ups_utility_voltage), 202 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false,
253 "mV", false, 0, false, 0, true, 0, false, 0)); 203 0, true, 0, false, 0));
254 } 204 }
255 } 205 }
256 206
@@ -268,22 +218,19 @@ int main(int argc, char **argv) {
268 xasprintf(&message, "%sBatt=%3.1f%% ", message, ups_battery_percent); 218 xasprintf(&message, "%sBatt=%3.1f%% ", message, ups_battery_percent);
269 219
270 if (config.check_variable == UPS_BATTPCT) { 220 if (config.check_variable == UPS_BATTPCT) {
271 if (config.check_crit && 221 if (config.check_crit && ups_battery_percent <= config.critical_value) {
272 ups_battery_percent <= config.critical_value) {
273 result = STATE_CRITICAL; 222 result = STATE_CRITICAL;
274 } else if (config.check_warn && 223 } else if (config.check_warn && ups_battery_percent <= config.warning_value) {
275 ups_battery_percent <= config.warning_value) {
276 result = max_state(result, STATE_WARNING); 224 result = max_state(result, STATE_WARNING);
277 } 225 }
278 xasprintf(&data, "%s %s", data, 226 xasprintf(&performance_data, "%s %s", performance_data,
279 perfdata("battery", (long)ups_battery_percent, "%", 227 perfdata("battery", (long)ups_battery_percent, "%", config.check_warn,
280 config.check_warn, (long)(config.warning_value), 228 (long)(config.warning_value), config.check_crit,
281 config.check_crit, (long)(config.critical_value), 229 (long)(config.critical_value), true, 0, true, 100));
282 true, 0, true, 100));
283 } else { 230 } else {
284 xasprintf(&data, "%s %s", data, 231 xasprintf(&performance_data, "%s %s", performance_data,
285 perfdata("battery", (long)ups_battery_percent, "%", false, 232 perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true,
286 0, false, 0, true, 0, true, 100)); 233 0, true, 100));
287 } 234 }
288 } 235 }
289 236
@@ -301,22 +248,19 @@ int main(int argc, char **argv) {
301 xasprintf(&message, "%sLoad=%3.1f%% ", message, ups_load_percent); 248 xasprintf(&message, "%sLoad=%3.1f%% ", message, ups_load_percent);
302 249
303 if (config.check_variable == UPS_LOADPCT) { 250 if (config.check_variable == UPS_LOADPCT) {
304 if (config.check_crit && 251 if (config.check_crit && ups_load_percent >= config.critical_value) {
305 ups_load_percent >= config.critical_value) {
306 result = STATE_CRITICAL; 252 result = STATE_CRITICAL;
307 } else if (config.check_warn && 253 } else if (config.check_warn && ups_load_percent >= config.warning_value) {
308 ups_load_percent >= config.warning_value) {
309 result = max_state(result, STATE_WARNING); 254 result = max_state(result, STATE_WARNING);
310 } 255 }
311 xasprintf(&data, "%s %s", data, 256 xasprintf(&performance_data, "%s %s", performance_data,
312 perfdata("load", (long)ups_load_percent, "%", 257 perfdata("load", (long)ups_load_percent, "%", config.check_warn,
313 config.check_warn, (long)(config.warning_value), 258 (long)(config.warning_value), config.check_crit,
314 config.check_crit, (long)(config.critical_value), 259 (long)(config.critical_value), true, 0, true, 100));
315 true, 0, true, 100));
316 } else { 260 } else {
317 xasprintf(&data, "%s %s", data, 261 xasprintf(&performance_data, "%s %s", performance_data,
318 perfdata("load", (long)ups_load_percent, "%", false, 0, 262 perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0,
319 false, 0, true, 0, true, 100)); 263 true, 100));
320 } 264 }
321 } 265 }
322 266
@@ -345,19 +289,17 @@ int main(int argc, char **argv) {
345 if (config.check_variable == UPS_TEMP) { 289 if (config.check_variable == UPS_TEMP) {
346 if (config.check_crit && ups_temperature >= config.critical_value) { 290 if (config.check_crit && ups_temperature >= config.critical_value) {
347 result = STATE_CRITICAL; 291 result = STATE_CRITICAL;
348 } else if (config.check_warn && 292 } else if (config.check_warn && ups_temperature >= config.warning_value) {
349 ups_temperature >= config.warning_value) {
350 result = max_state(result, STATE_WARNING); 293 result = max_state(result, STATE_WARNING);
351 } 294 }
352 xasprintf(&data, "%s %s", data, 295 xasprintf(&performance_data, "%s %s", performance_data,
353 perfdata("temp", (long)ups_temperature, tunits, 296 perfdata("temp", (long)ups_temperature, tunits, config.check_warn,
354 config.check_warn, (long)(config.warning_value), 297 (long)(config.warning_value), config.check_crit,
355 config.check_crit, (long)(config.critical_value), 298 (long)(config.critical_value), true, 0, false, 0));
356 true, 0, false, 0));
357 } else { 299 } else {
358 xasprintf(&data, "%s %s", data, 300 xasprintf(&performance_data, "%s %s", performance_data,
359 perfdata("temp", (long)ups_temperature, tunits, false, 0, 301 perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0,
360 false, 0, true, 0, false, 0)); 302 false, 0));
361 } 303 }
362 } 304 }
363 305
@@ -376,19 +318,17 @@ int main(int argc, char **argv) {
376 if (config.check_variable == UPS_REALPOWER) { 318 if (config.check_variable == UPS_REALPOWER) {
377 if (config.check_crit && ups_realpower >= config.critical_value) { 319 if (config.check_crit && ups_realpower >= config.critical_value) {
378 result = STATE_CRITICAL; 320 result = STATE_CRITICAL;
379 } else if (config.check_warn && 321 } else if (config.check_warn && ups_realpower >= config.warning_value) {
380 ups_realpower >= config.warning_value) {
381 result = max_state(result, STATE_WARNING); 322 result = max_state(result, STATE_WARNING);
382 } 323 }
383 xasprintf(&data, "%s %s", data, 324 xasprintf(&performance_data, "%s %s", performance_data,
384 perfdata("realpower", (long)ups_realpower, "W", 325 perfdata("realpower", (long)ups_realpower, "W", config.check_warn,
385 config.check_warn, (long)(config.warning_value), 326 (long)(config.warning_value), config.check_crit,
386 config.check_crit, (long)(config.critical_value), 327 (long)(config.critical_value), true, 0, false, 0));
387 true, 0, false, 0));
388 } else { 328 } else {
389 xasprintf(&data, "%s %s", data, 329 xasprintf(&performance_data, "%s %s", performance_data,
390 perfdata("realpower", (long)ups_realpower, "W", false, 0, 330 perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0,
391 false, 0, true, 0, false, 0)); 331 false, 0));
392 } 332 }
393 } 333 }
394 334
@@ -402,73 +342,79 @@ int main(int argc, char **argv) {
402 /* reset timeout */ 342 /* reset timeout */
403 alarm(0); 343 alarm(0);
404 344
405 printf("UPS %s - %s|%s\n", state_text(result), message, data); 345 printf("UPS %s - %s|%s\n", state_text(result), message, performance_data);
406 return result; 346 exit(result);
407} 347}
408 348
409/* determines what options are supported by the UPS */ 349/* determines what options are supported by the UPS */
410int determine_status(ups_config *config, int *supported_options) { 350determine_status_result determine_status(const check_ups_config config) {
411 char recv_buffer[MAX_INPUT_BUFFER]; 351
352 determine_status_result result = {
353 .errorcode = OK,
354 .ups_status = UPSSTATUS_NONE,
355 .supported_options = 0,
356 };
412 357
413 int res = get_ups_variable("ups.status", recv_buffer, *config); 358 char recv_buffer[MAX_INPUT_BUFFER];
359 int res = get_ups_variable("ups.status", recv_buffer, config);
414 if (res == NOSUCHVAR) { 360 if (res == NOSUCHVAR) {
415 return OK; 361 return result;
416 } 362 }
417 363
418 if (res != STATE_OK) { 364 if (res != STATE_OK) {
419 printf("%s\n", _("Invalid response received from host")); 365 printf("%s\n", _("Invalid response received from host"));
420 return ERROR; 366 result.errorcode = ERROR;
367 return result;
421 } 368 }
422 369
423 *supported_options |= UPS_STATUS; 370 result.supported_options |= UPS_STATUS;
424 371
425 char temp_buffer[MAX_INPUT_BUFFER]; 372 char temp_buffer[MAX_INPUT_BUFFER];
426 373
427 strcpy(temp_buffer, recv_buffer); 374 strcpy(temp_buffer, recv_buffer);
428 for (char *ptr = (char *)strtok(temp_buffer, " "); ptr != NULL; 375 for (char *ptr = strtok(temp_buffer, " "); ptr != NULL; ptr = strtok(NULL, " ")) {
429 ptr = (char *)strtok(NULL, " ")) {
430 if (!strcmp(ptr, "OFF")) { 376 if (!strcmp(ptr, "OFF")) {
431 config->status |= UPSSTATUS_OFF; 377 result.ups_status |= UPSSTATUS_OFF;
432 } else if (!strcmp(ptr, "OL")) { 378 } else if (!strcmp(ptr, "OL")) {
433 config->status |= UPSSTATUS_OL; 379 result.ups_status |= UPSSTATUS_OL;
434 } else if (!strcmp(ptr, "OB")) { 380 } else if (!strcmp(ptr, "OB")) {
435 config->status |= UPSSTATUS_OB; 381 result.ups_status |= UPSSTATUS_OB;
436 } else if (!strcmp(ptr, "LB")) { 382 } else if (!strcmp(ptr, "LB")) {
437 config->status |= UPSSTATUS_LB; 383 result.ups_status |= UPSSTATUS_LB;
438 } else if (!strcmp(ptr, "CAL")) { 384 } else if (!strcmp(ptr, "CAL")) {
439 config->status |= UPSSTATUS_CAL; 385 result.ups_status |= UPSSTATUS_CAL;
440 } else if (!strcmp(ptr, "RB")) { 386 } else if (!strcmp(ptr, "RB")) {
441 config->status |= UPSSTATUS_RB; 387 result.ups_status |= UPSSTATUS_RB;
442 } else if (!strcmp(ptr, "BYPASS")) { 388 } else if (!strcmp(ptr, "BYPASS")) {
443 config->status |= UPSSTATUS_BYPASS; 389 result.ups_status |= UPSSTATUS_BYPASS;
444 } else if (!strcmp(ptr, "OVER")) { 390 } else if (!strcmp(ptr, "OVER")) {
445 config->status |= UPSSTATUS_OVER; 391 result.ups_status |= UPSSTATUS_OVER;
446 } else if (!strcmp(ptr, "TRIM")) { 392 } else if (!strcmp(ptr, "TRIM")) {
447 config->status |= UPSSTATUS_TRIM; 393 result.ups_status |= UPSSTATUS_TRIM;
448 } else if (!strcmp(ptr, "BOOST")) { 394 } else if (!strcmp(ptr, "BOOST")) {
449 config->status |= UPSSTATUS_BOOST; 395 result.ups_status |= UPSSTATUS_BOOST;
450 } else if (!strcmp(ptr, "CHRG")) { 396 } else if (!strcmp(ptr, "CHRG")) {
451 config->status |= UPSSTATUS_CHRG; 397 result.ups_status |= UPSSTATUS_CHRG;
452 } else if (!strcmp(ptr, "DISCHRG")) { 398 } else if (!strcmp(ptr, "DISCHRG")) {
453 config->status |= UPSSTATUS_DISCHRG; 399 result.ups_status |= UPSSTATUS_DISCHRG;
454 } else if (!strcmp(ptr, "ALARM")) { 400 } else if (!strcmp(ptr, "ALARM")) {
455 config->status |= UPSSTATUS_ALARM; 401 result.ups_status |= UPSSTATUS_ALARM;
456 } else { 402 } else {
457 config->status |= UPSSTATUS_UNKNOWN; 403 result.ups_status |= UPSSTATUS_UNKNOWN;
458 } 404 }
459 } 405 }
460 406
461 return OK; 407 return result;
462} 408}
463 409
464/* gets a variable value for a specific UPS */ 410/* gets a variable value for a specific UPS */
465int get_ups_variable(const char *varname, char *buf, const ups_config config) { 411int get_ups_variable(const char *varname, char *buf, const check_ups_config config) {
466 char send_buffer[MAX_INPUT_BUFFER]; 412 char send_buffer[MAX_INPUT_BUFFER];
467 413
468 /* create the command string to send to the UPS daemon */ 414 /* create the command string to send to the UPS daemon */
469 /* Add LOGOUT to avoid read failure logs */ 415 /* Add LOGOUT to avoid read failure logs */
470 int res = snprintf(send_buffer, sizeof(send_buffer), 416 int res = snprintf(send_buffer, sizeof(send_buffer), "GET VAR %s %s\nLOGOUT\n", config.ups_name,
471 "GET VAR %s %s\nLOGOUT\n", config.ups_name, varname); 417 varname);
472 if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) { 418 if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) {
473 printf("%s\n", _("UPS name to long for buffer")); 419 printf("%s\n", _("UPS name to long for buffer"));
474 return ERROR; 420 return ERROR;
@@ -477,8 +423,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
477 char temp_buffer[MAX_INPUT_BUFFER]; 423 char temp_buffer[MAX_INPUT_BUFFER];
478 424
479 /* send the command to the daemon and get a response back */ 425 /* send the command to the daemon and get a response back */
480 if (process_tcp_request(config.server_address, config.server_port, 426 if (process_tcp_request(config.server_address, config.server_port, send_buffer, temp_buffer,
481 send_buffer, temp_buffer,
482 sizeof(temp_buffer)) != STATE_OK) { 427 sizeof(temp_buffer)) != STATE_OK) {
483 printf("%s\n", _("Invalid response received from host")); 428 printf("%s\n", _("Invalid response received from host"));
484 return ERROR; 429 return ERROR;
@@ -496,8 +441,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
496 ptr[len - 1] = 0; 441 ptr[len - 1] = 0;
497 } 442 }
498 if (strcmp(ptr, "ERR UNKNOWN-UPS") == 0) { 443 if (strcmp(ptr, "ERR UNKNOWN-UPS") == 0) {
499 printf(_("CRITICAL - no such UPS '%s' on that host\n"), 444 printf(_("CRITICAL - no such UPS '%s' on that host\n"), config.ups_name);
500 config.ups_name);
501 return ERROR; 445 return ERROR;
502 } 446 }
503 447
@@ -534,7 +478,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
534 [-wv warn_value] [-cv crit_value] [-to to_sec] */ 478 [-wv warn_value] [-cv crit_value] [-to to_sec] */
535 479
536/* process command-line arguments */ 480/* process command-line arguments */
537int process_arguments(int argc, char **argv, ups_config *config) { 481check_ups_config_wrapper process_arguments(int argc, char **argv) {
538 482
539 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 483 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
540 {"ups", required_argument, 0, 'u'}, 484 {"ups", required_argument, 0, 'u'},
@@ -548,8 +492,14 @@ int process_arguments(int argc, char **argv, ups_config *config) {
548 {"help", no_argument, 0, 'h'}, 492 {"help", no_argument, 0, 'h'},
549 {0, 0, 0, 0}}; 493 {0, 0, 0, 0}};
550 494
495 check_ups_config_wrapper result = {
496 .errorcode = OK,
497 .config = check_ups_config_init(),
498 };
499
551 if (argc < 2) { 500 if (argc < 2) {
552 return ERROR; 501 result.errorcode = ERROR;
502 return result;
553 } 503 }
554 504
555 int c; 505 int c;
@@ -576,52 +526,52 @@ int process_arguments(int argc, char **argv, ups_config *config) {
576 usage5(); 526 usage5();
577 case 'H': /* hostname */ 527 case 'H': /* hostname */
578 if (is_host(optarg)) { 528 if (is_host(optarg)) {
579 config->server_address = optarg; 529 result.config.server_address = optarg;
580 } else { 530 } else {
581 usage2(_("Invalid hostname/address"), optarg); 531 usage2(_("Invalid hostname/address"), optarg);
582 } 532 }
583 break; 533 break;
584 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for 534 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for
585 Fahrenheit) */ 535 Fahrenheit) */
586 config->temp_output_c = true; 536 result.config.temp_output_c = true;
587 break; 537 break;
588 case 'u': /* ups name */ 538 case 'u': /* ups name */
589 config->ups_name = optarg; 539 result.config.ups_name = optarg;
590 break; 540 break;
591 case 'p': /* port */ 541 case 'p': /* port */
592 if (is_intpos(optarg)) { 542 if (is_intpos(optarg)) {
593 config->server_port = atoi(optarg); 543 result.config.server_port = atoi(optarg);
594 } else { 544 } else {
595 usage2(_("Port must be a positive integer"), optarg); 545 usage2(_("Port must be a positive integer"), optarg);
596 } 546 }
597 break; 547 break;
598 case 'c': /* critical time threshold */ 548 case 'c': /* critical time threshold */
599 if (is_intnonneg(optarg)) { 549 if (is_intnonneg(optarg)) {
600 config->critical_value = atoi(optarg); 550 result.config.critical_value = atoi(optarg);
601 config->check_crit = true; 551 result.config.check_crit = true;
602 } else { 552 } else {
603 usage2(_("Critical time must be a positive integer"), optarg); 553 usage2(_("Critical time must be a positive integer"), optarg);
604 } 554 }
605 break; 555 break;
606 case 'w': /* warning time threshold */ 556 case 'w': /* warning time threshold */
607 if (is_intnonneg(optarg)) { 557 if (is_intnonneg(optarg)) {
608 config->warning_value = atoi(optarg); 558 result.config.warning_value = atoi(optarg);
609 config->check_warn = true; 559 result.config.check_warn = true;
610 } else { 560 } else {
611 usage2(_("Warning time must be a positive integer"), optarg); 561 usage2(_("Warning time must be a positive integer"), optarg);
612 } 562 }
613 break; 563 break;
614 case 'v': /* variable */ 564 case 'v': /* variable */
615 if (!strcmp(optarg, "LINE")) { 565 if (!strcmp(optarg, "LINE")) {
616 config->check_variable = UPS_UTILITY; 566 result.config.check_variable = UPS_UTILITY;
617 } else if (!strcmp(optarg, "TEMP")) { 567 } else if (!strcmp(optarg, "TEMP")) {
618 config->check_variable = UPS_TEMP; 568 result.config.check_variable = UPS_TEMP;
619 } else if (!strcmp(optarg, "BATTPCT")) { 569 } else if (!strcmp(optarg, "BATTPCT")) {
620 config->check_variable = UPS_BATTPCT; 570 result.config.check_variable = UPS_BATTPCT;
621 } else if (!strcmp(optarg, "LOADPCT")) { 571 } else if (!strcmp(optarg, "LOADPCT")) {
622 config->check_variable = UPS_LOADPCT; 572 result.config.check_variable = UPS_LOADPCT;
623 } else if (!strcmp(optarg, "REALPOWER")) { 573 } else if (!strcmp(optarg, "REALPOWER")) {
624 config->check_variable = UPS_REALPOWER; 574 result.config.check_variable = UPS_REALPOWER;
625 } else { 575 } else {
626 usage2(_("Unrecognized UPS variable"), optarg); 576 usage2(_("Unrecognized UPS variable"), optarg);
627 } 577 }
@@ -642,27 +592,27 @@ int process_arguments(int argc, char **argv, ups_config *config) {
642 } 592 }
643 } 593 }
644 594
645 if (config->server_address == NULL && argc > optind) { 595 if (result.config.server_address == NULL && argc > optind) {
646 if (is_host(argv[optind])) { 596 if (is_host(argv[optind])) {
647 config->server_address = argv[optind++]; 597 result.config.server_address = argv[optind++];
648 } else { 598 } else {
649 usage2(_("Invalid hostname/address"), optarg); 599 usage2(_("Invalid hostname/address"), optarg);
650 } 600 }
651 } 601 }
652 602
653 if (config->server_address == NULL) { 603 if (result.config.server_address == NULL) {
654 config->server_address = strdup("127.0.0.1"); 604 result.config.server_address = strdup("127.0.0.1");
655 } 605 }
656 606
657 return validate_arguments(*config); 607 return validate_arguments(result);
658} 608}
659 609
660int validate_arguments(ups_config config) { 610check_ups_config_wrapper validate_arguments(check_ups_config_wrapper config_wrapper) {
661 if (!config.ups_name) { 611 if (config_wrapper.config.ups_name) {
662 printf("%s\n", _("Error : no UPS indicated")); 612 printf("%s\n", _("Error : no UPS indicated"));
663 return ERROR; 613 config_wrapper.errorcode = ERROR;
664 } 614 }
665 return OK; 615 return config_wrapper;
666} 616}
667 617
668void print_help(void) { 618void print_help(void) {
@@ -731,8 +681,7 @@ void print_help(void) {
731 "with Russell Kroll's")); 681 "with Russell Kroll's"));
732 printf(" %s\n", _("Network UPS Tools be installed on the remote host. If " 682 printf(" %s\n", _("Network UPS Tools be installed on the remote host. If "
733 "you do not have the")); 683 "you do not have the"));
734 printf(" %s\n", 684 printf(" %s\n", _("package installed on your system, you can download it from"));
735 _("package installed on your system, you can download it from"));
736 printf(" %s\n", _("http://www.networkupstools.org")); 685 printf(" %s\n", _("http://www.networkupstools.org"));
737 686
738 printf(UT_SUPPORT); 687 printf(UT_SUPPORT);
diff --git a/plugins/check_ups.d/config.h b/plugins/check_ups.d/config.h
new file mode 100644
index 00000000..e05edceb
--- /dev/null
+++ b/plugins/check_ups.d/config.h
@@ -0,0 +1,54 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6#define UPS_NONE 0 /* no supported options */
7#define UPS_UTILITY 1 /* supports utility line */
8#define UPS_BATTPCT 2 /* supports percent battery remaining */
9#define UPS_STATUS 4 /* supports UPS status */
10#define UPS_TEMP 8 /* supports UPS temperature */
11#define UPS_LOADPCT 16 /* supports load percent */
12#define UPS_REALPOWER 32 /* supports real power */
13
14#define UPSSTATUS_NONE 0
15#define UPSSTATUS_OFF 1
16#define UPSSTATUS_OL 2
17#define UPSSTATUS_OB 4
18#define UPSSTATUS_LB 8
19#define UPSSTATUS_CAL 16
20#define UPSSTATUS_RB 32 /*Replace Battery */
21#define UPSSTATUS_BYPASS 64
22#define UPSSTATUS_OVER 128
23#define UPSSTATUS_TRIM 256
24#define UPSSTATUS_BOOST 512
25#define UPSSTATUS_CHRG 1024
26#define UPSSTATUS_DISCHRG 2048
27#define UPSSTATUS_UNKNOWN 4096
28#define UPSSTATUS_ALARM 8192
29
30enum {
31 PORT = 3493
32};
33
34typedef struct ups_config {
35 unsigned int server_port;
36 char *server_address;
37 char *ups_name;
38 double warning_value;
39 double critical_value;
40 bool check_warn;
41 bool check_crit;
42 int check_variable;
43 bool temp_output_c;
44} check_ups_config;
45
46check_ups_config check_ups_config_init(void) {
47 check_ups_config tmp = {0};
48 tmp.server_port = PORT;
49 tmp.server_address = NULL;
50 tmp.ups_name = NULL;
51 tmp.check_variable = UPS_NONE;
52
53 return tmp;
54}
diff --git a/plugins/check_users.c b/plugins/check_users.c
index 89b95369..2340eae4 100644
--- a/plugins/check_users.c
+++ b/plugins/check_users.c
@@ -1,282 +1,285 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_users plugin 3 * Monitoring check_users plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2012 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_users plugin 10 * This file contains the check_users plugin
11* 11 *
12* This plugin checks the number of users currently logged in on the local 12 * This plugin checks the number of users currently logged in on the local
13* system and generates an error if the number exceeds the thresholds 13 * system and generates an error if the number exceeds the thresholds
14* specified. 14 * specified.
15* 15 *
16* 16 *
17* This program is free software: you can redistribute it and/or modify 17 * This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by 18 * it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or 19 * the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version. 20 * (at your option) any later version.
21* 21 *
22* This program is distributed in the hope that it will be useful, 22 * This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details. 25 * GNU General Public License for more details.
26* 26 *
27* You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>. 28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29* 29 *
30* 30 *
31*****************************************************************************/ 31 *****************************************************************************/
32 32
33const char *progname = "check_users"; 33const char *progname = "check_users";
34const char *copyright = "2000-2007"; 34const char *copyright = "2000-2024";
35const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
36 36
37#include "common.h" 37#include "check_users.d/users.h"
38#include "utils.h" 38#include "output.h"
39#include "perfdata.h"
40#include "states.h"
41#include "utils_base.h"
42#include "./common.h"
43#include "./utils.h"
44#include "check_users.d/config.h"
45#include "thresholds.h"
39 46
40#if HAVE_WTSAPI32_H 47#if HAVE_WTSAPI32_H
41# include <windows.h> 48# include <windows.h>
42# include <wtsapi32.h> 49# include <wtsapi32.h>
43# undef ERROR 50# undef ERROR
44# define ERROR -1 51# define ERROR -1
45#elif HAVE_UTMPX_H 52#elif HAVE_UTMPX_H
46# include <utmpx.h> 53# include <utmpx.h>
47#else 54#else
48# include "popen.h" 55# include "popen.h"
49#endif 56#endif
50 57
51#ifdef HAVE_LIBSYSTEMD 58#ifdef HAVE_LIBSYSTEMD
52#include <systemd/sd-daemon.h> 59# include <systemd/sd-daemon.h>
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
58int process_arguments (int, char **); 69void print_help(void);
59void print_help (void); 70void print_usage(void);
60void print_usage (void);
61 71
62char *warning_range = NULL; 72int main(int argc, char **argv) {
63char *critical_range = NULL; 73 setlocale(LC_ALL, "");
64thresholds *thlds = NULL; 74 bindtextdomain(PACKAGE, LOCALEDIR);
65 75 textdomain(PACKAGE);
66int
67main (int argc, char **argv)
68{
69 int users = -1;
70 int result = STATE_UNKNOWN;
71#if HAVE_WTSAPI32_H
72 WTS_SESSION_INFO *wtsinfo;
73 DWORD wtscount;
74 DWORD index;
75#elif HAVE_UTMPX_H
76 struct utmpx *putmpx;
77#else
78 char input_buffer[MAX_INPUT_BUFFER];
79#endif
80
81 setlocale (LC_ALL, "");
82 bindtextdomain (PACKAGE, LOCALEDIR);
83 textdomain (PACKAGE);
84 76
85 /* Parse extra opts if any */ 77 /* Parse extra opts if any */
86 argv = np_extra_opts (&argc, argv, progname); 78 argv = np_extra_opts(&argc, argv, progname);
87 79
88 if (process_arguments (argc, argv) == ERROR) 80 check_users_config_wrapper tmp_config = process_arguments(argc, argv);
89 usage4 (_("Could not parse arguments"));
90 81
91 users = 0; 82 if (tmp_config.errorcode == ERROR) {
92 83 usage4(_("Could not parse arguments"));
93#ifdef HAVE_LIBSYSTEMD
94 if (sd_booted () > 0)
95 users = sd_get_sessions (NULL);
96 else {
97#endif
98#if HAVE_WTSAPI32_H
99 if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE,
100 0, 1, &wtsinfo, &wtscount)) {
101 printf(_("Could not enumerate RD sessions: %d\n"), GetLastError());
102 return STATE_UNKNOWN;
103 } 84 }
104 85
105 for (index = 0; index < wtscount; index++) { 86 check_users_config config = tmp_config.config;
106 LPTSTR username;
107 DWORD size;
108 int len;
109
110 if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
111 wtsinfo[index].SessionId, WTSUserName, &username, &size))
112 continue;
113
114 len = lstrlen(username);
115 87
116 WTSFreeMemory(username); 88#ifdef _WIN32
117 89# if HAVE_WTSAPI32_H
118 if (len == 0) 90 get_num_of_users_wrapper user_wrapper = get_num_of_users_windows();
119 continue; 91# else
120 92# error Did not find WTSAPI32
121 if (wtsinfo[index].State == WTSActive || 93# endif // HAVE_WTSAPI32_H
122 wtsinfo[index].State == WTSDisconnected)
123 users++;
124 }
125
126 WTSFreeMemory(wtsinfo);
127#elif HAVE_UTMPX_H
128 /* get currently logged users from utmpx */
129 setutxent ();
130
131 while ((putmpx = getutxent ()) != NULL)
132 if (putmpx->ut_type == USER_PROCESS)
133 users++;
134
135 endutxent ();
136#else 94#else
137 /* run the command */ 95# ifdef HAVE_LIBSYSTEMD
138 child_process = spopen (WHO_COMMAND); 96 get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd();
139 if (child_process == NULL) { 97# elif HAVE_UTMPX_H
140 printf (_("Could not open pipe: %s\n"), WHO_COMMAND); 98 get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp();
141 return STATE_UNKNOWN; 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);
142 } 107 }
108 mp_subcheck sc_users = mp_subcheck_init();
143 109
144 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 110 if (user_wrapper.errorcode != 0) {
145 if (child_stderr == NULL) 111 sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN);
146 printf (_("Could not open stderr for %s\n"), WHO_COMMAND); 112 sc_users.output = "Failed to retrieve number of users";
147 113 mp_add_subcheck_to_check(&overall, sc_users);
148 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 114 mp_exit(overall);
149 /* increment 'users' on all lines except total user count */
150 if (input_buffer[0] != '#') {
151 users++;
152 continue;
153 }
154
155 /* get total logged in users */
156 if (sscanf (input_buffer, _("# users=%d"), &users) == 1)
157 break;
158 } 115 }
116 /* check the user count against warning and critical thresholds */
159 117
160 /* check STDERR */ 118 mp_perfdata users_pd = {
161 if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) 119 .label = "users",
162 result = possibly_set (result, STATE_UNKNOWN); 120 .value = mp_create_pd_value(user_wrapper.users),
163 (void) fclose (child_stderr); 121 };
164
165 /* close the pipe */
166 if (spclose (child_process))
167 result = possibly_set (result, STATE_UNKNOWN);
168#endif
169#ifdef HAVE_LIBSYSTEMD
170 }
171#endif
172 122
173 /* check the user count against warning and critical thresholds */ 123 users_pd = mp_pd_set_thresholds(users_pd, config.thresholds);
174 result = get_status((double)users, thlds); 124 mp_add_perfdata_to_subcheck(&sc_users, users_pd);
175 125
176 if (result == STATE_UNKNOWN) 126 int tmp_status = mp_get_pd_status(users_pd);
177 printf ("%s\n", _("Unable to read output")); 127 sc_users = mp_set_subcheck_state(sc_users, tmp_status);
178 else { 128
179 printf (_("USERS %s - %d users currently logged in |%s\n"), 129 switch (tmp_status) {
180 state_text(result), users, 130 case STATE_WARNING:
181 sperfdata_int("users", users, "", warning_range, 131 xasprintf(&sc_users.output,
182 critical_range, true, 0, false, 0)); 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);
183 } 142 }
184 143
185 return result; 144 mp_add_subcheck_to_check(&overall, sc_users);
145 mp_exit(overall);
186} 146}
187 147
148#define output_format_index CHAR_MAX + 1
149
188/* process command-line arguments */ 150/* process command-line arguments */
189int 151check_users_config_wrapper process_arguments(int argc, char **argv) {
190process_arguments (int argc, char **argv) 152 static struct option longopts[] = {{"critical", required_argument, 0, 'c'},
191{ 153 {"warning", required_argument, 0, 'w'},
192 int c; 154 {"version", no_argument, 0, 'V'},
193 int option = 0; 155 {"help", no_argument, 0, 'h'},
194 static struct option longopts[] = { 156 {"output-format", required_argument, 0, output_format_index},
195 {"critical", required_argument, 0, 'c'}, 157 {0, 0, 0, 0}};
196 {"warning", required_argument, 0, 'w'}, 158
197 {"version", no_argument, 0, 'V'}, 159 if (argc < 2) {
198 {"help", no_argument, 0, 'h'}, 160 usage(progname);
199 {0, 0, 0, 0} 161 }
200 };
201 162
202 if (argc < 2) 163 char *warning_range = NULL;
203 usage ("\n"); 164 char *critical_range = NULL;
165 check_users_config_wrapper result = {
166 .config = check_users_config_init(),
167 .errorcode = OK,
168 };
204 169
205 while (true) { 170 while (true) {
206 c = getopt_long (argc, argv, "+hVvc:w:", longopts, &option); 171 int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL);
207 172
208 if (c == -1 || c == EOF || c == 1) 173 if (counter == -1 || counter == EOF || counter == 1) {
209 break; 174 break;
175 }
210 176
211 switch (c) { 177 switch (counter) {
212 case '?': /* print short usage statement if args not parsable */ 178 case '?': /* print short usage statement if args not parsable */
213 usage5 (); 179 usage5();
214 case 'h': /* help */ 180 case 'h': /* help */
215 print_help (); 181 print_help();
216 exit (STATE_UNKNOWN); 182 exit(STATE_UNKNOWN);
217 case 'V': /* version */ 183 case 'V': /* version */
218 print_revision (progname, NP_VERSION); 184 print_revision(progname, NP_VERSION);
219 exit (STATE_UNKNOWN); 185 exit(STATE_UNKNOWN);
220 case 'c': /* critical */ 186 case 'c': /* critical */
221 critical_range = optarg; 187 critical_range = optarg;
222 break; 188 break;
223 case 'w': /* warning */ 189 case 'w': /* warning */
224 warning_range = optarg; 190 warning_range = optarg;
225 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 }
226 } 204 }
227 } 205 }
228 206
229 c = optind; 207 int option_char = optind;
230 208
231 if (warning_range == NULL && argc > c) 209 if (warning_range == NULL && argc > option_char) {
232 warning_range = argv[c++]; 210 warning_range = argv[option_char++];
211 }
233 212
234 if (critical_range == NULL && argc > c) 213 if (critical_range == NULL && argc > option_char) {
235 critical_range = argv[c++]; 214 critical_range = argv[option_char++];
215 }
236 216
237 /* this will abort in case of invalid ranges */ 217 // TODO add proper verification for ranges here!
238 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 }
239 226
240 if (!thlds->warning) { 227 if (tmp.error == MP_PARSING_SUCCES) {
241 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);
242 } 233 }
243 234
244 if (!thlds->critical) { 235 if (critical_range) {
245 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);
246 } 241 }
247 242
248 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;
249} 252}
250 253
251void 254void print_help(void) {
252print_help (void) 255 print_revision(progname, NP_VERSION);
253{
254 print_revision (progname, NP_VERSION);
255 256
256 printf ("Copyright (c) 1999 Ethan Galstad\n"); 257 printf("Copyright (c) 1999 Ethan Galstad\n");
257 printf (COPYRIGHT, copyright, email); 258 printf(COPYRIGHT, copyright, email);
258 259
259 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"));
260 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."));
261 263
262 printf ("\n\n"); 264 printf("\n\n");
263 265
264 print_usage (); 266 print_usage();
265 267
266 printf (UT_HELP_VRSN); 268 printf(UT_HELP_VRSN);
267 printf (UT_EXTRA_OPTS); 269 printf(UT_EXTRA_OPTS);
268 270
269 printf (" %s\n", "-w, --warning=RANGE_EXPRESSION"); 271 printf(" %s\n", "-w, --warning=RANGE_EXPRESSION");
270 printf (" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); 272 printf(" %s\n",
271 printf (" %s\n", "-c, --critical=RANGE_EXPRESSION"); 273 _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION"));
272 printf (" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); 274 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION");
275 printf(" %s\n",
276 _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION"));
277 printf(UT_OUTPUT_FORMAT);
273 278
274 printf (UT_SUPPORT); 279 printf(UT_SUPPORT);
275} 280}
276 281
277void 282void print_usage(void) {
278print_usage (void) 283 printf("%s\n", _("Usage:"));
279{ 284 printf("%s -w <users> -c <users>\n", progname);
280 printf ("%s\n", _("Usage:"));
281 printf ("%s -w <users> -c <users>\n", progname);
282} 285}
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 833479ce..ef888d08 100644
--- a/plugins/common.h
+++ b/plugins/common.h
@@ -1,126 +1,122 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins common include file 3 * Monitoring Plugins common include file
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2003-2007 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2007 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains common include files and defines used in many of 11 * This file contains common include files and defines used in many of
12* the plugins. 12 * the plugins.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31#ifndef _COMMON_H_ 31#ifndef _COMMON_H_
32#define _COMMON_H_ 32#define _COMMON_H_
33 33
34#include "config.h" 34#include "../config.h"
35#include "../lib/monitoringplug.h"
35 36
36#ifdef HAVE_FEATURES_H 37#ifdef HAVE_FEATURES_H
37#include <features.h> 38# include <features.h>
38#endif 39#endif
39 40
40#include <stdio.h> /* obligatory includes */ 41#include <stdio.h> /* obligatory includes */
41#include <stdlib.h> 42#include <stdlib.h>
42#include <errno.h> 43#include <errno.h>
43 44
44/* This block provides uintmax_t - should be reported to coreutils that this should be added to fsuage.h */ 45/* This block provides uintmax_t - should be reported to coreutils that this should be added to
46 * fsuage.h */
45#if HAVE_INTTYPES_H 47#if HAVE_INTTYPES_H
46# include <inttypes.h> 48# include <inttypes.h>
47#endif 49#endif
48#if HAVE_STDINT_H 50#if HAVE_STDINT_H
49# include <stdint.h> 51# include <stdint.h>
50#endif 52#endif
51#include <unistd.h> 53#include <unistd.h>
52#ifndef UINTMAX_MAX 54#ifndef UINTMAX_MAX
53# define UINTMAX_MAX ((uintmax_t) -1) 55# define UINTMAX_MAX ((uintmax_t) - 1)
54#endif 56#endif
55 57
56#include <limits.h> /* This is assumed true, because coreutils assume it too */ 58#include <limits.h> /* This is assumed true, because coreutils assume it too */
57 59
58#ifdef HAVE_MATH_H 60#ifdef HAVE_MATH_H
59#include <math.h> 61# include <math.h>
60#endif 62#endif
61 63
62#ifdef _AIX 64#ifdef _AIX
63#ifdef HAVE_MP_H 65# ifdef HAVE_MP_H
64#include <mp.h> 66# include <mp.h>
65#endif 67# endif
66#endif 68#endif
67 69
68#ifdef HAVE_STRINGS_H 70#ifdef HAVE_STRINGS_H
69#include <strings.h> 71# include <strings.h>
70#endif 72#endif
71#ifdef HAVE_STRING_H 73#ifdef HAVE_STRING_H
72#include <string.h> 74# include <string.h>
73#endif 75#endif
74 76
75#ifdef HAVE_UNISTD_H 77#ifdef HAVE_UNISTD_H
76#include <unistd.h> 78# include <unistd.h>
77#endif 79#endif
78 80
79/* GET_NUMBER_OF_CPUS is a macro to return 81/* GET_NUMBER_OF_CPUS is a macro to return
80 number of CPUs, if we can get that data. 82 number of CPUs, if we can get that data.
81 Use configure.in to test for various OS ways of 83 Use configure.in to test for various OS ways of
82 getting that data 84 getting that data
83 Will return -1 if cannot get data 85 Will return -1 if cannot get data
84*/ 86*/
85#if defined(HAVE_SYSCONF__SC_NPROCESSORS_ONLN) 87#if defined(HAVE_SYSCONF__SC_NPROCESSORS_ONLN)
86# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_ONLN) 88# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_ONLN)
87#elif defined (HAVE_SYSCONF__SC_NPROCESSORS_CONF) 89#elif defined(HAVE_SYSCONF__SC_NPROCESSORS_CONF)
88# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_CONF) 90# define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_CONF)
89#else 91#else
90# define GET_NUMBER_OF_CPUS() -1 92# define GET_NUMBER_OF_CPUS() -1
91#endif 93#endif
92 94
93#ifdef TIME_WITH_SYS_TIME 95#ifdef HAVE_SYS_TIME_H
94# include <sys/time.h> 96# include <sys/time.h>
95# include <time.h>
96#else
97# ifdef HAVE_SYS_TIME_H
98# include <sys/time.h>
99# else
100# include <time.h>
101# endif
102#endif 97#endif
98#include <time.h>
103 99
104#ifdef HAVE_SYS_TYPES_H 100#ifdef HAVE_SYS_TYPES_H
105#include <sys/types.h> 101# include <sys/types.h>
106#endif 102#endif
107 103
108#ifdef HAVE_SYS_SOCKET_H 104#ifdef HAVE_SYS_SOCKET_H
109#include <sys/socket.h> 105# include <sys/socket.h>
110#endif 106#endif
111 107
112#ifdef HAVE_SIGNAL_H 108#ifdef HAVE_SIGNAL_H
113#include <signal.h> 109# include <signal.h>
114#endif 110#endif
115 111
116/* GNU Libraries */ 112/* GNU Libraries */
117#include <getopt.h> 113#include <getopt.h>
118#include "dirname.h" 114#include "../gl/dirname.h"
119 115
120#include <locale.h> 116#include <locale.h>
121 117
122#ifdef HAVE_SYS_POLL_H 118#ifdef HAVE_SYS_POLL_H
123# include "sys/poll.h" 119# include "sys/poll.h"
124#endif 120#endif
125 121
126/* 122/*
@@ -130,42 +126,42 @@
130 */ 126 */
131 127
132#ifndef HAVE_STRTOL 128#ifndef HAVE_STRTOL
133# define strtol(a,b,c) atol((a)) 129# define strtol(a, b, c) atol((a))
134#endif 130#endif
135 131
136#ifndef HAVE_STRTOUL 132#ifndef HAVE_STRTOUL
137# define strtoul(a,b,c) (unsigned long)atol((a)) 133# define strtoul(a, b, c) (unsigned long)atol((a))
138#endif 134#endif
139 135
140/* SSL implementations */ 136/* SSL implementations */
141#ifdef HAVE_GNUTLS_OPENSSL_H 137#ifdef HAVE_GNUTLS_OPENSSL_H
142# include <gnutls/openssl.h> 138# include <gnutls/openssl.h>
143#else 139#else
144# define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */ 140# define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */
145# ifdef HAVE_SSL_H 141# ifdef HAVE_SSL_H
146# include <rsa.h> 142# include <rsa.h>
147# include <crypto.h> 143# include <crypto.h>
148# include <x509.h> 144# include <x509.h>
149# include <pem.h> 145# include <pem.h>
150# include <ssl.h> 146# include <ssl.h>
151# include <err.h> 147# include <err.h>
152# else 148# else
153# ifdef HAVE_OPENSSL_SSL_H 149# ifdef HAVE_OPENSSL_SSL_H
154# include <openssl/rsa.h> 150# include <openssl/rsa.h>
155# include <openssl/crypto.h> 151# include <openssl/crypto.h>
156# include <openssl/x509.h> 152# include <openssl/x509.h>
157# include <openssl/pem.h> 153# include <openssl/pem.h>
158# include <openssl/ssl.h> 154# include <openssl/ssl.h>
159# include <openssl/err.h> 155# include <openssl/err.h>
160# endif 156# endif
161# endif 157# endif
162#endif 158#endif
163 159
164/* openssl 1.1 does not set OPENSSL_NO_SSL2 by default but ships without ssl2 */ 160/* openssl 1.1 does not set OPENSSL_NO_SSL2 by default but ships without ssl2 */
165#ifdef OPENSSL_VERSION_NUMBER 161#ifdef OPENSSL_VERSION_NUMBER
166# if OPENSSL_VERSION_NUMBER >= 0x10100000 162# if OPENSSL_VERSION_NUMBER >= 0x10100000
167# define OPENSSL_NO_SSL2 163# define OPENSSL_NO_SSL2
168# endif 164# endif
169#endif 165#endif
170 166
171/* 167/*
@@ -176,7 +172,7 @@
176 172
177/* MariaDB 10.2 client does not set MYSQL_PORT */ 173/* MariaDB 10.2 client does not set MYSQL_PORT */
178#ifndef MYSQL_PORT 174#ifndef MYSQL_PORT
179# define MYSQL_PORT 3306 175# define MYSQL_PORT 3306
180#endif 176#endif
181 177
182enum { 178enum {
@@ -185,17 +181,9 @@ enum {
185}; 181};
186 182
187enum { 183enum {
188 STATE_OK, 184 DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
189 STATE_WARNING, 185 MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */
190 STATE_CRITICAL, 186 MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */
191 STATE_UNKNOWN,
192 STATE_DEPENDENT
193};
194
195enum {
196 DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
197 MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */
198 MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */
199}; 187};
200 188
201/* 189/*
@@ -203,18 +191,18 @@ enum {
203 * Internationalization 191 * Internationalization
204 * 192 *
205 */ 193 */
206#include "gettext.h" 194#include "../gl/gettext.h"
207#define _(String) gettext (String) 195#define _(String) gettext(String)
208#if ! ENABLE_NLS 196#if !ENABLE_NLS
209# undef textdomain 197# undef textdomain
210# define textdomain(Domainname) /* empty */ 198# define textdomain(Domainname) /* empty */
211# undef bindtextdomain 199# undef bindtextdomain
212# define bindtextdomain(Domainname, Dirname) /* empty */ 200# define bindtextdomain(Domainname, Dirname) /* empty */
213#endif 201#endif
214 202
215/* For non-GNU compilers to ignore __attribute__ */ 203/* For non-GNU compilers to ignore __attribute__ */
216#ifndef __GNUC__ 204#ifndef __GNUC__
217# define __attribute__(x) /* do nothing */ 205# define __attribute__(x) /* do nothing */
218#endif 206#endif
219 207
220#endif /* _COMMON_H_ */ 208#endif /* _COMMON_H_ */
diff --git a/plugins/negate.c b/plugins/negate.c
index c5fe7e13..a42a6c59 100644
--- a/plugins/negate.c
+++ b/plugins/negate.c
@@ -1,36 +1,36 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring negate plugin 3 * Monitoring negate plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2002-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the negate plugin 10 * This file contains the negate plugin
11* 11 *
12* Negates the status of a plugin (returns OK for CRITICAL, and vice-versa). 12 * Negates the status of a plugin (returns OK for CRITICAL, and vice-versa).
13* Can also perform custom state switching. 13 * Can also perform custom state switching.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "negate"; 32const char *progname = "negate";
33const char *copyright = "2002-2008"; 33const char *copyright = "2002-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#define DEFAULT_TIMEOUT 11 36#define DEFAULT_TIMEOUT 11
@@ -38,203 +38,212 @@ const char *email = "devel@monitoring-plugins.org";
38#include "common.h" 38#include "common.h"
39#include "utils.h" 39#include "utils.h"
40#include "utils_cmd.h" 40#include "utils_cmd.h"
41#include "negate.d/config.h"
42#include "../lib/states.h"
41 43
42#include <ctype.h> 44typedef struct {
45 int errorcode;
46 negate_config config;
47} negate_config_wrapper;
48static negate_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
49static negate_config_wrapper validate_arguments(negate_config_wrapper /*config_wrapper*/);
43 50
44/* char *command_line; */ 51static void print_help(void);
52void print_usage(void);
45 53
46static const char **process_arguments (int, char **); 54int main(int argc, char **argv) {
47void validate_arguments (char **); 55 setlocale(LC_ALL, "");
48void print_help (void); 56 bindtextdomain(PACKAGE, LOCALEDIR);
49void print_usage (void); 57 textdomain(PACKAGE);
50bool subst_text = false;
51 58
52static int state[4] = { 59 timeout_interval = DEFAULT_TIMEOUT;
53 STATE_OK,
54 STATE_WARNING,
55 STATE_CRITICAL,
56 STATE_UNKNOWN,
57};
58 60
59int 61 negate_config_wrapper tmp_config = process_arguments(argc, argv);
60main (int argc, char **argv)
61{
62 int result = STATE_UNKNOWN;
63 char *sub;
64 char **command_line;
65 output chld_out, chld_err;
66 62
67 setlocale (LC_ALL, ""); 63 if (tmp_config.errorcode == ERROR) {
68 bindtextdomain (PACKAGE, LOCALEDIR); 64 die(STATE_UNKNOWN, _("negate: Failed to parse input"));
69 textdomain (PACKAGE); 65 }
70 66
71 timeout_interval = DEFAULT_TIMEOUT; 67 negate_config config = tmp_config.config;
72 68
73 command_line = (char **) process_arguments (argc, argv); 69 char **command_line = config.command_line;
74 70
75 /* Set signal handling and alarm */ 71 /* Set signal handling and alarm */
76 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) 72 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
77 die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); 73 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
74 }
75
76 (void)alarm(timeout_interval);
78 77
79 (void) alarm ((unsigned) timeout_interval); 78 mp_state_enum result = STATE_UNKNOWN;
79 output chld_out;
80 output chld_err;
80 81
81 /* catch when the command is quoted */ 82 /* catch when the command is quoted */
82 if(command_line[1] == NULL) { 83 if (command_line[1] == NULL) {
83 result = cmd_run (command_line[0], &chld_out, &chld_err, 0); 84 result = cmd_run(command_line[0], &chld_out, &chld_err, 0);
84 } else { 85 } else {
85 result = cmd_run_array (command_line, &chld_out, &chld_err, 0); 86 result = cmd_run_array(command_line, &chld_out, &chld_err, 0);
86 } 87 }
87 if (chld_err.lines > 0) { 88 if (chld_err.lines > 0) {
88 for (size_t i = 0; i < chld_err.lines; i++) { 89 for (size_t i = 0; i < chld_err.lines; i++) {
89 fprintf (stderr, "%s\n", chld_err.line[i]); 90 fprintf(stderr, "%s\n", chld_err.line[i]);
90 } 91 }
91 } 92 }
92 93
93 /* Return UNKNOWN or worse if no output is returned */ 94 /* Return UNKNOWN or worse if no output is returned */
94 if (chld_out.lines == 0) 95 if (chld_out.lines == 0) {
95 die (max_state_alt (result, STATE_UNKNOWN), _("No data returned from command\n")); 96 die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n"));
97 }
96 98
99 char *sub;
97 for (size_t i = 0; i < chld_out.lines; i++) { 100 for (size_t i = 0; i < chld_out.lines; i++) {
98 if (subst_text && result >= 0 && result <= 4 && result != state[result]) { 101 if (config.subst_text && result >= 0 && result <= 4 && result != config.state[result]) {
99 /* Loop over each match found */ 102 /* Loop over each match found */
100 while ((sub = strstr (chld_out.line[i], state_text (result)))) { 103 while ((sub = strstr(chld_out.line[i], state_text(result)))) {
101 /* Terminate the first part and skip over the string we'll substitute */ 104 /* Terminate the first part and skip over the string we'll substitute */
102 *sub = '\0'; 105 *sub = '\0';
103 sub += strlen (state_text (result)); 106 sub += strlen(state_text(result));
104 /* then put everything back together */ 107 /* then put everything back together */
105 xasprintf (&chld_out.line[i], "%s%s%s", chld_out.line[i], state_text (state[result]), sub); 108 xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i],
109 state_text(config.state[result]), sub);
106 } 110 }
107 } 111 }
108 printf ("%s\n", chld_out.line[i]); 112 printf("%s\n", chld_out.line[i]);
109 } 113 }
110 114
111 if (result >= 0 && result <= 4) { 115 if (result >= 0 && result <= 4) {
112 exit (state[result]); 116 exit(config.state[result]);
113 } else { 117 } else {
114 exit (result); 118 exit(result);
115 } 119 }
116} 120}
117 121
118
119/* process command-line arguments */ 122/* process command-line arguments */
120static const char ** 123static negate_config_wrapper process_arguments(int argc, char **argv) {
121process_arguments (int argc, char **argv)
122{
123 int c;
124 bool permute = true;
125
126 int option = 0;
127 static struct option longopts[] = { 124 static struct option longopts[] = {
128 {"help", no_argument, 0, 'h'}, 125 {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'},
129 {"version", no_argument, 0, 'V'}, 126 {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'},
130 {"timeout", required_argument, 0, 't'}, 127 {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'},
131 {"timeout-result", required_argument, 0, 'T'}, 128 {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'},
132 {"ok", required_argument, 0, 'o'}, 129 {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}};
133 {"warning", required_argument, 0, 'w'}, 130
134 {"critical", required_argument, 0, 'c'}, 131 negate_config_wrapper result = {
135 {"unknown", required_argument, 0, 'u'}, 132 .errorcode = OK,
136 {"substitute", no_argument, 0, 's'}, 133 .config = negate_config_init(),
137 {0, 0, 0, 0}
138 }; 134 };
135 bool permute = true;
136 while (true) {
137 int option = 0;
138 int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option);
139 139
140 while (1) { 140 if (option_char == -1 || option_char == EOF) {
141 c = getopt_long (argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option);
142
143 if (c == -1 || c == EOF)
144 break; 141 break;
142 }
145 143
146 switch (c) { 144 switch (option_char) {
147 case '?': /* help */ 145 case '?': /* help */
148 usage5 (); 146 usage5();
149 break; 147 break;
150 case 'h': /* help */ 148 case 'h': /* help */
151 print_help (); 149 print_help();
152 exit (EXIT_SUCCESS); 150 exit(STATE_UNKNOWN);
153 break; 151 break;
154 case 'V': /* version */ 152 case 'V': /* version */
155 print_revision (progname, NP_VERSION); 153 print_revision(progname, NP_VERSION);
156 exit (EXIT_SUCCESS); 154 exit(STATE_UNKNOWN);
157 case 't': /* timeout period */ 155 case 't': /* timeout period */
158 if (!is_integer (optarg)) 156 if (!is_integer(optarg)) {
159 usage2 (_("Timeout interval must be a positive integer"), optarg); 157 usage2(_("Timeout interval must be a positive integer"), optarg);
160 else 158 } else {
161 timeout_interval = atoi (optarg); 159 timeout_interval = atoi(optarg);
160 }
162 break; 161 break;
163 case 'T': /* Result to return on timeouts */ 162 case 'T': /* Result to return on timeouts */
164 if ((timeout_state = mp_translate_state(optarg)) == ERROR) 163 if ((timeout_state = mp_translate_state(optarg)) == ERROR) {
165 usage4 (_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 164 usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, "
165 "UNKNOWN) or integer (0-3)."));
166 }
166 break; 167 break;
167 case 'o': /* replacement for OK */ 168 case 'o': /* replacement for OK */
168 if ((state[STATE_OK] = mp_translate_state(optarg)) == ERROR) 169 if ((result.config.state[STATE_OK] = mp_translate_state(optarg)) == ERROR) {
169 usage4 (_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 170 usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
171 "integer (0-3)."));
172 }
170 permute = false; 173 permute = false;
171 break; 174 break;
172 175
173 case 'w': /* replacement for WARNING */ 176 case 'w': /* replacement for WARNING */
174 if ((state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) 177 if ((result.config.state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) {
175 usage4 (_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 178 usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
179 "integer (0-3)."));
180 }
176 permute = false; 181 permute = false;
177 break; 182 break;
178 case 'c': /* replacement for CRITICAL */ 183 case 'c': /* replacement for CRITICAL */
179 if ((state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) 184 if ((result.config.state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) {
180 usage4 (_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 185 usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
186 "integer (0-3)."));
187 }
181 permute = false; 188 permute = false;
182 break; 189 break;
183 case 'u': /* replacement for UNKNOWN */ 190 case 'u': /* replacement for UNKNOWN */
184 if ((state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) 191 if ((result.config.state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) {
185 usage4 (_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 192 usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
193 "integer (0-3)."));
194 }
186 permute = false; 195 permute = false;
187 break; 196 break;
188 case 's': /* Substitute status text */ 197 case 's': /* Substitute status text */
189 subst_text = true; 198 result.config.subst_text = true;
190 break; 199 break;
191 } 200 }
192 } 201 }
193 202
194 validate_arguments (&argv[optind]);
195
196 if (permute) { /* No [owcu] switch specified, default to this */ 203 if (permute) { /* No [owcu] switch specified, default to this */
197 state[STATE_OK] = STATE_CRITICAL; 204 result.config.state[STATE_OK] = STATE_CRITICAL;
198 state[STATE_CRITICAL] = STATE_OK; 205 result.config.state[STATE_CRITICAL] = STATE_OK;
199 } 206 }
200 207
201 return (const char **) &argv[optind]; 208 result.config.command_line = &argv[optind];
209
210 return validate_arguments(result);
202} 211}
203 212
213negate_config_wrapper validate_arguments(negate_config_wrapper config_wrapper) {
214 if (config_wrapper.config.command_line[0] == NULL) {
215 usage4(_("Could not parse arguments"));
216 }
204 217
205void 218 if (strncmp(config_wrapper.config.command_line[0], "/", 1) != 0 &&
206validate_arguments (char **command_line) 219 strncmp(config_wrapper.config.command_line[0], "./", 2) != 0) {
207{ 220 usage4(_("Require path to command"));
208 if (command_line[0] == NULL) 221 }
209 usage4 (_("Could not parse arguments"));
210 222
211 if (strncmp(command_line[0],"/",1) != 0 && strncmp(command_line[0],"./",2) != 0) 223 return config_wrapper;
212 usage4 (_("Require path to command"));
213} 224}
214 225
226void print_help(void) {
227 print_revision(progname, NP_VERSION);
215 228
216void 229 printf(COPYRIGHT, copyright, email);
217print_help (void)
218{
219 print_revision (progname, NP_VERSION);
220 230
221 printf (COPYRIGHT, copyright, email); 231 printf("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and "
232 "vice-versa) by default."));
233 printf("%s\n", _("Additional switches can be used to control:\n"));
234 printf("\t - which state becomes what\n");
235 printf("\t - changing the plugin output text to match the return code");
222 236
223 printf ("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and vice-versa) by default.")); 237 printf("\n\n");
224 printf ("%s\n", _("Additional switches can be used to control:\n"));
225 printf ("\t - which state becomes what\n");
226 printf ("\t - changing the plugin output text to match the return code");
227 238
228 printf ("\n\n"); 239 print_usage();
229 240
230 print_usage (); 241 printf(UT_HELP_VRSN);
231 242
232 printf (UT_HELP_VRSN); 243 printf(UT_PLUG_TIMEOUT, timeout_interval);
233 244 printf(" %s\n", _("Keep timeout longer than the plugin timeout to retain CRITICAL status."));
234 printf (UT_PLUG_TIMEOUT, timeout_interval); 245 printf(" -T, --timeout-result=STATUS\n");
235 printf (" %s\n", _("Keep timeout longer than the plugin timeout to retain CRITICAL status.")); 246 printf(" %s\n", _("Custom result on Negate timeouts; see below for STATUS definition\n"));
236 printf (" -T, --timeout-result=STATUS\n");
237 printf (" %s\n", _("Custom result on Negate timeouts; see below for STATUS definition\n"));
238 247
239 printf(" -o, --ok=STATUS\n"); 248 printf(" -o, --ok=STATUS\n");
240 printf(" -w, --warning=STATUS\n"); 249 printf(" -w, --warning=STATUS\n");
@@ -246,31 +255,30 @@ print_help (void)
246 printf(" -s, --substitute\n"); 255 printf(" -s, --substitute\n");
247 printf(_(" Substitute output text as well. Will only substitute text in CAPITALS\n")); 256 printf(_(" Substitute output text as well. Will only substitute text in CAPITALS\n"));
248 257
249 printf ("\n"); 258 printf("\n");
250 printf ("%s\n", _("Examples:")); 259 printf("%s\n", _("Examples:"));
251 printf (" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host"); 260 printf(" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host");
252 printf (" %s\n", _("Run check_ping and invert result. Must use full path to plugin")); 261 printf(" %s\n", _("Run check_ping and invert result. Must use full path to plugin"));
253 printf (" %s\n", "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'"); 262 printf(" %s\n",
254 printf (" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL")); 263 "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'");
255 printf ("\n"); 264 printf(" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL"));
256 printf ("%s\n", _("Notes:")); 265 printf("\n");
257 printf (" %s\n", _("This plugin is a wrapper to take the output of another plugin and invert it.")); 266 printf("%s\n", _("Notes:"));
258 printf (" %s\n", _("The full path of the plugin must be provided.")); 267 printf(" %s\n",
259 printf (" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL.")); 268 _("This plugin is a wrapper to take the output of another plugin and invert it."));
260 printf (" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK.")); 269 printf(" %s\n", _("The full path of the plugin must be provided."));
261 printf (" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged.")); 270 printf(" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL."));
262 printf ("\n"); 271 printf(" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK."));
263 printf (" %s\n", _("Using timeout-result, it is possible to override the timeout behaviour or a")); 272 printf(" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged."));
264 printf (" %s\n", _("plugin by setting the negate timeout a bit lower.")); 273 printf("\n");
265 274 printf(" %s\n",
266 printf (UT_SUPPORT); 275 _("Using timeout-result, it is possible to override the timeout behaviour or a"));
276 printf(" %s\n", _("plugin by setting the negate timeout a bit lower."));
277
278 printf(UT_SUPPORT);
267} 279}
268 280
269 281void print_usage(void) {
270 282 printf("%s\n", _("Usage:"));
271void 283 printf("%s [-t timeout] [-Towcu STATE] [-s] <definition of wrapped plugin>\n", progname);
272print_usage (void)
273{
274 printf ("%s\n", _("Usage:"));
275 printf ("%s [-t timeout] [-Towcu STATE] [-s] <definition of wrapped plugin>\n", progname);
276} 284}
diff --git a/plugins/negate.d/config.h b/plugins/negate.d/config.h
new file mode 100644
index 00000000..0cf30cd4
--- /dev/null
+++ b/plugins/negate.d/config.h
@@ -0,0 +1,24 @@
1#pragma once
2
3#include "states.h"
4
5typedef struct {
6 mp_state_enum state[4];
7 bool subst_text;
8 char **command_line;
9} negate_config;
10
11negate_config negate_config_init() {
12 negate_config tmp = {
13 .state =
14 {
15 STATE_OK,
16 STATE_WARNING,
17 STATE_CRITICAL,
18 STATE_UNKNOWN,
19 },
20 .subst_text = false,
21 .command_line = NULL,
22 };
23 return tmp;
24}
diff --git a/plugins/netutils.c b/plugins/netutils.c
index c6af248e..b4c6ff0a 100644
--- a/plugins/netutils.c
+++ b/plugins/netutils.c
@@ -1,40 +1,43 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins network utilities 3 * Monitoring Plugins network utilities
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2003-2008 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains commons functions used in many of the plugins. 11 * This file contains commons functions used in many of the plugins.
12* 12 *
13* 13 *
14* This program is free software: you can redistribute it and/or modify 14 * This program is free software: you can redistribute it and/or modify
15* it under the terms of the GNU General Public License as published by 15 * it under the terms of the GNU General Public License as published by
16* the Free Software Foundation, either version 3 of the License, or 16 * the Free Software Foundation, either version 3 of the License, or
17* (at your option) any later version. 17 * (at your option) any later version.
18* 18 *
19* This program is distributed in the hope that it will be useful, 19 * This program is distributed in the hope that it will be useful,
20* but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22* GNU General Public License for more details. 22 * GNU General Public License for more details.
23* 23 *
24* You should have received a copy of the GNU General Public License 24 * You should have received a copy of the GNU General Public License
25* along with this program. If not, see <http://www.gnu.org/licenses/>. 25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26* 26 *
27* 27 *
28*****************************************************************************/ 28 *****************************************************************************/
29 29
30#include "common.h" 30#include "common.h"
31#include "output.h"
32#include "states.h"
33#include <sys/types.h>
31#include "netutils.h" 34#include "netutils.h"
32 35
33unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT; 36unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT;
34unsigned int socket_timeout_state = STATE_CRITICAL; 37mp_state_enum socket_timeout_state = STATE_CRITICAL;
35 38mp_state_enum econn_refuse_state = STATE_CRITICAL;
36int econn_refuse_state = STATE_CRITICAL;
37bool was_refused = false; 39bool was_refused = false;
40
38#if USE_IPV6 41#if USE_IPV6
39int address_family = AF_UNSPEC; 42int address_family = AF_UNSPEC;
40#else 43#else
@@ -42,177 +45,177 @@ int address_family = AF_INET;
42#endif 45#endif
43 46
44/* handles socket timeouts */ 47/* handles socket timeouts */
45void 48void socket_timeout_alarm_handler(int sig) {
46socket_timeout_alarm_handler (int sig) 49 mp_subcheck timeout_sc = mp_subcheck_init();
47{ 50 timeout_sc = mp_set_subcheck_state(timeout_sc, socket_timeout_state);
48 if (sig == SIGALRM) 51
49 printf (_("%s - Socket timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); 52 if (sig == SIGALRM) {
50 else 53 xasprintf(&timeout_sc.output, _("Socket timeout after %d seconds\n"), socket_timeout);
51 printf (_("%s - Abnormal timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); 54 } else {
52 55 xasprintf(&timeout_sc.output, _("Abnormal timeout after %d seconds\n"), socket_timeout);
53 exit (socket_timeout_state); 56 }
54} 57
58 mp_check overall = mp_check_init();
59 mp_add_subcheck_to_check(&overall, timeout_sc);
55 60
61 mp_exit(overall);
62}
56 63
57/* 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
58 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
59 multi-packet answer */ 66 multi-packet answer */
60int 67mp_state_enum process_tcp_request2(const char *server_address, const int server_port,
61process_tcp_request2 (const char *server_address, int server_port, 68 const char *send_buffer, char *recv_buffer,
62 const char *send_buffer, char *recv_buffer, int recv_size) 69 const int recv_size) {
63{
64 70
65 int result; 71 int socket;
66 int send_result;
67 int recv_result;
68 int sd;
69 struct timeval tv;
70 fd_set readfds;
71 int recv_length = 0;
72 72
73 result = np_net_connect (server_address, server_port, &sd, IPPROTO_TCP); 73 mp_state_enum connect_result =
74 if (result != STATE_OK) 74 np_net_connect(server_address, server_port, &socket, IPPROTO_TCP);
75 if (connect_result != STATE_OK) {
75 return STATE_CRITICAL; 76 return STATE_CRITICAL;
77 }
76 78
77 send_result = send (sd, send_buffer, strlen (send_buffer), 0); 79 mp_state_enum result;
78 if (send_result<0 || (size_t)send_result!=strlen(send_buffer)) { 80 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
79 printf ("%s\n", _("Send failed")); 81 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
82 // printf("%s\n", _("Send failed"));
80 result = STATE_WARNING; 83 result = STATE_WARNING;
81 } 84 }
82 85
83 while (1) { 86 fd_set readfds;
87 ssize_t recv_length = 0;
88 while (true) {
84 /* wait up to the number of seconds for socket timeout 89 /* wait up to the number of seconds for socket timeout
85 minus one for data from the host */ 90 minus one for data from the host */
86 tv.tv_sec = socket_timeout - 1; 91 struct timeval timeout = {
87 tv.tv_usec = 0; 92 .tv_sec = socket_timeout - 1,
88 FD_ZERO (&readfds); 93 .tv_usec = 0,
89 FD_SET (sd, &readfds); 94 };
90 select (sd + 1, &readfds, NULL, NULL, &tv); 95 FD_ZERO(&readfds);
96 FD_SET(socket, &readfds);
97 select(socket + 1, &readfds, NULL, NULL, &timeout);
91 98
92 /* make sure some data has arrived */ 99 /* make sure some data has arrived */
93 if (!FD_ISSET (sd, &readfds)) { /* it hasn't */ 100 if (!FD_ISSET(socket, &readfds)) { /* it hasn't */
94 if (!recv_length) { 101 if (!recv_length) {
95 strcpy (recv_buffer, ""); 102 strcpy(recv_buffer, "");
96 printf ("%s\n", _("No data was received from host!")); 103 // printf("%s\n", _("No data was received from host!"));
97 result = STATE_WARNING; 104 result = STATE_WARNING;
98 } 105 } else { /* this one failed, but previous ones worked */
99 else { /* this one failed, but previous ones worked */
100 recv_buffer[recv_length] = 0; 106 recv_buffer[recv_length] = 0;
101 } 107 }
102 break; 108 break;
109 } /* it has */
110
111 ssize_t recv_result =
112 recv(socket, recv_buffer + recv_length, (size_t)(recv_size - recv_length - 1), 0);
113 if (recv_result == -1) {
114 /* recv failed, bail out */
115 strcpy(recv_buffer + recv_length, "");
116 result = STATE_WARNING;
117 break;
103 } 118 }
104 else { /* it has */ 119
105 recv_result = 120 if (recv_result == 0) {
106 recv (sd, recv_buffer + recv_length, 121 /* end of file ? */
107 (size_t)recv_size - recv_length - 1, 0); 122 recv_buffer[recv_length] = 0;
108 if (recv_result == -1) { 123 break;
109 /* recv failed, bail out */ 124 }
110 strcpy (recv_buffer + recv_length, ""); 125
111 result = STATE_WARNING; 126 /* we got data! */
112 break; 127 recv_length += recv_result;
113 } 128 if (recv_length >= recv_size - 1) {
114 else if (recv_result == 0) { 129 /* buffer full, we're done */
115 /* end of file ? */ 130 recv_buffer[recv_size - 1] = 0;
116 recv_buffer[recv_length] = 0; 131 break;
117 break;
118 }
119 else { /* we got data! */
120 recv_length += recv_result;
121 if (recv_length >= recv_size - 1) {
122 /* buffer full, we're done */
123 recv_buffer[recv_size - 1] = 0;
124 break;
125 }
126 }
127 } 132 }
128 /* end if(!FD_ISSET(sd,&readfds)) */ 133 /* end if(!FD_ISSET(sd,&readfds)) */
129 } 134 }
130 /* end while(1) */
131 135
132 close (sd); 136 close(socket);
133 return result; 137 return result;
134} 138}
135 139
136
137/* 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
138 response */ 141 response */
139int 142mp_state_enum process_request(const char *server_address, const int server_port, const int proto,
140process_request (const char *server_address, int server_port, int proto, 143 const char *send_buffer, char *recv_buffer, const int recv_size) {
141 const char *send_buffer, char *recv_buffer, int recv_size)
142{
143 int result;
144 int sd;
145
146 result = STATE_OK;
147 144
148 result = np_net_connect (server_address, server_port, &sd, proto); 145 mp_state_enum result = STATE_OK;
149 if (result != STATE_OK) 146 int socket;
147 result = np_net_connect(server_address, server_port, &socket, proto);
148 if (result != STATE_OK) {
150 return STATE_CRITICAL; 149 return STATE_CRITICAL;
150 }
151 151
152 result = send_request (sd, proto, send_buffer, recv_buffer, recv_size); 152 result = send_request(socket, proto, send_buffer, recv_buffer, recv_size);
153 153
154 close (sd); 154 close(socket);
155 155
156 return result; 156 return result;
157} 157}
158 158
159
160/* 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 */
161int 160mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor,
162np_net_connect (const char *host_name, int port, int *sd, int proto) 161 const int proto) {
163{ 162 /* send back STATE_UNKOWN if there's an error
164 /* send back STATE_UNKOWN if there's an error 163 send back STATE_OK if we connect
165 send back STATE_OK if we connect 164 send back STATE_CRITICAL if we can't connect.
166 send back STATE_CRITICAL if we can't connect. 165 Let upstream figure out what to send to the user. */
167 Let upstream figure out what to send to the user. */ 166 bool is_socket = (host_name[0] == '/');
168 struct addrinfo hints; 167 int socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
169 struct addrinfo *r, *res; 168
170 struct sockaddr_un su; 169 struct addrinfo hints = {};
171 char port_str[6], host[MAX_HOST_ADDRESS_LENGTH]; 170 struct addrinfo *res = NULL;
172 size_t len; 171 int result;
173 int socktype, result;
174 short is_socket = (host_name[0] == '/');
175
176 socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
177
178 /* as long as it doesn't start with a '/', it's assumed a host or ip */ 172 /* as long as it doesn't start with a '/', it's assumed a host or ip */
179 if (!is_socket){ 173 if (!is_socket) {
180 memset (&hints, 0, sizeof (hints)); 174 memset(&hints, 0, sizeof(hints));
181 hints.ai_family = address_family; 175 hints.ai_family = address_family;
182 hints.ai_protocol = proto; 176 hints.ai_protocol = proto;
183 hints.ai_socktype = socktype; 177 hints.ai_socktype = socktype;
184 178
185 len = strlen (host_name); 179 size_t len = strlen(host_name);
186 /* check for an [IPv6] address (and strip the brackets) */ 180 /* check for an [IPv6] address (and strip the brackets) */
187 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') { 181 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') {
188 host_name++; 182 host_name++;
189 len -= 2; 183 len -= 2;
190 } 184 }
191 if (len >= sizeof(host)) 185
186 char host[MAX_HOST_ADDRESS_LENGTH];
187
188 if (len >= sizeof(host)) {
192 return STATE_UNKNOWN; 189 return STATE_UNKNOWN;
193 memcpy (host, host_name, len); 190 }
191
192 memcpy(host, host_name, len);
194 host[len] = '\0'; 193 host[len] = '\0';
195 snprintf (port_str, sizeof (port_str), "%d", port);
196 result = getaddrinfo (host, port_str, &hints, &res);
197 194
198 if (result != 0) { 195 char port_str[6];
199 printf ("%s\n", gai_strerror (result)); 196 snprintf(port_str, sizeof(port_str), "%d", port);
197 int getaddrinfo_err = getaddrinfo(host, port_str, &hints, &res);
198
199 if (getaddrinfo_err != 0) {
200 // printf("%s\n", gai_strerror(result));
200 return STATE_UNKNOWN; 201 return STATE_UNKNOWN;
201 } 202 }
202 203
203 r = res; 204 struct addrinfo *addressPointer = res;
204 while (r) { 205 while (addressPointer) {
205 /* attempt to create a socket */ 206 /* attempt to create a socket */
206 *sd = socket (r->ai_family, socktype, r->ai_protocol); 207 *socketDescriptor =
208 socket(addressPointer->ai_family, socktype, addressPointer->ai_protocol);
207 209
208 if (*sd < 0) { 210 if (*socketDescriptor < 0) {
209 printf ("%s\n", _("Socket creation failed")); 211 // printf("%s\n", _("Socket creation failed"));
210 freeaddrinfo (r); 212 freeaddrinfo(addressPointer);
211 return STATE_UNKNOWN; 213 return STATE_UNKNOWN;
212 } 214 }
213 215
214 /* attempt to open a connection */ 216 /* attempt to open a connection */
215 result = connect (*sd, r->ai_addr, r->ai_addrlen); 217 result =
218 connect(*socketDescriptor, addressPointer->ai_addr, addressPointer->ai_addrlen);
216 219
217 if (result == 0) { 220 if (result == 0) {
218 was_refused = false; 221 was_refused = false;
@@ -227,149 +230,157 @@ np_net_connect (const char *host_name, int port, int *sd, int proto)
227 } 230 }
228 } 231 }
229 232
230 close (*sd); 233 close(*socketDescriptor);
231 r = r->ai_next; 234 addressPointer = addressPointer->ai_next;
232 } 235 }
233 freeaddrinfo (res); 236
234 } 237 freeaddrinfo(res);
235 /* else the hostname is interpreted as a path to a unix socket */ 238
236 else { 239 } else {
237 if(strlen(host_name) >= UNIX_PATH_MAX){ 240 /* else the hostname is interpreted as a path to a unix socket */
241 if (strlen(host_name) >= UNIX_PATH_MAX) {
238 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket")); 242 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket"));
239 } 243 }
240 memset(&su, 0, sizeof(su)); 244
245 struct sockaddr_un su = {};
241 su.sun_family = AF_UNIX; 246 su.sun_family = AF_UNIX;
242 strncpy(su.sun_path, host_name, UNIX_PATH_MAX); 247 strncpy(su.sun_path, host_name, UNIX_PATH_MAX);
243 *sd = socket(PF_UNIX, SOCK_STREAM, 0); 248 *socketDescriptor = socket(PF_UNIX, SOCK_STREAM, 0);
244 if(*sd < 0){ 249
250 if (*socketDescriptor < 0) {
245 die(STATE_UNKNOWN, _("Socket creation failed")); 251 die(STATE_UNKNOWN, _("Socket creation failed"));
246 } 252 }
247 result = connect(*sd, (struct sockaddr *)&su, sizeof(su)); 253
248 if (result < 0 && errno == ECONNREFUSED) 254 result = connect(*socketDescriptor, (struct sockaddr *)&su, sizeof(su));
255 if (result < 0 && errno == ECONNREFUSED) {
249 was_refused = true; 256 was_refused = true;
257 }
250 } 258 }
251 259
252 if (result == 0) 260 if (result == 0) {
253 return STATE_OK; 261 return STATE_OK;
254 else if (was_refused) { 262 }
263
264 if (was_refused) {
255 switch (econn_refuse_state) { /* a user-defined expected outcome */ 265 switch (econn_refuse_state) { /* a user-defined expected outcome */
256 case STATE_OK: 266 case STATE_OK:
257 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */ 267 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */
258 case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */ 268 case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */
259 if (is_socket) 269 if (is_socket) {
260 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));
261 else 271 } else {
262 printf("connect to address %s and port %d: %s\n", 272 // printf("connect to address %s and port %d: %s\n", host_name, port,
263 host_name, port, strerror(errno)); 273 // strerror(errno));
274 }
264 return STATE_CRITICAL; 275 return STATE_CRITICAL;
265 break; 276 break;
266 default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */ 277 default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */
267 return STATE_UNKNOWN; 278 return STATE_UNKNOWN;
268 break; 279 break;
269 } 280 }
270 } 281 } else {
271 else { 282 if (is_socket) {
272 if (is_socket) 283 // printf("connect to file socket %s: %s\n", host_name, strerror(errno));
273 printf("connect to file socket %s: %s\n", host_name, strerror(errno)); 284 } else {
274 else 285 // printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno));
275 printf("connect to address %s and port %d: %s\n", 286 }
276 host_name, port, strerror(errno));
277 return STATE_CRITICAL; 287 return STATE_CRITICAL;
278 } 288 }
279} 289}
280 290
281int 291mp_state_enum send_request(const int socket, const int proto, const char *send_buffer,
282send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size) 292 char *recv_buffer, const int recv_size) {
283{ 293 mp_state_enum result = STATE_OK;
284 int result = STATE_OK;
285 int send_result;
286 int recv_result;
287 struct timeval tv;
288 fd_set readfds;
289 294
290 send_result = send (sd, send_buffer, strlen (send_buffer), 0); 295 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
291 if (send_result<0 || (size_t)send_result!=strlen(send_buffer)) { 296 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
292 printf ("%s\n", _("Send failed")); 297 // printf("%s\n", _("Send failed"));
293 result = STATE_WARNING; 298 result = STATE_WARNING;
294 } 299 }
295 300
296 /* 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
297 for data from the host */ 302 for data from the host */
298 tv.tv_sec = socket_timeout - 1; 303 struct timeval timestamp = {
299 tv.tv_usec = 0; 304 .tv_sec = socket_timeout - 1,
300 FD_ZERO (&readfds); 305 .tv_usec = 0,
301 FD_SET (sd, &readfds); 306 };
302 select (sd + 1, &readfds, NULL, NULL, &tv); 307 fd_set readfds;
308 FD_ZERO(&readfds);
309 FD_SET(socket, &readfds);
310 select(socket + 1, &readfds, NULL, NULL, &timestamp);
303 311
304 /* make sure some data has arrived */ 312 /* make sure some data has arrived */
305 if (!FD_ISSET (sd, &readfds)) { 313 if (!FD_ISSET(socket, &readfds)) {
306 strcpy (recv_buffer, ""); 314 strcpy(recv_buffer, "");
307 printf ("%s\n", _("No data was received from host!")); 315 // printf("%s\n", _("No data was received from host!"));
308 result = STATE_WARNING; 316 result = STATE_WARNING;
309 } 317 } else {
310 318 ssize_t recv_result = recv(socket, recv_buffer, (size_t)(recv_size - 1), 0);
311 else {
312 recv_result = recv (sd, recv_buffer, (size_t)recv_size - 1, 0);
313 if (recv_result == -1) { 319 if (recv_result == -1) {
314 strcpy (recv_buffer, ""); 320 strcpy(recv_buffer, "");
315 if (proto != IPPROTO_TCP) 321 if (proto != IPPROTO_TCP) {
316 printf ("%s\n", _("Receive failed")); 322 // printf("%s\n", _("Receive failed"));
323 }
317 result = STATE_WARNING; 324 result = STATE_WARNING;
318 } 325 } else {
319 else
320 recv_buffer[recv_result] = 0; 326 recv_buffer[recv_result] = 0;
327 }
321 328
322 /* die returned string */ 329 /* die returned string */
323 recv_buffer[recv_size - 1] = 0; 330 recv_buffer[recv_size - 1] = 0;
324 } 331 }
332
325 return result; 333 return result;
326} 334}
327 335
328 336bool is_host(const char *address) {
329bool is_host (const char *address) { 337 if (is_addr(address) || is_hostname(address)) {
330 if (is_addr (address) || is_hostname (address))
331 return (true); 338 return (true);
339 }
332 340
333 return (false); 341 return (false);
334} 342}
335 343
336void 344void host_or_die(const char *str) {
337host_or_die(const char *str) 345 if (!str || (!is_addr(str) && !is_hostname(str))) {
338{
339 if(!str || (!is_addr(str) && !is_hostname(str)))
340 usage_va(_("Invalid hostname/address - %s"), str); 346 usage_va(_("Invalid hostname/address - %s"), str);
347 }
341} 348}
342 349
343bool is_addr (const char *address) { 350bool is_addr(const char *address) {
344#ifdef USE_IPV6 351#ifdef USE_IPV6
345 if (address_family == AF_INET && is_inet_addr (address)) 352 if (address_family == AF_INET && is_inet_addr(address)) {
346 return true; 353 return true;
347 else if (address_family == AF_INET6 && is_inet6_addr (address)) 354 }
355
356 if (address_family == AF_INET6 && is_inet6_addr(address)) {
348 return true; 357 return true;
358 }
349#else 359#else
350 if (is_inet_addr (address)) 360 if (is_inet_addr(address)) {
351 return (true); 361 return true;
362 }
352#endif 363#endif
353 364
354 return (false); 365 return false;
355} 366}
356 367
357int 368bool dns_lookup(const char *node_string, struct sockaddr_storage *ss, const int family) {
358dns_lookup (const char *in, struct sockaddr_storage *ss, int family)
359{
360 struct addrinfo hints; 369 struct addrinfo hints;
361 struct addrinfo *res; 370 memset(&hints, 0, sizeof(struct addrinfo));
362 int retval;
363
364 memset (&hints, 0, sizeof(struct addrinfo));
365 hints.ai_family = family; 371 hints.ai_family = family;
366 372
367 retval = getaddrinfo (in, NULL, &hints, &res); 373 struct addrinfo *res;
368 if (retval != 0) 374 int retval = getaddrinfo(node_string, NULL, &hints, &res);
375 if (retval != 0) {
369 return false; 376 return false;
377 }
378
379 if (ss != NULL) {
380 memcpy(ss, res->ai_addr, res->ai_addrlen);
381 }
382
383 freeaddrinfo(res);
370 384
371 if (ss != NULL)
372 memcpy (ss, res->ai_addr, res->ai_addrlen);
373 freeaddrinfo (res);
374 return true; 385 return true;
375} 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 d0bfac62..e87388b0 100644
--- a/plugins/picohttpparser/picohttpparser.c
+++ b/plugins/picohttpparser/picohttpparser.c
@@ -28,622 +28,640 @@
28#include <stddef.h> 28#include <stddef.h>
29#include <string.h> 29#include <string.h>
30#ifdef __SSE4_2__ 30#ifdef __SSE4_2__
31#ifdef _MSC_VER 31# ifdef _MSC_VER
32#include <nmmintrin.h> 32# include <nmmintrin.h>
33#else 33# else
34#include <x86intrin.h> 34# include <x86intrin.h>
35#endif 35# endif
36#endif 36#endif
37#include "picohttpparser.h" 37#include "picohttpparser.h"
38 38
39#if __GNUC__ >= 3 39#if __GNUC__ >= 3
40#define likely(x) __builtin_expect(!!(x), 1) 40# define likely(x) __builtin_expect(!!(x), 1)
41#define unlikely(x) __builtin_expect(!!(x), 0) 41# define unlikely(x) __builtin_expect(!!(x), 0)
42#else 42#else
43#define likely(x) (x) 43# define likely(x) (x)
44#define unlikely(x) (x) 44# define unlikely(x) (x)
45#endif 45#endif
46 46
47#ifdef _MSC_VER 47#ifdef _MSC_VER
48#define ALIGNED(n) _declspec(align(n)) 48# define ALIGNED(n) _declspec(align(n))
49#else 49#else
50#define ALIGNED(n) __attribute__((aligned(n))) 50# define ALIGNED(n) __attribute__((aligned(n)))
51#endif 51#endif
52 52
53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) 53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c) - 040u < 0137u)
54 54
55#define CHECK_EOF() \ 55#define CHECK_EOF() \
56 if (buf == buf_end) { \ 56 if (buf == buf_end) { \
57 *ret = -2; \ 57 *ret = -2; \
58 return NULL; \ 58 return NULL; \
59 } 59 }
60 60
61#define EXPECT_CHAR_NO_CHECK(ch) \ 61#define EXPECT_CHAR_NO_CHECK(ch) \
62 if (*buf++ != ch) { \ 62 if (*buf++ != ch) { \
63 *ret = -1; \ 63 *ret = -1; \
64 return NULL; \ 64 return NULL; \
65 } 65 }
66 66
67#define EXPECT_CHAR(ch) \ 67#define EXPECT_CHAR(ch) \
68 CHECK_EOF(); \ 68 CHECK_EOF(); \
69 EXPECT_CHAR_NO_CHECK(ch); 69 EXPECT_CHAR_NO_CHECK(ch);
70 70
71#define ADVANCE_TOKEN(tok, toklen) \ 71#define ADVANCE_TOKEN(tok, toklen) \
72 do { \ 72 do { \
73 const char *tok_start = buf; \ 73 const char *tok_start = buf; \
74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ 74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
75 int found2; \ 75 int found2; \
76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ 76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
77 if (!found2) { \ 77 if (!found2) { \
78 CHECK_EOF(); \ 78 CHECK_EOF(); \
79 } \ 79 } \
80 while (1) { \ 80 while (1) { \
81 if (*buf == ' ') { \ 81 if (*buf == ' ') { \
82 break; \ 82 break; \
83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ 83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \ 84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \
85 *ret = -1; \ 85 *ret = -1; \
86 return NULL; \ 86 return NULL; \
87 } \ 87 } \
88 } \ 88 } \
89 ++buf; \ 89 ++buf; \
90 CHECK_EOF(); \ 90 CHECK_EOF(); \
91 } \ 91 } \
92 tok = tok_start; \ 92 tok = tok_start; \
93 toklen = buf - tok_start; \ 93 toklen = buf - tok_start; \
94 } while (0) 94 } while (0)
95 95
96static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 96static const char *token_char_map =
97 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" 97 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
98 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" 98 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
99 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" 99 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
100 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 100 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
101 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 101 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
104 104 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
105static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) 105
106{ 106static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges,
107 *found = 0; 107 size_t ranges_size, int *found) {
108 *found = 0;
108#if __SSE4_2__ 109#if __SSE4_2__
109 if (likely(buf_end - buf >= 16)) { 110 if (likely(buf_end - buf >= 16)) {
110 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); 111 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
111 112
112 size_t left = (buf_end - buf) & ~15; 113 size_t left = (buf_end - buf) & ~15;
113 do { 114 do {
114 __m128i b16 = _mm_loadu_si128((const __m128i *)buf); 115 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
115 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); 116 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16,
116 if (unlikely(r != 16)) { 117 _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
117 buf += r; 118 if (unlikely(r != 16)) {
118 *found = 1; 119 buf += r;
119 break; 120 *found = 1;
120 } 121 break;
121 buf += 16; 122 }
122 left -= 16; 123 buf += 16;
123 } while (likely(left != 0)); 124 left -= 16;
124 } 125 } while (likely(left != 0));
126 }
125#else 127#else
126 /* suppress unused parameter warning */ 128 /* suppress unused parameter warning */
127 (void)buf_end; 129 (void)buf_end;
128 (void)ranges; 130 (void)ranges;
129 (void)ranges_size; 131 (void)ranges_size;
130#endif 132#endif
131 return buf; 133 return buf;
132} 134}
133 135
134static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) 136static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token,
135{ 137 size_t *token_len, int *ret) {
136 const char *token_start = buf; 138 const char *token_start = buf;
137 139
138#ifdef __SSE4_2__ 140#ifdef __SSE4_2__
139 static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ 141 static const char ALIGNED(16) ranges1[16] =
140 "\012\037" /* allow SP and up to but not including DEL */ 142 "\0\010" /* allow HT */
141 "\177\177"; /* allow chars w. MSB set */ 143 "\012\037" /* allow SP and up to but not including DEL */
142 int found; 144 "\177\177"; /* allow chars w. MSB set */
143 buf = findchar_fast(buf, buf_end, ranges1, 6, &found); 145 int found;
144 if (found) 146 buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
145 goto FOUND_CTL; 147 if (found) {
148 goto FOUND_CTL;
149 }
146#else 150#else
147 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ 151 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined
148 while (likely(buf_end - buf >= 8)) { 152 */
149#define DOIT() \ 153 while (likely(buf_end - buf >= 8)) {
150 do { \ 154# define DOIT() \
151 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ 155 do { \
152 goto NonPrintable; \ 156 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
153 ++buf; \ 157 goto NonPrintable; \
154 } while (0) 158 ++buf; \
155 DOIT(); 159 } while (0)
156 DOIT(); 160 DOIT();
157 DOIT(); 161 DOIT();
158 DOIT(); 162 DOIT();
159 DOIT(); 163 DOIT();
160 DOIT(); 164 DOIT();
161 DOIT(); 165 DOIT();
162 DOIT(); 166 DOIT();
163#undef DOIT 167 DOIT();
164 continue; 168# undef DOIT
165 NonPrintable: 169 continue;
166 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 170 NonPrintable:
167 goto FOUND_CTL; 171 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
168 } 172 unlikely(*buf == '\177')) {
169 ++buf; 173 goto FOUND_CTL;
170 } 174 }
175 ++buf;
176 }
171#endif 177#endif
172 for (;; ++buf) { 178 for (;; ++buf) {
173 CHECK_EOF(); 179 CHECK_EOF();
174 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { 180 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
175 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 181 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
176 goto FOUND_CTL; 182 unlikely(*buf == '\177')) {
177 } 183 goto FOUND_CTL;
178 } 184 }
179 } 185 }
186 }
180FOUND_CTL: 187FOUND_CTL:
181 if (likely(*buf == '\015')) { 188 if (likely(*buf == '\015')) {
182 ++buf; 189 ++buf;
183 EXPECT_CHAR('\012'); 190 EXPECT_CHAR('\012');
184 *token_len = buf - 2 - token_start; 191 *token_len = buf - 2 - token_start;
185 } else if (*buf == '\012') { 192 } else if (*buf == '\012') {
186 *token_len = buf - token_start; 193 *token_len = buf - token_start;
187 ++buf; 194 ++buf;
188 } else { 195 } else {
189 *ret = -1; 196 *ret = -1;
190 return NULL; 197 return NULL;
191 } 198 }
192 *token = token_start; 199 *token = token_start;
193 200
194 return buf; 201 return buf;
195} 202}
196 203
197static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) 204static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) {
198{ 205 int ret_cnt = 0;
199 int ret_cnt = 0; 206 buf = last_len < 3 ? buf : buf + last_len - 3;
200 buf = last_len < 3 ? buf : buf + last_len - 3; 207
201 208 while (1) {
202 while (1) { 209 CHECK_EOF();
203 CHECK_EOF(); 210 if (*buf == '\015') {
204 if (*buf == '\015') { 211 ++buf;
205 ++buf; 212 CHECK_EOF();
206 CHECK_EOF(); 213 EXPECT_CHAR('\012');
207 EXPECT_CHAR('\012'); 214 ++ret_cnt;
208 ++ret_cnt; 215 } else if (*buf == '\012') {
209 } else if (*buf == '\012') { 216 ++buf;
210 ++buf; 217 ++ret_cnt;
211 ++ret_cnt; 218 } else {
212 } else { 219 ++buf;
213 ++buf; 220 ret_cnt = 0;
214 ret_cnt = 0; 221 }
215 } 222 if (ret_cnt == 2) {
216 if (ret_cnt == 2) { 223 return buf;
217 return buf; 224 }
218 } 225 }
219 } 226
220 227 *ret = -2;
221 *ret = -2; 228 return NULL;
222 return NULL;
223} 229}
224 230
225#define PARSE_INT(valp_, mul_) \ 231#define PARSE_INT(valp_, mul_) \
226 if (*buf < '0' || '9' < *buf) { \ 232 if (*buf < '0' || '9' < *buf) { \
227 buf++; \ 233 buf++; \
228 *ret = -1; \ 234 *ret = -1; \
229 return NULL; \ 235 return NULL; \
230 } \ 236 } \
231 *(valp_) = (mul_) * (*buf++ - '0'); 237 *(valp_) = (mul_) * (*buf++ - '0');
232 238
233#define PARSE_INT_3(valp_) \ 239#define PARSE_INT_3(valp_) \
234 do { \ 240 do { \
235 int res_ = 0; \ 241 int res_ = 0; \
236 PARSE_INT(&res_, 100) \ 242 PARSE_INT(&res_, 100) \
237 *valp_ = res_; \ 243 *valp_ = res_; \
238 PARSE_INT(&res_, 10) \ 244 PARSE_INT(&res_, 10) \
239 *valp_ += res_; \ 245 *valp_ += res_; \
240 PARSE_INT(&res_, 1) \ 246 PARSE_INT(&res_, 1) \
241 *valp_ += res_; \ 247 *valp_ += res_; \
242 } while (0) 248 } while (0)
243 249
244/* returned pointer is always within [buf, buf_end), or null */ 250/* returned pointer is always within [buf, buf_end), or null */
245static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *ret) 251static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version,
246{ 252 int *minor_version, int *ret) {
247 /* we want at least [HTTP/1.<two chars>] to try to parse */ 253 /* we want at least [HTTP/1.<two chars>] to try to parse */
248 if (buf_end - buf < 9) { 254 if (buf_end - buf < 9) {
249 *ret = -2; 255 *ret = -2;
250 return NULL; 256 return NULL;
251 } 257 }
252 EXPECT_CHAR_NO_CHECK('H'); 258 EXPECT_CHAR_NO_CHECK('H');
253 EXPECT_CHAR_NO_CHECK('T'); 259 EXPECT_CHAR_NO_CHECK('T');
254 EXPECT_CHAR_NO_CHECK('T'); 260 EXPECT_CHAR_NO_CHECK('T');
255 EXPECT_CHAR_NO_CHECK('P'); 261 EXPECT_CHAR_NO_CHECK('P');
256 EXPECT_CHAR_NO_CHECK('/'); 262 EXPECT_CHAR_NO_CHECK('/');
257 PARSE_INT(major_version, 1); 263 PARSE_INT(major_version, 1);
258 if (*major_version == 1) { 264 if (*major_version == 1) {
259 EXPECT_CHAR_NO_CHECK('.'); 265 EXPECT_CHAR_NO_CHECK('.');
260 PARSE_INT(minor_version, 1); 266 PARSE_INT(minor_version, 1);
261 } else { 267 } else {
262 *minor_version = 0; 268 *minor_version = 0;
263 } 269 }
264 return buf; 270 return buf;
265} 271}
266 272
267static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, 273static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers,
268 size_t max_headers, int *ret) 274 size_t *num_headers, size_t max_headers, int *ret) {
269{ 275 for (;; ++*num_headers) {
270 for (;; ++*num_headers) { 276 CHECK_EOF();
271 CHECK_EOF(); 277 if (*buf == '\015') {
272 if (*buf == '\015') { 278 ++buf;
273 ++buf; 279 EXPECT_CHAR('\012');
274 EXPECT_CHAR('\012'); 280 break;
275 break; 281 } else if (*buf == '\012') {
276 } else if (*buf == '\012') { 282 ++buf;
277 ++buf; 283 break;
278 break; 284 }
279 } 285 if (*num_headers == max_headers) {
280 if (*num_headers == max_headers) { 286 *ret = -1;
281 *ret = -1; 287 return NULL;
282 return NULL; 288 }
283 } 289 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
284 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { 290 /* parsing name, but do not discard SP before colon, see
285 /* parsing name, but do not discard SP before colon, see 291 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
286 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ 292 headers[*num_headers].name = buf;
287 headers[*num_headers].name = buf; 293 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
288 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */ 294 "\"\"" /* 0x22 */
289 "\"\"" /* 0x22 */ 295 "()" /* 0x28,0x29 */
290 "()" /* 0x28,0x29 */ 296 ",," /* 0x2c */
291 ",," /* 0x2c */ 297 "//" /* 0x2f */
292 "//" /* 0x2f */ 298 ":@" /* 0x3a-0x40 */
293 ":@" /* 0x3a-0x40 */ 299 "[]" /* 0x5b-0x5d */
294 "[]" /* 0x5b-0x5d */ 300 "{\377"; /* 0x7b-0xff */
295 "{\377"; /* 0x7b-0xff */ 301 int found;
296 int found; 302 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
297 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found); 303 if (!found) {
298 if (!found) { 304 CHECK_EOF();
299 CHECK_EOF(); 305 }
300 } 306 while (1) {
301 while (1) { 307 if (*buf == ':') {
302 if (*buf == ':') { 308 break;
303 break; 309 } else if (!token_char_map[(unsigned char)*buf]) {
304 } else if (!token_char_map[(unsigned char)*buf]) { 310 *ret = -1;
305 *ret = -1; 311 return NULL;
306 return NULL; 312 }
307 } 313 ++buf;
308 ++buf; 314 CHECK_EOF();
309 CHECK_EOF(); 315 }
310 } 316 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
311 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) { 317 *ret = -1;
312 *ret = -1; 318 return NULL;
313 return NULL; 319 }
314 } 320 ++buf;
315 ++buf; 321 for (;; ++buf) {
316 for (;; ++buf) { 322 CHECK_EOF();
317 CHECK_EOF(); 323 if (!(*buf == ' ' || *buf == '\t')) {
318 if (!(*buf == ' ' || *buf == '\t')) { 324 break;
319 break; 325 }
320 } 326 }
321 } 327 } else {
322 } else { 328 headers[*num_headers].name = NULL;
323 headers[*num_headers].name = NULL; 329 headers[*num_headers].name_len = 0;
324 headers[*num_headers].name_len = 0; 330 }
325 } 331 const char *value;
326 const char *value; 332 size_t value_len;
327 size_t value_len; 333 if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
328 if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) { 334 return NULL;
329 return NULL; 335 }
330 } 336 /* remove trailing SPs and HTABs */
331 /* remove trailing SPs and HTABs */ 337 const char *value_end = value + value_len;
332 const char *value_end = value + value_len; 338 for (; value_end != value; --value_end) {
333 for (; value_end != value; --value_end) { 339 const char c = *(value_end - 1);
334 const char c = *(value_end - 1); 340 if (!(c == ' ' || c == '\t')) {
335 if (!(c == ' ' || c == '\t')) { 341 break;
336 break; 342 }
337 } 343 }
338 } 344 headers[*num_headers].value = value;
339 headers[*num_headers].value = value; 345 headers[*num_headers].value_len = value_end - value;
340 headers[*num_headers].value_len = value_end - value; 346 }
341 } 347 return buf;
342 return buf;
343} 348}
344 349
345static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, 350static const char *parse_request(const char *buf, const char *buf_end, const char **method,
346 size_t *path_len, int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, 351 size_t *method_len, const char **path, size_t *path_len,
347 size_t max_headers, int *ret) 352 int *major_version, int *minor_version, struct phr_header *headers,
348{ 353 size_t *num_headers, size_t max_headers, int *ret) {
349 /* skip first empty line (some clients add CRLF after POST content) */ 354 /* skip first empty line (some clients add CRLF after POST content) */
350 CHECK_EOF(); 355 CHECK_EOF();
351 if (*buf == '\015') { 356 if (*buf == '\015') {
352 ++buf; 357 ++buf;
353 EXPECT_CHAR('\012'); 358 EXPECT_CHAR('\012');
354 } else if (*buf == '\012') { 359 } else if (*buf == '\012') {
355 ++buf; 360 ++buf;
356 } 361 }
357 362
358 /* parse request line */ 363 /* parse request line */
359 ADVANCE_TOKEN(*method, *method_len); 364 ADVANCE_TOKEN(*method, *method_len);
360 do { 365 do {
361 ++buf; 366 ++buf;
362 } while (*buf == ' '); 367 } while (*buf == ' ');
363 ADVANCE_TOKEN(*path, *path_len); 368 ADVANCE_TOKEN(*path, *path_len);
364 do { 369 do {
365 ++buf; 370 ++buf;
366 } while (*buf == ' '); 371 } while (*buf == ' ');
367 if (*method_len == 0 || *path_len == 0) { 372 if (*method_len == 0 || *path_len == 0) {
368 *ret = -1; 373 *ret = -1;
369 return NULL; 374 return NULL;
370 } 375 }
371 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { 376 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) {
372 return NULL; 377 return NULL;
373 } 378 }
374 if (*buf == '\015') { 379 if (*buf == '\015') {
375 ++buf; 380 ++buf;
376 EXPECT_CHAR('\012'); 381 EXPECT_CHAR('\012');
377 } else if (*buf == '\012') { 382 } else if (*buf == '\012') {
378 ++buf; 383 ++buf;
379 } else { 384 } else {
380 *ret = -1; 385 *ret = -1;
381 return NULL; 386 return NULL;
382 } 387 }
383 388
384 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 389 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
385} 390}
386 391
387int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, 392int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len,
388 size_t *path_len, int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) 393 const char **path, size_t *path_len, int *major_version, int *minor_version,
389{ 394 struct phr_header *headers, size_t *num_headers, size_t last_len) {
390 const char *buf = buf_start, *buf_end = buf_start + len; 395 const char *buf = buf_start, *buf_end = buf_start + len;
391 size_t max_headers = *num_headers; 396 size_t max_headers = *num_headers;
392 int r; 397 int r;
393 398
394 *method = NULL; 399 *method = NULL;
395 *method_len = 0; 400 *method_len = 0;
396 *path = NULL; 401 *path = NULL;
397 *path_len = 0; 402 *path_len = 0;
398 *major_version = -1; 403 *major_version = -1;
399 *minor_version = -1; 404 *minor_version = -1;
400 *num_headers = 0; 405 *num_headers = 0;
401 406
402 /* if last_len != 0, check if the request is complete (a fast countermeasure 407 /* if last_len != 0, check if the request is complete (a fast countermeasure
403 against slowloris */ 408 against slowloris */
404 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 409 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
405 return r; 410 return r;
406 } 411 }
407 412
408 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version, minor_version, headers, num_headers, max_headers, 413 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version,
409 &r)) == NULL) { 414 minor_version, headers, num_headers, max_headers, &r)) == NULL) {
410 return r; 415 return r;
411 } 416 }
412 417
413 return (int)(buf - buf_start); 418 return (int)(buf - buf_start);
414} 419}
415 420
416static const char *parse_response(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *status, const char **msg, 421static const char *parse_response(const char *buf, const char *buf_end, int *major_version,
417 size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret) 422 int *minor_version, int *status, const char **msg,
418{ 423 size_t *msg_len, struct phr_header *headers, size_t *num_headers,
419 /* parse "HTTP/1.x" */ 424 size_t max_headers, int *ret) {
420 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { 425 /* parse "HTTP/1.x" */
421 return NULL; 426 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) {
422 } 427 return NULL;
423 /* skip space */ 428 }
424 if (*buf != ' ') { 429 /* skip space */
425 *ret = -1; 430 if (*buf != ' ') {
426 return NULL; 431 *ret = -1;
427 } 432 return NULL;
428 do { 433 }
429 ++buf; 434 do {
430 } while (*buf == ' '); 435 ++buf;
431 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */ 436 } while (*buf == ' ');
432 if (buf_end - buf < 4) { 437 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse
433 *ret = -2; 438 */
434 return NULL; 439 if (buf_end - buf < 4) {
435 } 440 *ret = -2;
436 PARSE_INT_3(status); 441 return NULL;
437 442 }
438 /* get message including preceding space */ 443 PARSE_INT_3(status);
439 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { 444
440 return NULL; 445 /* get message including preceding space */
441 } 446 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
442 if (*msg_len == 0) { 447 return NULL;
443 /* ok */ 448 }
444 } else if (**msg == ' ') { 449 if (*msg_len == 0) {
445 /* remove preceding space */ 450 /* ok */
446 do { 451 } else if (**msg == ' ') {
447 ++*msg; 452 /* remove preceding space */
448 --*msg_len; 453 do {
449 } while (**msg == ' '); 454 ++*msg;
450 } else { 455 --*msg_len;
451 /* garbage found after status code */ 456 } while (**msg == ' ');
452 *ret = -1; 457 } else {
453 return NULL; 458 /* garbage found after status code */
454 } 459 *ret = -1;
455 460 return NULL;
456 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 461 }
462
463 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
457} 464}
458 465
459int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version, int *status, const char **msg, size_t *msg_len, 466int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version,
460 struct phr_header *headers, size_t *num_headers, size_t last_len) 467 int *status, const char **msg, size_t *msg_len, struct phr_header *headers,
461{ 468 size_t *num_headers, size_t last_len) {
462 const char *buf = buf_start, *buf_end = buf + len; 469 const char *buf = buf_start, *buf_end = buf + len;
463 size_t max_headers = *num_headers; 470 size_t max_headers = *num_headers;
464 int r; 471 int r;
465 472
466 *major_version = -1; 473 *major_version = -1;
467 *minor_version = -1; 474 *minor_version = -1;
468 *status = 0; 475 *status = 0;
469 *msg = NULL; 476 *msg = NULL;
470 *msg_len = 0; 477 *msg_len = 0;
471 *num_headers = 0; 478 *num_headers = 0;
472 479
473 /* if last_len != 0, check if the response is complete (a fast countermeasure 480 /* if last_len != 0, check if the response is complete (a fast countermeasure
474 against slowloris */ 481 against slowloris */
475 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 482 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
476 return r; 483 return r;
477 } 484 }
478 485
479 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) { 486 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len,
480 return r; 487 headers, num_headers, max_headers, &r)) == NULL) {
481 } 488 return r;
482 489 }
483 return (int)(buf - buf_start); 490
491 return (int)(buf - buf_start);
484} 492}
485 493
486int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) 494int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers,
487{ 495 size_t *num_headers, size_t last_len) {
488 const char *buf = buf_start, *buf_end = buf + len; 496 const char *buf = buf_start, *buf_end = buf + len;
489 size_t max_headers = *num_headers; 497 size_t max_headers = *num_headers;
490 int r; 498 int r;
491 499
492 *num_headers = 0; 500 *num_headers = 0;
493 501
494 /* if last_len != 0, check if the response is complete (a fast countermeasure 502 /* if last_len != 0, check if the response is complete (a fast countermeasure
495 against slowloris */ 503 against slowloris */
496 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 504 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
497 return r; 505 return r;
498 } 506 }
499 507
500 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) { 508 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
501 return r; 509 return r;
502 } 510 }
503 511
504 return (int)(buf - buf_start); 512 return (int)(buf - buf_start);
505} 513}
506 514
507enum { 515enum {
508 CHUNKED_IN_CHUNK_SIZE, 516 CHUNKED_IN_CHUNK_SIZE,
509 CHUNKED_IN_CHUNK_EXT, 517 CHUNKED_IN_CHUNK_EXT,
510 CHUNKED_IN_CHUNK_DATA, 518 CHUNKED_IN_CHUNK_DATA,
511 CHUNKED_IN_CHUNK_CRLF, 519 CHUNKED_IN_CHUNK_CRLF,
512 CHUNKED_IN_TRAILERS_LINE_HEAD, 520 CHUNKED_IN_TRAILERS_LINE_HEAD,
513 CHUNKED_IN_TRAILERS_LINE_MIDDLE 521 CHUNKED_IN_TRAILERS_LINE_MIDDLE
514}; 522};
515 523
516static int decode_hex(int ch) 524static int decode_hex(int ch) {
517{ 525 if ('0' <= ch && ch <= '9') {
518 if ('0' <= ch && ch <= '9') { 526 return ch - '0';
519 return ch - '0'; 527 } else if ('A' <= ch && ch <= 'F') {
520 } else if ('A' <= ch && ch <= 'F') { 528 return ch - 'A' + 0xa;
521 return ch - 'A' + 0xa; 529 } else if ('a' <= ch && ch <= 'f') {
522 } else if ('a' <= ch && ch <= 'f') { 530 return ch - 'a' + 0xa;
523 return ch - 'a' + 0xa; 531 } else {
524 } else { 532 return -1;
525 return -1; 533 }
526 }
527} 534}
528 535
529ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) 536ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) {
530{ 537 size_t dst = 0, src = 0, bufsz = *_bufsz;
531 size_t dst = 0, src = 0, bufsz = *_bufsz; 538 ssize_t ret = -2; /* incomplete */
532 ssize_t ret = -2; /* incomplete */ 539
533 540 while (1) {
534 while (1) { 541 switch (decoder->_state) {
535 switch (decoder->_state) { 542 case CHUNKED_IN_CHUNK_SIZE:
536 case CHUNKED_IN_CHUNK_SIZE: 543 for (;; ++src) {
537 for (;; ++src) { 544 int v;
538 int v; 545 if (src == bufsz) {
539 if (src == bufsz) 546 goto Exit;
540 goto Exit; 547 }
541 if ((v = decode_hex(buf[src])) == -1) { 548 if ((v = decode_hex(buf[src])) == -1) {
542 if (decoder->_hex_count == 0) { 549 if (decoder->_hex_count == 0) {
543 ret = -1; 550 ret = -1;
544 goto Exit; 551 goto Exit;
545 } 552 }
546 break; 553 break;
547 } 554 }
548 if (decoder->_hex_count == sizeof(size_t) * 2) { 555 if (decoder->_hex_count == sizeof(size_t) * 2) {
549 ret = -1; 556 ret = -1;
550 goto Exit; 557 goto Exit;
551 } 558 }
552 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v; 559 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
553 ++decoder->_hex_count; 560 ++decoder->_hex_count;
554 } 561 }
555 decoder->_hex_count = 0; 562 decoder->_hex_count = 0;
556 decoder->_state = CHUNKED_IN_CHUNK_EXT; 563 decoder->_state = CHUNKED_IN_CHUNK_EXT;
557 /* fallthru */ 564 /* fallthru */
558 case CHUNKED_IN_CHUNK_EXT: 565 case CHUNKED_IN_CHUNK_EXT:
559 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ 566 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
560 for (;; ++src) { 567 for (;; ++src) {
561 if (src == bufsz) 568 if (src == bufsz) {
562 goto Exit; 569 goto Exit;
563 if (buf[src] == '\012') 570 }
564 break; 571 if (buf[src] == '\012') {
565 } 572 break;
566 ++src; 573 }
567 if (decoder->bytes_left_in_chunk == 0) { 574 }
568 if (decoder->consume_trailer) { 575 ++src;
569 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; 576 if (decoder->bytes_left_in_chunk == 0) {
570 break; 577 if (decoder->consume_trailer) {
571 } else { 578 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
572 goto Complete; 579 break;
573 } 580 } else {
574 } 581 goto Complete;
575 decoder->_state = CHUNKED_IN_CHUNK_DATA; 582 }
576 /* fallthru */ 583 }
577 case CHUNKED_IN_CHUNK_DATA: { 584 decoder->_state = CHUNKED_IN_CHUNK_DATA;
578 size_t avail = bufsz - src; 585 /* fallthru */
579 if (avail < decoder->bytes_left_in_chunk) { 586 case CHUNKED_IN_CHUNK_DATA: {
580 if (dst != src) 587 size_t avail = bufsz - src;
581 memmove(buf + dst, buf + src, avail); 588 if (avail < decoder->bytes_left_in_chunk) {
582 src += avail; 589 if (dst != src) {
583 dst += avail; 590 memmove(buf + dst, buf + src, avail);
584 decoder->bytes_left_in_chunk -= avail; 591 }
585 goto Exit; 592 src += avail;
586 } 593 dst += avail;
587 if (dst != src) 594 decoder->bytes_left_in_chunk -= avail;
588 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); 595 goto Exit;
589 src += decoder->bytes_left_in_chunk; 596 }
590 dst += decoder->bytes_left_in_chunk; 597 if (dst != src) {
591 decoder->bytes_left_in_chunk = 0; 598 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
592 decoder->_state = CHUNKED_IN_CHUNK_CRLF; 599 }
593 } 600 src += decoder->bytes_left_in_chunk;
594 /* fallthru */ 601 dst += decoder->bytes_left_in_chunk;
595 case CHUNKED_IN_CHUNK_CRLF: 602 decoder->bytes_left_in_chunk = 0;
596 for (;; ++src) { 603 decoder->_state = CHUNKED_IN_CHUNK_CRLF;
597 if (src == bufsz) 604 }
598 goto Exit; 605 /* fallthru */
599 if (buf[src] != '\015') 606 case CHUNKED_IN_CHUNK_CRLF:
600 break; 607 for (;; ++src) {
601 } 608 if (src == bufsz) {
602 if (buf[src] != '\012') { 609 goto Exit;
603 ret = -1; 610 }
604 goto Exit; 611 if (buf[src] != '\015') {
605 } 612 break;
606 ++src; 613 }
607 decoder->_state = CHUNKED_IN_CHUNK_SIZE; 614 }
608 break; 615 if (buf[src] != '\012') {
609 case CHUNKED_IN_TRAILERS_LINE_HEAD: 616 ret = -1;
610 for (;; ++src) { 617 goto Exit;
611 if (src == bufsz) 618 }
612 goto Exit; 619 ++src;
613 if (buf[src] != '\015') 620 decoder->_state = CHUNKED_IN_CHUNK_SIZE;
614 break; 621 break;
615 } 622 case CHUNKED_IN_TRAILERS_LINE_HEAD:
616 if (buf[src++] == '\012') 623 for (;; ++src) {
617 goto Complete; 624 if (src == bufsz) {
618 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; 625 goto Exit;
619 /* fallthru */ 626 }
620 case CHUNKED_IN_TRAILERS_LINE_MIDDLE: 627 if (buf[src] != '\015') {
621 for (;; ++src) { 628 break;
622 if (src == bufsz) 629 }
623 goto Exit; 630 }
624 if (buf[src] == '\012') 631 if (buf[src++] == '\012') {
625 break; 632 goto Complete;
626 } 633 }
627 ++src; 634 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
628 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; 635 /* fallthru */
629 break; 636 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
630 default: 637 for (;; ++src) {
631 assert(!"decoder is corrupt"); 638 if (src == bufsz) {
632 } 639 goto Exit;
633 } 640 }
641 if (buf[src] == '\012') {
642 break;
643 }
644 }
645 ++src;
646 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
647 break;
648 default:
649 assert(!"decoder is corrupt");
650 }
651 }
634 652
635Complete: 653Complete:
636 ret = bufsz - src; 654 ret = bufsz - src;
637Exit: 655Exit:
638 if (dst != src) 656 if (dst != src) {
639 memmove(buf + dst, buf + src, bufsz - src); 657 memmove(buf + dst, buf + src, bufsz - src);
640 *_bufsz = dst; 658 }
641 return ret; 659 *_bufsz = dst;
660 return ret;
642} 661}
643 662
644int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) 663int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) {
645{ 664 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
646 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
647} 665}
648 666
649#undef CHECK_EOF 667#undef CHECK_EOF
diff --git a/plugins/picohttpparser/picohttpparser.h b/plugins/picohttpparser/picohttpparser.h
index 8f13b36f..054c2812 100644
--- a/plugins/picohttpparser/picohttpparser.h
+++ b/plugins/picohttpparser/picohttpparser.h
@@ -30,7 +30,7 @@
30#include <sys/types.h> 30#include <sys/types.h>
31 31
32#ifdef _MSC_VER 32#ifdef _MSC_VER
33#define ssize_t intptr_t 33# define ssize_t intptr_t
34#endif 34#endif
35 35
36#ifdef __cplusplus 36#ifdef __cplusplus
@@ -40,30 +40,33 @@ extern "C" {
40/* contains name and value of a header (name == NULL if is a continuing line 40/* contains name and value of a header (name == NULL if is a continuing line
41 * of a multiline header */ 41 * of a multiline header */
42struct phr_header { 42struct phr_header {
43 const char *name; 43 const char *name;
44 size_t name_len; 44 size_t name_len;
45 const char *value; 45 const char *value;
46 size_t value_len; 46 size_t value_len;
47}; 47};
48 48
49/* returns number of bytes consumed if successful, -2 if request is partial, 49/* returns number of bytes consumed if successful, -2 if request is partial,
50 * -1 if failed */ 50 * -1 if failed */
51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len,
52 int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); 52 const char **path, size_t *path_len, int *major_version, int *minor_version,
53 struct phr_header *headers, size_t *num_headers, size_t last_len);
53 54
54/* ditto */ 55/* ditto */
55int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version, int *status, const char **msg, size_t *msg_len, 56int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version,
56 struct phr_header *headers, size_t *num_headers, size_t last_len); 57 int *status, const char **msg, size_t *msg_len, struct phr_header *headers,
58 size_t *num_headers, size_t last_len);
57 59
58/* ditto */ 60/* ditto */
59int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); 61int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers,
62 size_t last_len);
60 63
61/* should be zero-filled before start */ 64/* should be zero-filled before start */
62struct phr_chunked_decoder { 65struct phr_chunked_decoder {
63 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ 66 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
64 char consume_trailer; /* if trailing headers should be consumed */ 67 char consume_trailer; /* if trailing headers should be consumed */
65 char _hex_count; 68 char _hex_count;
66 char _state; 69 char _state;
67}; 70};
68 71
69/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- 72/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
diff --git a/plugins/popen.c b/plugins/popen.c
index 54e63bc5..c596d1e0 100644
--- a/plugins/popen.c
+++ b/plugins/popen.c
@@ -1,294 +1,302 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins popen 3 * Monitoring Plugins popen
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2005-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2005-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* A safe alternative to popen 10 * A safe alternative to popen
11* 11 *
12* Provides spopen and spclose 12 * Provides spopen and spclose
13* 13 *
14* FILE * spopen(const char *); 14 * FILE * spopen(const char *);
15* int spclose(FILE *); 15 * int spclose(FILE *);
16* 16 *
17* Code taken with little modification from "Advanced Programming for the Unix 17 * Code taken with little modification from "Advanced Programming for the Unix
18* Environment" by W. Richard Stevens 18 * Environment" by W. Richard Stevens
19* 19 *
20* This is considered safe in that no shell is spawned, and the environment 20 * This is considered safe in that no shell is spawned, and the environment
21* and path passed to the exec'd program are essentially empty. (popen create 21 * and path passed to the exec'd program are essentially empty. (popen create
22* a shell and passes the environment to it). 22 * a shell and passes the environment to it).
23* 23 *
24* 24 *
25* This program is free software: you can redistribute it and/or modify 25 * This program is free software: you can redistribute it and/or modify
26* it under the terms of the GNU General Public License as published by 26 * it under the terms of the GNU General Public License as published by
27* the Free Software Foundation, either version 3 of the License, or 27 * the Free Software Foundation, either version 3 of the License, or
28* (at your option) any later version. 28 * (at your option) any later version.
29* 29 *
30* This program is distributed in the hope that it will be useful, 30 * This program is distributed in the hope that it will be useful,
31* but WITHOUT ANY WARRANTY; without even the implied warranty of 31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33* GNU General Public License for more details. 33 * GNU General Public License for more details.
34* 34 *
35* You should have received a copy of the GNU General Public License 35 * You should have received a copy of the GNU General Public License
36* along with this program. If not, see <http://www.gnu.org/licenses/>. 36 * along with this program. If not, see <http://www.gnu.org/licenses/>.
37* 37 *
38* 38 *
39*****************************************************************************/ 39 *****************************************************************************/
40 40
41#include "./common.h" 41#include "./common.h"
42#include "./utils.h" 42#include "./utils.h"
43#include "../lib/maxfd.h"
44 43
45/* extern so plugin has pid to kill exec'd process on timeouts */ 44/* extern so plugin has pid to kill exec'd process on timeouts */
46extern pid_t *childpid; 45extern pid_t *childpid;
47extern int *child_stderr_array; 46extern int *child_stderr_array;
48extern FILE *child_process; 47extern FILE *child_process;
49 48
50FILE *spopen (const char *); 49FILE *spopen(const char * /*cmdstring*/);
51int spclose (FILE *); 50int spclose(FILE * /*fp*/);
52#ifdef REDHAT_SPOPEN_ERROR 51#ifdef REDHAT_SPOPEN_ERROR
53void popen_sigchld_handler (int); 52void popen_sigchld_handler(int);
54#endif 53#endif
55void popen_timeout_alarm_handler (int); 54void popen_timeout_alarm_handler(int /*signo*/);
56 55
57#include <stdarg.h> /* ANSI C header file */ 56#include <stdarg.h> /* ANSI C header file */
58#include <fcntl.h> 57#include <fcntl.h>
59 58
60#include <limits.h> 59#include <limits.h>
61#include <sys/resource.h> 60#include <sys/resource.h>
62 61
63#ifdef HAVE_SYS_WAIT_H 62#ifdef HAVE_SYS_WAIT_H
64#include <sys/wait.h> 63# include <sys/wait.h>
65#endif 64#endif
66 65
67#ifndef WEXITSTATUS 66#ifndef WEXITSTATUS
68# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 67# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
69#endif 68#endif
70 69
71#ifndef WIFEXITED 70#ifndef WIFEXITED
72# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 71# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
73#endif 72#endif
74 73
75/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ 74/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
76#if defined(SIG_IGN) && !defined(SIG_ERR) 75#if defined(SIG_IGN) && !defined(SIG_ERR)
77#define SIG_ERR ((Sigfunc *)-1) 76# define SIG_ERR ((Sigfunc *)-1)
78#endif 77#endif
79 78
80 79char *pname = NULL; /* caller can set this from argv[0] */
81char *pname = NULL; /* caller can set this from argv[0] */
82 80
83#ifdef REDHAT_SPOPEN_ERROR 81#ifdef REDHAT_SPOPEN_ERROR
84static volatile int childtermd = 0; 82static volatile int childtermd = 0;
85#endif 83#endif
86 84
87FILE * 85FILE *spopen(const char *cmdstring) {
88spopen (const char *cmdstring) 86#ifdef RLIMIT_CORE
89{
90 char *env[2];
91 char *cmd = NULL;
92 char **argv = NULL;
93 char *str, *tmp;
94 int argc;
95
96 int i = 0, pfd[2], pfderr[2];
97 pid_t pid;
98
99#ifdef RLIMIT_CORE
100 /* do not leave core files */ 87 /* do not leave core files */
101 struct rlimit limit; 88 struct rlimit limit;
102 getrlimit (RLIMIT_CORE, &limit); 89 getrlimit(RLIMIT_CORE, &limit);
103 limit.rlim_cur = 0; 90 limit.rlim_cur = 0;
104 setrlimit (RLIMIT_CORE, &limit); 91 setrlimit(RLIMIT_CORE, &limit);
105#endif 92#endif
106 93
94 char *env[2];
107 env[0] = strdup("LC_ALL=C"); 95 env[0] = strdup("LC_ALL=C");
108 env[1] = NULL; 96 env[1] = NULL;
109 97
110 /* if no command was passed, return with no error */ 98 /* if no command was passed, return with no error */
111 if (cmdstring == NULL) 99 if (cmdstring == NULL) {
112 return (NULL); 100 return (NULL);
101 }
113 102
103 char *cmd = NULL;
114 /* make copy of command string so strtok() doesn't silently modify it */ 104 /* make copy of command string so strtok() doesn't silently modify it */
115 /* (the calling program may want to access it later) */ 105 /* (the calling program may want to access it later) */
116 cmd = malloc (strlen (cmdstring) + 1); 106 cmd = malloc(strlen(cmdstring) + 1);
117 if (cmd == NULL) 107 if (cmd == NULL) {
118 return NULL; 108 return NULL;
119 strcpy (cmd, cmdstring); 109 }
110 strcpy(cmd, cmdstring);
120 111
121 /* This is not a shell, so we don't handle "???" */ 112 /* This is not a shell, so we don't handle "???" */
122 if (strstr (cmdstring, "\"")) 113 if (strstr(cmdstring, "\"")) {
123 return NULL; 114 return NULL;
115 }
124 116
125 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ 117 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
126 if (strstr (cmdstring, " ' ") || strstr (cmdstring, "'''")) 118 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
127 return NULL; 119 return NULL;
120 }
128 121
122 int argc;
123 char **argv = NULL;
129 /* there cannot be more args than characters */ 124 /* there cannot be more args than characters */
130 argc = strlen (cmdstring) + 1; /* add 1 for NULL termination */ 125 argc = strlen(cmdstring) + 1; /* add 1 for NULL termination */
131 argv = malloc (sizeof(char*)*argc); 126 argv = malloc(sizeof(char *) * argc);
132 127
133 if (argv == NULL) { 128 if (argv == NULL) {
134 printf ("%s\n", _("Could not malloc argv array in popen()")); 129 printf("%s\n", _("Could not malloc argv array in popen()"));
135 return NULL; 130 return NULL;
136 } 131 }
137 132
133 int i = 0;
134 char *str;
138 /* loop to get arguments to command */ 135 /* loop to get arguments to command */
139 while (cmd) { 136 while (cmd) {
140 str = cmd; 137 str = cmd;
141 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */ 138 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
142 139
143 if (i >= argc - 2) { 140 if (i >= argc - 2) {
144 printf ("%s\n",_("CRITICAL - You need more args!!!")); 141 printf("%s\n", _("CRITICAL - You need more args!!!"));
145 return (NULL); 142 return (NULL);
146 } 143 }
147 144
148 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */ 145 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
149 str++; 146 str++;
150 if (!strstr (str, "'")) 147 if (!strstr(str, "'")) {
151 return NULL; /* balanced? */ 148 return NULL; /* balanced? */
152 cmd = 1 + strstr (str, "'"); 149 }
153 str[strcspn (str, "'")] = 0; 150 cmd = 1 + strstr(str, "'");
154 } 151 str[strcspn(str, "'")] = 0;
155 else if (strcspn(str,"'") < strcspn (str, " \t\r\n")) { 152 } else if (strcspn(str, "'") < strcspn(str, " \t\r\n")) {
156 /* handle --option='foo bar' strings */ 153 /* handle --option='foo bar' strings */
157 tmp = str + strcspn(str, "'") + 1; 154 char *tmp = str + strcspn(str, "'") + 1;
158 if (!strstr (tmp, "'")) 155 if (!strstr(tmp, "'")) {
159 return NULL; /* balanced? */ 156 return NULL; /* balanced? */
160 tmp += strcspn(tmp,"'") + 1; 157 }
158 tmp += strcspn(tmp, "'") + 1;
161 *tmp = 0; 159 *tmp = 0;
162 cmd = tmp + 1; 160 cmd = tmp + 1;
163 } else { 161 } else {
164 if (strpbrk (str, " \t\r\n")) { 162 if (strpbrk(str, " \t\r\n")) {
165 cmd = 1 + strpbrk (str, " \t\r\n"); 163 cmd = 1 + strpbrk(str, " \t\r\n");
166 str[strcspn (str, " \t\r\n")] = 0; 164 str[strcspn(str, " \t\r\n")] = 0;
167 } 165 } else {
168 else {
169 cmd = NULL; 166 cmd = NULL;
170 } 167 }
171 } 168 }
172 169
173 if (cmd && strlen (cmd) == strspn (cmd, " \t\r\n")) 170 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
174 cmd = NULL; 171 cmd = NULL;
172 }
175 173
176 argv[i++] = str; 174 argv[i++] = str;
177
178 } 175 }
179 argv[i] = NULL; 176 argv[i] = NULL;
180 177
181 long maxfd = mp_open_max(); 178 long maxfd = mp_open_max();
182 179
183 if (childpid == NULL) { /* first time through */ 180 if (childpid == NULL) { /* first time through */
184 if ((childpid = calloc ((size_t)maxfd, sizeof (pid_t))) == NULL) 181 if ((childpid = calloc((size_t)maxfd, sizeof(pid_t))) == NULL) {
185 return (NULL); 182 return (NULL);
183 }
186 } 184 }
187 185
188 if (child_stderr_array == NULL) { /* first time through */ 186 if (child_stderr_array == NULL) { /* first time through */
189 if ((child_stderr_array = calloc ((size_t)maxfd, sizeof (int))) == NULL) 187 if ((child_stderr_array = calloc((size_t)maxfd, sizeof(int))) == NULL) {
190 return (NULL); 188 return (NULL);
189 }
191 } 190 }
192 191
193 if (pipe (pfd) < 0) 192 int pfd[2];
194 return (NULL); /* errno set by pipe() */ 193 if (pipe(pfd) < 0) {
194 return (NULL); /* errno set by pipe() */
195 }
195 196
196 if (pipe (pfderr) < 0) 197 int pfderr[2];
197 return (NULL); /* errno set by pipe() */ 198 if (pipe(pfderr) < 0) {
199 return (NULL); /* errno set by pipe() */
200 }
198 201
199#ifdef REDHAT_SPOPEN_ERROR 202#ifdef REDHAT_SPOPEN_ERROR
200 if (signal (SIGCHLD, popen_sigchld_handler) == SIG_ERR) { 203 if (signal(SIGCHLD, popen_sigchld_handler) == SIG_ERR) {
201 usage4 (_("Cannot catch SIGCHLD")); 204 usage4(_("Cannot catch SIGCHLD"));
202 } 205 }
203#endif 206#endif
204 207
205 if ((pid = fork ()) < 0) 208 pid_t pid;
206 return (NULL); /* errno set by fork() */ 209 if ((pid = fork()) < 0) {
207 else if (pid == 0) { /* child */ 210 return (NULL); /* errno set by fork() */
208 close (pfd[0]); 211 }
212
213 if (pid == 0) { /* child */
214 close(pfd[0]);
209 if (pfd[1] != STDOUT_FILENO) { 215 if (pfd[1] != STDOUT_FILENO) {
210 dup2 (pfd[1], STDOUT_FILENO); 216 dup2(pfd[1], STDOUT_FILENO);
211 close (pfd[1]); 217 close(pfd[1]);
212 } 218 }
213 close (pfderr[0]); 219 close(pfderr[0]);
214 if (pfderr[1] != STDERR_FILENO) { 220 if (pfderr[1] != STDERR_FILENO) {
215 dup2 (pfderr[1], STDERR_FILENO); 221 dup2(pfderr[1], STDERR_FILENO);
216 close (pfderr[1]); 222 close(pfderr[1]);
217 } 223 }
218 /* close all descriptors in childpid[] */ 224 /* close all descriptors in childpid[] */
219 for (i = 0; i < maxfd; i++) 225 for (i = 0; i < maxfd; i++) {
220 if (childpid[i] > 0) 226 if (childpid[i] > 0) {
221 close (i); 227 close(i);
228 }
229 }
222 230
223 execve (argv[0], argv, env); 231 execve(argv[0], argv, env);
224 _exit (0); 232 _exit(0);
225 } 233 }
226 234
227 close (pfd[1]); /* parent */ 235 close(pfd[1]); /* parent */
228 if ((child_process = fdopen (pfd[0], "r")) == NULL) 236 if ((child_process = fdopen(pfd[0], "r")) == NULL) {
229 return (NULL); 237 return (NULL);
230 close (pfderr[1]); 238 }
239 close(pfderr[1]);
231 240
232 childpid[fileno (child_process)] = pid; /* remember child pid for this fd */ 241 childpid[fileno(child_process)] = pid; /* remember child pid for this fd */
233 child_stderr_array[fileno (child_process)] = pfderr[0]; /* remember STDERR */ 242 child_stderr_array[fileno(child_process)] = pfderr[0]; /* remember STDERR */
234 return (child_process); 243 return (child_process);
235} 244}
236 245
237int 246int spclose(FILE *fp) {
238spclose (FILE * fp) 247 if (childpid == NULL) {
239{ 248 return (1); /* popen() has never been called */
240 int fd, status; 249 }
241 pid_t pid;
242
243 if (childpid == NULL)
244 return (1); /* popen() has never been called */
245 250
246 fd = fileno (fp); 251 pid_t pid;
247 if ((pid = childpid[fd]) == 0) 252 int fd = fileno(fp);
248 return (1); /* fp wasn't opened by popen() */ 253 if ((pid = childpid[fd]) == 0) {
254 return (1); /* fp wasn't opened by popen() */
255 }
249 256
250 childpid[fd] = 0; 257 childpid[fd] = 0;
251 if (fclose (fp) == EOF) 258 if (fclose(fp) == EOF) {
252 return (1); 259 return (1);
260 }
253 261
254#ifdef REDHAT_SPOPEN_ERROR 262#ifdef REDHAT_SPOPEN_ERROR
255 while (!childtermd); /* wait until SIGCHLD */ 263 while (!childtermd)
264 ; /* wait until SIGCHLD */
256#endif 265#endif
257 266
258 while (waitpid (pid, &status, 0) < 0) 267 int status;
259 if (errno != EINTR) 268 while (waitpid(pid, &status, 0) < 0) {
260 return (1); /* error other than EINTR from waitpid() */ 269 if (errno != EINTR) {
270 return (1); /* error other than EINTR from waitpid() */
271 }
272 }
261 273
262 if (WIFEXITED (status)) 274 if (WIFEXITED(status)) {
263 return (WEXITSTATUS (status)); /* return child's termination status */ 275 return (WEXITSTATUS(status)); /* return child's termination status */
276 }
264 277
265 return (1); 278 return (1);
266} 279}
267 280
268#ifdef REDHAT_SPOPEN_ERROR 281#ifdef REDHAT_SPOPEN_ERROR
269void 282void popen_sigchld_handler(int signo) {
270popen_sigchld_handler (int signo) 283 if (signo == SIGCHLD) {
271{
272 if (signo == SIGCHLD)
273 childtermd = 1; 284 childtermd = 1;
285 }
274} 286}
275#endif 287#endif
276 288
277void 289void popen_timeout_alarm_handler(int signo) {
278popen_timeout_alarm_handler (int signo)
279{
280 int fh;
281 if (signo == SIGALRM) { 290 if (signo == SIGALRM) {
282 if (child_process != NULL) { 291 if (child_process != NULL) {
283 fh=fileno (child_process); 292 int fh = fileno(child_process);
284 if(fh >= 0){ 293 if (fh >= 0) {
285 kill (childpid[fh], SIGKILL); 294 kill(childpid[fh], SIGKILL);
286 } 295 }
287 printf (_("CRITICAL - Plugin timed out after %d seconds\n"), 296 printf(_("CRITICAL - Plugin timed out after %d seconds\n"), timeout_interval);
288 timeout_interval);
289 } else { 297 } else {
290 printf ("%s\n", _("CRITICAL - popen timeout received, but no child process")); 298 printf("%s\n", _("CRITICAL - popen timeout received, but no child process"));
291 } 299 }
292 exit (STATE_CRITICAL); 300 exit(STATE_CRITICAL);
293 } 301 }
294} 302}
diff --git a/plugins/popen.h b/plugins/popen.h
index 1ea69632..e318ce25 100644
--- a/plugins/popen.h
+++ b/plugins/popen.h
@@ -1,13 +1,13 @@
1/****************************************************************************** 1/******************************************************************************
2* 2 *
3* 3 *
4*****************************************************************************/ 4 *****************************************************************************/
5 5
6FILE *spopen (const char *); 6FILE *spopen(const char *);
7int spclose (FILE *); 7int spclose(FILE *);
8void popen_timeout_alarm_handler (int); 8void popen_timeout_alarm_handler(int);
9 9
10pid_t *childpid=NULL; 10pid_t *childpid = NULL;
11int *child_stderr_array=NULL; 11int *child_stderr_array = NULL;
12FILE *child_process=NULL; 12FILE *child_process = NULL;
13FILE *child_stderr=NULL; 13FILE *child_stderr = NULL;
diff --git a/plugins/runcmd.c b/plugins/runcmd.c
index ed49bb99..7c583b85 100644
--- a/plugins/runcmd.c
+++ b/plugins/runcmd.c
@@ -1,63 +1,64 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring run command utilities 3 * Monitoring run command utilities
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2005-2006 Monitoring Plugins Development Team 6 * Copyright (c) 2005-2024 Monitoring Plugins Development Team
7* 7 *
8* Description : 8 * Description :
9* 9 *
10* A simple interface to executing programs from other programs, using an 10 * A simple interface to executing programs from other programs, using an
11* optimized and safe popen()-like implementation. It is considered safe 11 * optimized and safe popen()-like implementation. It is considered safe
12* in that no shell needs to be spawned and the environment passed to the 12 * in that no shell needs to be spawned and the environment passed to the
13* execve()'d program is essentially empty. 13 * execve()'d program is essentially empty.
14* 14 *
15* The code in this file is a derivative of popen.c which in turn was taken 15 * The code in this file is a derivative of popen.c which in turn was taken
16* from "Advanced Programming for the Unix Environment" by W. Richard Stevens. 16 * from "Advanced Programming for the Unix Environment" by W. Richard Stevens.
17* 17 *
18* Care has been taken to make sure the functions are async-safe. The one 18 * Care has been taken to make sure the functions are async-safe. The one
19* function which isn't is np_runcmd_init() which it doesn't make sense to 19 * function which isn't is np_runcmd_init() which it doesn't make sense to
20* call twice anyway, so the api as a whole should be considered async-safe. 20 * call twice anyway, so the api as a whole should be considered async-safe.
21* 21 *
22* 22 *
23* This program is free software: you can redistribute it and/or modify 23 * This program is free software: you can redistribute it and/or modify
24* it under the terms of the GNU General Public License as published by 24 * it under the terms of the GNU General Public License as published by
25* the Free Software Foundation, either version 3 of the License, or 25 * the Free Software Foundation, either version 3 of the License, or
26* (at your option) any later version. 26 * (at your option) any later version.
27* 27 *
28* This program is distributed in the hope that it will be useful, 28 * This program is distributed in the hope that it will be useful,
29* but WITHOUT ANY WARRANTY; without even the implied warranty of 29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31* GNU General Public License for more details. 31 * GNU General Public License for more details.
32* 32 *
33* You should have received a copy of the GNU General Public License 33 * You should have received a copy of the GNU General Public License
34* along with this program. If not, see <http://www.gnu.org/licenses/>. 34 * along with this program. If not, see <http://www.gnu.org/licenses/>.
35* 35 *
36* 36 *
37*****************************************************************************/ 37 *****************************************************************************/
38 38
39#define NAGIOSPLUG_API_C 1 39#define NAGIOSPLUG_API_C 1
40 40
41/** includes **/ 41/** includes **/
42#include "runcmd.h" 42#include "runcmd.h"
43#include "../lib/monitoringplug.h"
43#ifdef HAVE_SYS_WAIT_H 44#ifdef HAVE_SYS_WAIT_H
44# include <sys/wait.h> 45# include <sys/wait.h>
45#endif 46#endif
46 47
47#include "./utils.h" 48#include "./utils.h"
48 49
49/** macros **/ 50/** macros **/
50#ifndef WEXITSTATUS 51#ifndef WEXITSTATUS
51# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 52# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
52#endif 53#endif
53 54
54#ifndef WIFEXITED 55#ifndef WIFEXITED
55# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 56# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
56#endif 57#endif
57 58
58/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ 59/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
59#if defined(SIG_IGN) && !defined(SIG_ERR) 60#if defined(SIG_IGN) && !defined(SIG_ERR)
60# define SIG_ERR ((Sigfunc *)-1) 61# define SIG_ERR ((Sigfunc *)-1)
61#endif 62#endif
62 63
63#include "../lib/maxfd.h" 64#include "../lib/maxfd.h"
@@ -72,33 +73,27 @@
72static pid_t *np_pids = NULL; 73static pid_t *np_pids = NULL;
73 74
74/** prototypes **/ 75/** prototypes **/
75static int np_runcmd_open(const char *, int *, int *) 76static int np_runcmd_open(const char *, int *, int *) __attribute__((__nonnull__(1, 2, 3)));
76 __attribute__((__nonnull__(1, 2, 3)));
77 77
78static int np_fetch_output(int, output *, int) 78static int np_fetch_output(int, output *, int) __attribute__((__nonnull__(2)));
79 __attribute__((__nonnull__(2)));
80 79
81static int np_runcmd_close(int); 80static int np_runcmd_close(int);
82 81
83/* prototype imported from utils.h */ 82/* prototype imported from utils.h */
84extern void die (int, const char *, ...) 83extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
85 __attribute__((__noreturn__,__format__(__printf__, 2, 3)));
86
87 84
88/* this function is NOT async-safe. It is exported so multithreaded 85/* this function is NOT async-safe. It is exported so multithreaded
89 * plugins (or other apps) can call it prior to running any commands 86 * plugins (or other apps) can call it prior to running any commands
90 * through this api and thus achieve async-safeness throughout the api */ 87 * through this api and thus achieve async-safeness throughout the api */
91void np_runcmd_init(void) 88void np_runcmd_init(void) {
92{ 89 long maxfd = mp_open_max();
93 long maxfd = mp_open_max(); 90 if (!np_pids) {
94 if(!np_pids) np_pids = calloc(maxfd, sizeof(pid_t)); 91 np_pids = calloc(maxfd, sizeof(pid_t));
92 }
95} 93}
96 94
97
98/* Start running a command */ 95/* Start running a command */
99static int 96static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
100np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
101{
102 char *env[2]; 97 char *env[2];
103 char *cmd = NULL; 98 char *cmd = NULL;
104 char **argv = NULL; 99 char **argv = NULL;
@@ -112,7 +107,9 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
112 107
113 int i = 0; 108 int i = 0;
114 109
115 if(!np_pids) NP_RUNCMD_INIT; 110 if (!np_pids) {
111 NP_RUNCMD_INIT;
112 }
116 113
117 env[0] = strdup("LC_ALL=C"); 114 env[0] = strdup("LC_ALL=C");
118 env[1] = NULL; 115 env[1] = NULL;
@@ -120,86 +117,95 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
120 /* make copy of command string so strtok() doesn't silently modify it */ 117 /* make copy of command string so strtok() doesn't silently modify it */
121 /* (the calling program may want to access it later) */ 118 /* (the calling program may want to access it later) */
122 cmdlen = strlen(cmdstring); 119 cmdlen = strlen(cmdstring);
123 if((cmd = malloc(cmdlen + 1)) == NULL) return -1; 120 if ((cmd = malloc(cmdlen + 1)) == NULL) {
121 return -1;
122 }
124 memcpy(cmd, cmdstring, cmdlen); 123 memcpy(cmd, cmdstring, cmdlen);
125 cmd[cmdlen] = '\0'; 124 cmd[cmdlen] = '\0';
126 125
127 /* This is not a shell, so we don't handle "???" */ 126 /* This is not a shell, so we don't handle "???" */
128 if (strstr (cmdstring, "\"")) return -1; 127 if (strstr(cmdstring, "\"")) {
128 return -1;
129 }
129 130
130 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ 131 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
131 if (strstr (cmdstring, " ' ") || strstr (cmdstring, "'''")) 132 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
132 return -1; 133 return -1;
134 }
133 135
134 /* each arg must be whitespace-separated, so args can be a maximum 136 /* each arg must be whitespace-separated, so args can be a maximum
135 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ 137 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */
136 argc = (cmdlen >> 1) + 2; 138 argc = (cmdlen >> 1) + 2;
137 argv = calloc(sizeof(char *), argc); 139 argv = calloc(argc, sizeof(char *));
138 140
139 if (argv == NULL) { 141 if (argv == NULL) {
140 printf ("%s\n", _("Could not malloc argv array in popen()")); 142 printf("%s\n", _("Could not malloc argv array in popen()"));
141 return -1; 143 return -1;
142 } 144 }
143 145
144 /* get command arguments (stupidly, but fairly quickly) */ 146 /* get command arguments (stupidly, but fairly quickly) */
145 while (cmd) { 147 while (cmd) {
146 str = cmd; 148 str = cmd;
147 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */ 149 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
148 150
149 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */ 151 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
150 str++; 152 str++;
151 if (!strstr (str, "'")) return -1; /* balanced? */ 153 if (!strstr(str, "'")) {
152 cmd = 1 + strstr (str, "'"); 154 return -1; /* balanced? */
153 str[strcspn (str, "'")] = 0;
154 }
155 else {
156 if (strpbrk (str, " \t\r\n")) {
157 cmd = 1 + strpbrk (str, " \t\r\n");
158 str[strcspn (str, " \t\r\n")] = 0;
159 } 155 }
160 else { 156 cmd = 1 + strstr(str, "'");
157 str[strcspn(str, "'")] = 0;
158 } else {
159 if (strpbrk(str, " \t\r\n")) {
160 cmd = 1 + strpbrk(str, " \t\r\n");
161 str[strcspn(str, " \t\r\n")] = 0;
162 } else {
161 cmd = NULL; 163 cmd = NULL;
162 } 164 }
163 } 165 }
164 166
165 if (cmd && strlen (cmd) == strspn (cmd, " \t\r\n")) 167 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
166 cmd = NULL; 168 cmd = NULL;
169 }
167 170
168 argv[i++] = str; 171 argv[i++] = str;
169 } 172 }
170 173
171 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) 174 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) {
172 return -1; /* errno set by the failing function */ 175 return -1; /* errno set by the failing function */
176 }
173 177
174 /* child runs exceve() and _exit. */ 178 /* child runs exceve() and _exit. */
175 if (pid == 0) { 179 if (pid == 0) {
176#ifdef RLIMIT_CORE 180#ifdef RLIMIT_CORE
177 /* the program we execve shouldn't leave core files */ 181 /* the program we execve shouldn't leave core files */
178 getrlimit (RLIMIT_CORE, &limit); 182 getrlimit(RLIMIT_CORE, &limit);
179 limit.rlim_cur = 0; 183 limit.rlim_cur = 0;
180 setrlimit (RLIMIT_CORE, &limit); 184 setrlimit(RLIMIT_CORE, &limit);
181#endif 185#endif
182 close (pfd[0]); 186 close(pfd[0]);
183 if (pfd[1] != STDOUT_FILENO) { 187 if (pfd[1] != STDOUT_FILENO) {
184 dup2 (pfd[1], STDOUT_FILENO); 188 dup2(pfd[1], STDOUT_FILENO);
185 close (pfd[1]); 189 close(pfd[1]);
186 } 190 }
187 close (pfderr[0]); 191 close(pfderr[0]);
188 if (pfderr[1] != STDERR_FILENO) { 192 if (pfderr[1] != STDERR_FILENO) {
189 dup2 (pfderr[1], STDERR_FILENO); 193 dup2(pfderr[1], STDERR_FILENO);
190 close (pfderr[1]); 194 close(pfderr[1]);
191 } 195 }
192 196
193 /* close all descriptors in np_pids[] 197 /* close all descriptors in np_pids[]
194 * This is executed in a separate address space (pure child), 198 * This is executed in a separate address space (pure child),
195 * so we don't have to worry about async safety */ 199 * so we don't have to worry about async safety */
196 long maxfd = mp_open_max(); 200 long maxfd = mp_open_max();
197 for (i = 0; i < maxfd; i++) 201 for (i = 0; i < maxfd; i++) {
198 if(np_pids[i] > 0) 202 if (np_pids[i] > 0) {
199 close (i); 203 close(i);
204 }
205 }
200 206
201 execve (argv[0], argv, env); 207 execve(argv[0], argv, env);
202 _exit (STATE_UNKNOWN); 208 _exit(STATE_UNKNOWN);
203 } 209 }
204 210
205 /* parent picks up execution here */ 211 /* parent picks up execution here */
@@ -213,49 +219,51 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
213 return pfd[0]; 219 return pfd[0];
214} 220}
215 221
216 222static int np_runcmd_close(int fd) {
217static int
218np_runcmd_close(int fd)
219{
220 int status; 223 int status;
221 pid_t pid; 224 pid_t pid;
222 225
223 /* make sure this fd was opened by popen() */ 226 /* make sure this fd was opened by popen() */
224 long maxfd = mp_open_max(); 227 long maxfd = mp_open_max();
225 if(fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) 228 if (fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) {
226 return -1; 229 return -1;
230 }
227 231
228 np_pids[fd] = 0; 232 np_pids[fd] = 0;
229 if (close (fd) == -1) return -1; 233 if (close(fd) == -1) {
234 return -1;
235 }
230 236
231 /* EINTR is ok (sort of), everything else is bad */ 237 /* EINTR is ok (sort of), everything else is bad */
232 while (waitpid (pid, &status, 0) < 0) 238 while (waitpid(pid, &status, 0) < 0) {
233 if (errno != EINTR) return -1; 239 if (errno != EINTR) {
240 return -1;
241 }
242 }
234 243
235 /* return child's termination status */ 244 /* return child's termination status */
236 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; 245 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
237} 246}
238 247
248void runcmd_timeout_alarm_handler(int signo) {
239 249
240void 250 if (signo == SIGALRM) {
241runcmd_timeout_alarm_handler (int signo)
242{
243
244 if (signo == SIGALRM)
245 puts(_("CRITICAL - Plugin timed out while executing system call")); 251 puts(_("CRITICAL - Plugin timed out while executing system call"));
252 }
246 253
247 long maxfd = mp_open_max(); 254 long maxfd = mp_open_max();
248 if(np_pids) for(long int i = 0; i < maxfd; i++) { 255 if (np_pids) {
249 if(np_pids[i] != 0) kill(np_pids[i], SIGKILL); 256 for (long int i = 0; i < maxfd; i++) {
257 if (np_pids[i] != 0) {
258 kill(np_pids[i], SIGKILL);
259 }
260 }
250 } 261 }
251 262
252 exit (STATE_CRITICAL); 263 exit(STATE_CRITICAL);
253} 264}
254 265
255 266static int np_fetch_output(int fd, output *op, int flags) {
256static int
257np_fetch_output(int fd, output *op, int flags)
258{
259 size_t len = 0, i = 0, lineno = 0; 267 size_t len = 0, i = 0, lineno = 0;
260 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ 268 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
261 char *buf = NULL; 269 char *buf = NULL;
@@ -264,7 +272,7 @@ np_fetch_output(int fd, output *op, int flags)
264 272
265 op->buf = NULL; 273 op->buf = NULL;
266 op->buflen = 0; 274 op->buflen = 0;
267 while((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) { 275 while ((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) {
268 len = (size_t)ret; 276 len = (size_t)ret;
269 op->buf = realloc(op->buf, op->buflen + len + 1); 277 op->buf = realloc(op->buf, op->buflen + len + 1);
270 memcpy(op->buf + op->buflen, tmpbuf, len); 278 memcpy(op->buf + op->buflen, tmpbuf, len);
@@ -272,33 +280,35 @@ np_fetch_output(int fd, output *op, int flags)
272 i++; 280 i++;
273 } 281 }
274 282
275 if(ret < 0) { 283 if (ret < 0) {
276 printf("read() returned %d: %s\n", ret, strerror(errno)); 284 printf("read() returned %d: %s\n", ret, strerror(errno));
277 return ret; 285 return ret;
278 } 286 }
279 287
280 /* some plugins may want to keep output unbroken, and some commands 288 /* some plugins may want to keep output unbroken, and some commands
281 * will yield no output, so return here for those */ 289 * will yield no output, so return here for those */
282 if(flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) 290 if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) {
283 return op->buflen; 291 return op->buflen;
292 }
284 293
285 /* and some may want both */ 294 /* and some may want both */
286 if(flags & RUNCMD_NO_ASSOC) { 295 if (flags & RUNCMD_NO_ASSOC) {
287 buf = malloc(op->buflen); 296 buf = malloc(op->buflen);
288 memcpy(buf, op->buf, op->buflen); 297 memcpy(buf, op->buf, op->buflen);
298 } else {
299 buf = op->buf;
289 } 300 }
290 else buf = op->buf;
291 301
292 op->line = NULL; 302 op->line = NULL;
293 op->lens = NULL; 303 op->lens = NULL;
294 i = 0; 304 i = 0;
295 while(i < op->buflen) { 305 while (i < op->buflen) {
296 /* make sure we have enough memory */ 306 /* make sure we have enough memory */
297 if(lineno >= ary_size) { 307 if (lineno >= ary_size) {
298 /* ary_size must never be zero */ 308 /* ary_size must never be zero */
299 do { 309 do {
300 ary_size = op->buflen >> --rsf; 310 ary_size = op->buflen >> --rsf;
301 } while(!ary_size); 311 } while (!ary_size);
302 312
303 op->line = realloc(op->line, ary_size * sizeof(char *)); 313 op->line = realloc(op->line, ary_size * sizeof(char *));
304 op->lens = realloc(op->lens, ary_size * sizeof(size_t)); 314 op->lens = realloc(op->lens, ary_size * sizeof(size_t));
@@ -308,7 +318,9 @@ np_fetch_output(int fd, output *op, int flags)
308 op->line[lineno] = &buf[i]; 318 op->line[lineno] = &buf[i];
309 319
310 /* hop to next newline or end of buffer */ 320 /* hop to next newline or end of buffer */
311 while(buf[i] != '\n' && i < op->buflen) i++; 321 while (buf[i] != '\n' && i < op->buflen) {
322 i++;
323 }
312 buf[i] = '\0'; 324 buf[i] = '\0';
313 325
314 /* calculate the string length using pointer difference */ 326 /* calculate the string length using pointer difference */
@@ -321,21 +333,27 @@ np_fetch_output(int fd, output *op, int flags)
321 return lineno; 333 return lineno;
322} 334}
323 335
324 336int np_runcmd(const char *cmd, output *out, output *err, int flags) {
325int
326np_runcmd(const char *cmd, output *out, output *err, int flags)
327{
328 int fd, pfd_out[2], pfd_err[2]; 337 int fd, pfd_out[2], pfd_err[2];
329 338
330 /* initialize the structs */ 339 /* initialize the structs */
331 if(out) memset(out, 0, sizeof(output)); 340 if (out) {
332 if(err) memset(err, 0, sizeof(output)); 341 memset(out, 0, sizeof(output));
342 }
343 if (err) {
344 memset(err, 0, sizeof(output));
345 }
333 346
334 if((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) 347 if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) {
335 die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 348 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
349 }
336 350
337 if(out) out->lines = np_fetch_output(pfd_out[0], out, flags); 351 if (out) {
338 if(err) err->lines = np_fetch_output(pfd_err[0], err, flags); 352 out->lines = np_fetch_output(pfd_out[0], out, flags);
353 }
354 if (err) {
355 err->lines = np_fetch_output(pfd_err[0], err, flags);
356 }
339 357
340 return np_runcmd_close(fd); 358 return np_runcmd_close(fd);
341} 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 6bc0ba81..0e6d7525 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -1,42 +1,43 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins SSL utilities 3 * Monitoring Plugins SSL utilities
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2005-2010 Monitoring Plugins Development Team 6 * Copyright (c) 2005-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains common functions for plugins that require SSL. 10 * This file contains common functions for plugins that require SSL.
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 13 * This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14 * it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 23 * You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29#include "output.h"
29#define MAX_CN_LENGTH 256 30#define MAX_CN_LENGTH 256
30#include "common.h" 31#include "common.h"
31#include "netutils.h" 32#include "netutils.h"
33#include "../lib/monitoringplug.h"
34#include "states.h"
32 35
33#ifdef HAVE_SSL 36#ifdef HAVE_SSL
34static SSL_CTX *ctx=NULL; 37static SSL_CTX *ctx = NULL;
35static SSL *s=NULL; 38static SSL *s = NULL;
36 39
37int np_net_ssl_init(int sd) { 40int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); }
38 return np_net_ssl_init_with_hostname(sd, NULL);
39}
40 41
41int np_net_ssl_init_with_hostname(int sd, char *host_name) { 42int np_net_ssl_init_with_hostname(int sd, char *host_name) {
42 return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); 43 return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0);
@@ -46,7 +47,8 @@ int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int versi
46 return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL); 47 return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL);
47} 48}
48 49
49int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey) { 50int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert,
51 char *privkey) {
50 long options = 0; 52 long options = 0;
51 53
52 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) { 54 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) {
@@ -59,272 +61,398 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
59 printf("%s\n", _("UNKNOWN - SSL protocol version 2 is not supported by your SSL library.")); 61 printf("%s\n", _("UNKNOWN - SSL protocol version 2 is not supported by your SSL library."));
60 return STATE_UNKNOWN; 62 return STATE_UNKNOWN;
61 case MP_SSLv3: /* SSLv3 protocol */ 63 case MP_SSLv3: /* SSLv3 protocol */
62#if defined(OPENSSL_NO_SSL3) 64# if defined(OPENSSL_NO_SSL3)
63 printf("%s\n", _("UNKNOWN - SSL protocol version 3 is not supported by your SSL library.")); 65 printf("%s\n", _("UNKNOWN - SSL protocol version 3 is not supported by your SSL library."));
64 return STATE_UNKNOWN; 66 return STATE_UNKNOWN;
65#else 67# else
66 SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); 68 SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
67 SSL_CTX_set_max_proto_version(ctx, SSL3_VERSION); 69 SSL_CTX_set_max_proto_version(ctx, SSL3_VERSION);
68 break; 70 break;
69#endif 71# endif
70 case MP_TLSv1: /* TLSv1 protocol */ 72 case MP_TLSv1: /* TLSv1 protocol */
71#if defined(OPENSSL_NO_TLS1) 73# if defined(OPENSSL_NO_TLS1)
72 printf("%s\n", _("UNKNOWN - TLS protocol version 1 is not supported by your SSL library.")); 74 printf("%s\n", _("UNKNOWN - TLS protocol version 1 is not supported by your SSL library."));
73 return STATE_UNKNOWN; 75 return STATE_UNKNOWN;
74#else 76# else
75 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION); 77 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
76 SSL_CTX_set_max_proto_version(ctx, TLS1_VERSION); 78 SSL_CTX_set_max_proto_version(ctx, TLS1_VERSION);
77 break; 79 break;
78#endif 80# endif
79 case MP_TLSv1_1: /* TLSv1.1 protocol */ 81 case MP_TLSv1_1: /* TLSv1.1 protocol */
80#if !defined(SSL_OP_NO_TLSv1_1) 82# if !defined(SSL_OP_NO_TLSv1_1)
81 printf("%s\n", _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library.")); 83 printf("%s\n",
84 _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library."));
82 return STATE_UNKNOWN; 85 return STATE_UNKNOWN;
83#else 86# else
84 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); 87 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
85 SSL_CTX_set_max_proto_version(ctx, TLS1_1_VERSION); 88 SSL_CTX_set_max_proto_version(ctx, TLS1_1_VERSION);
86 break; 89 break;
87#endif 90# endif
88 case MP_TLSv1_2: /* TLSv1.2 protocol */ 91 case MP_TLSv1_2: /* TLSv1.2 protocol */
89#if !defined(SSL_OP_NO_TLSv1_2) 92# if !defined(SSL_OP_NO_TLSv1_2)
90 printf("%s\n", _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library.")); 93 printf("%s\n",
94 _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library."));
91 return STATE_UNKNOWN; 95 return STATE_UNKNOWN;
92#else 96# else
93 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); 97 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
94 SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION); 98 SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
95 break; 99 break;
96#endif 100# endif
97 case MP_TLSv1_2_OR_NEWER: 101 case MP_TLSv1_2_OR_NEWER:
98#if !defined(SSL_OP_NO_TLSv1_1) 102# if !defined(SSL_OP_NO_TLSv1_1)
99 printf("%s\n", _("UNKNOWN - Disabling TLSv1.1 is not supported by your SSL library.")); 103 printf("%s\n", _("UNKNOWN - Disabling TLSv1.1 is not supported by your SSL library."));
100 return STATE_UNKNOWN; 104 return STATE_UNKNOWN;
101#else 105# else
102 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); 106 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
103 break; 107 break;
104#endif 108# endif
105 case MP_TLSv1_1_OR_NEWER: 109 case MP_TLSv1_1_OR_NEWER:
106#if !defined(SSL_OP_NO_TLSv1) 110# if !defined(SSL_OP_NO_TLSv1)
107 printf("%s\n", _("UNKNOWN - Disabling TLSv1 is not supported by your SSL library.")); 111 printf("%s\n", _("UNKNOWN - Disabling TLSv1 is not supported by your SSL library."));
108 return STATE_UNKNOWN; 112 return STATE_UNKNOWN;
109#else 113# else
110 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); 114 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
111 break; 115 break;
112#endif 116# endif
113 case MP_TLSv1_OR_NEWER: 117 case MP_TLSv1_OR_NEWER:
114#if defined(SSL_OP_NO_SSLv3) 118# if defined(SSL_OP_NO_SSLv3)
115 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION); 119 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
116 break; 120 break;
117#endif 121# endif
118 case MP_SSLv3_OR_NEWER: 122 case MP_SSLv3_OR_NEWER:
119#if defined(SSL_OP_NO_SSLv2) 123# if defined(SSL_OP_NO_SSLv2)
120 SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); 124 SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
121 break; 125 break;
122#endif 126# endif
123 } 127 }
124 128
125 if (cert && privkey) { 129 if (cert && privkey) {
126#ifdef USE_OPENSSL 130# ifdef USE_OPENSSL
127 if (!SSL_CTX_use_certificate_chain_file(ctx, cert)) { 131 if (!SSL_CTX_use_certificate_chain_file(ctx, cert)) {
128#elif USE_GNUTLS 132# elif USE_GNUTLS
129 if (!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM)) { 133 if (!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM)) {
130#else 134# else
131#error Unported for unknown SSL library 135# error Unported for unknown SSL library
132#endif 136# endif
133 printf ("%s\n", _("CRITICAL - Unable to open certificate chain file!\n")); 137 printf("%s\n", _("CRITICAL - Unable to open certificate chain file!\n"));
134 return STATE_CRITICAL; 138 return STATE_CRITICAL;
135 } 139 }
136 SSL_CTX_use_PrivateKey_file(ctx, privkey, SSL_FILETYPE_PEM); 140 SSL_CTX_use_PrivateKey_file(ctx, privkey, SSL_FILETYPE_PEM);
137#ifdef USE_OPENSSL 141# ifdef USE_OPENSSL
138 if (!SSL_CTX_check_private_key(ctx)) { 142 if (!SSL_CTX_check_private_key(ctx)) {
139 printf ("%s\n", _("CRITICAL - Private key does not seem to match certificate!\n")); 143 printf("%s\n", _("CRITICAL - Private key does not seem to match certificate!\n"));
140 return STATE_CRITICAL; 144 return STATE_CRITICAL;
141 } 145 }
142#endif 146# endif
143 } 147 }
144#ifdef SSL_OP_NO_TICKET 148# ifdef SSL_OP_NO_TICKET
145 options |= SSL_OP_NO_TICKET; 149 options |= SSL_OP_NO_TICKET;
146#endif 150# endif
147 SSL_CTX_set_options(ctx, options); 151 SSL_CTX_set_options(ctx, options);
148 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); 152 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
149 if ((s = SSL_new(ctx)) != NULL) { 153 if ((s = SSL_new(ctx)) != NULL) {
150#ifdef SSL_set_tlsext_host_name 154# ifdef SSL_set_tlsext_host_name
151 if (host_name != NULL) 155 if (host_name != NULL) {
152 SSL_set_tlsext_host_name(s, host_name); 156 SSL_set_tlsext_host_name(s, host_name);
153#endif 157 }
158# endif
154 SSL_set_fd(s, sd); 159 SSL_set_fd(s, sd);
155 if (SSL_connect(s) == 1) { 160 if (SSL_connect(s) == 1) {
156 return OK; 161 return OK;
157 } else { 162 } else {
158 printf("%s\n", _("CRITICAL - Cannot make SSL connection.")); 163 printf("%s\n", _("CRITICAL - Cannot make SSL connection."));
159# ifdef USE_OPENSSL /* XXX look into ERR_error_string */ 164# ifdef USE_OPENSSL /* XXX look into ERR_error_string */
160 ERR_print_errors_fp(stdout); 165 ERR_print_errors_fp(stdout);
161# endif /* USE_OPENSSL */ 166# endif /* USE_OPENSSL */
162 } 167 }
163 } else { 168 } else {
164 printf("%s\n", _("CRITICAL - Cannot initiate SSL handshake.")); 169 printf("%s\n", _("CRITICAL - Cannot initiate SSL handshake."));
165 } 170 }
166 return STATE_CRITICAL; 171 return STATE_CRITICAL;
167} 172}
168 173
169void np_net_ssl_cleanup() { 174void np_net_ssl_cleanup() {
170 if (s) { 175 if (s) {
171#ifdef SSL_set_tlsext_host_name 176# ifdef SSL_set_tlsext_host_name
172 SSL_set_tlsext_host_name(s, NULL); 177 SSL_set_tlsext_host_name(s, NULL);
173#endif 178# endif
174 SSL_shutdown(s); 179 SSL_shutdown(s);
175 SSL_free(s); 180 SSL_free(s);
176 if (ctx) { 181 if (ctx) {
177 SSL_CTX_free(ctx); 182 SSL_CTX_free(ctx);
178 ctx=NULL; 183 ctx = NULL;
179 } 184 }
180 s=NULL; 185 s = NULL;
181 } 186 }
182} 187}
183 188
184int np_net_ssl_write(const void *buf, int num) { 189int np_net_ssl_write(const void *buf, int num) { return SSL_write(s, buf, num); }
185 return SSL_write(s, buf, num);
186}
187
188int np_net_ssl_read(void *buf, int num) {
189 return SSL_read(s, buf, num);
190}
191 190
192int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit){ 191int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); }
193# ifdef USE_OPENSSL
194 X509_NAME *subj=NULL;
195 char timestamp[50] = "";
196 char cn[MAX_CN_LENGTH]= "";
197 char *tz;
198
199 int cnlen =-1;
200 int status=STATE_UNKNOWN;
201
202 ASN1_STRING *tm;
203 int offset;
204 struct tm stamp;
205 float time_left;
206 int days_left;
207 int time_remaining;
208 time_t tm_t;
209 192
193mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
194 int days_till_exp_crit) {
195# ifdef USE_OPENSSL
210 if (!certificate) { 196 if (!certificate) {
211 printf("%s\n",_("CRITICAL - Cannot retrieve server certificate.")); 197 printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
212 return STATE_CRITICAL; 198 return STATE_CRITICAL;
213 } 199 }
214 200
215 /* Extract CN from certificate subject */ 201 /* Extract CN from certificate subject */
216 subj=X509_get_subject_name(certificate); 202 X509_NAME *subj = X509_get_subject_name(certificate);
217 203
218 if (!subj) { 204 if (!subj) {
219 printf("%s\n",_("CRITICAL - Cannot retrieve certificate subject.")); 205 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
220 return STATE_CRITICAL; 206 return STATE_CRITICAL;
221 } 207 }
222 cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); 208
223 if (cnlen == -1) 209 char cn[MAX_CN_LENGTH] = "";
210 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn));
211 if (cnlen == -1) {
224 strcpy(cn, _("Unknown CN")); 212 strcpy(cn, _("Unknown CN"));
213 }
225 214
226 /* Retrieve timestamp of certificate */ 215 /* Retrieve timestamp of certificate */
227 tm = X509_get_notAfter(certificate); 216 ASN1_STRING *tm = X509_get_notAfter(certificate);
228 217
218 int offset = 0;
219 struct tm stamp = {};
229 /* Generate tm structure to process timestamp */ 220 /* Generate tm structure to process timestamp */
230 if (tm->type == V_ASN1_UTCTIME) { 221 if (tm->type == V_ASN1_UTCTIME) {
231 if (tm->length < 10) { 222 if (tm->length < 10) {
232 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 223 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
233 return STATE_CRITICAL; 224 return STATE_CRITICAL;
234 } else {
235 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
236 if (stamp.tm_year < 50)
237 stamp.tm_year += 100;
238 offset = 0;
239 } 225 }
226 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
227 if (stamp.tm_year < 50) {
228 stamp.tm_year += 100;
229 }
230 offset = 0;
231
240 } else { 232 } else {
241 if (tm->length < 12) { 233 if (tm->length < 12) {
242 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 234 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
243 return STATE_CRITICAL; 235 return STATE_CRITICAL;
244 } else {
245 stamp.tm_year =
246 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
247 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
248 stamp.tm_year -= 1900;
249 offset = 2;
250 } 236 }
237 stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
238 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
239 stamp.tm_year -= 1900;
240 offset = 2;
251 } 241 }
252 stamp.tm_mon = 242 stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
253 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; 243 stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
254 stamp.tm_mday = 244 stamp.tm_hour = (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
255 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); 245 stamp.tm_min = (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
256 stamp.tm_hour = 246 stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0');
257 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
258 stamp.tm_min =
259 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
260 stamp.tm_sec =
261 (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0');
262 stamp.tm_isdst = -1; 247 stamp.tm_isdst = -1;
263 248
264 tm_t = timegm(&stamp); 249 time_t tm_t = timegm(&stamp);
265 time_left = difftime(tm_t, time(NULL)); 250 float time_left = difftime(tm_t, time(NULL));
266 days_left = time_left / 86400; 251 int days_left = time_left / 86400;
267 tz = getenv("TZ"); 252 char *tz = getenv("TZ");
268 setenv("TZ", "GMT", 1); 253 setenv("TZ", "GMT", 1);
269 tzset(); 254 tzset();
255
256 char timestamp[50] = "";
270 strftime(timestamp, 50, "%c %z", localtime(&tm_t)); 257 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
271 if (tz) 258 if (tz) {
272 setenv("TZ", tz, 1); 259 setenv("TZ", tz, 1);
273 else 260 } else {
274 unsetenv("TZ"); 261 unsetenv("TZ");
262 }
263
275 tzset(); 264 tzset();
276 265
266 int time_remaining;
267 mp_state_enum status = STATE_UNKNOWN;
277 if (days_left > 0 && days_left <= days_till_exp_warn) { 268 if (days_left > 0 && days_left <= days_till_exp_warn) {
278 printf (_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", cn, days_left, timestamp); 269 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
279 if (days_left > days_till_exp_crit) 270 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, days_left, timestamp);
271 if (days_left > days_till_exp_crit) {
280 status = STATE_WARNING; 272 status = STATE_WARNING;
281 else 273 } else {
282 status = STATE_CRITICAL; 274 status = STATE_CRITICAL;
275 }
283 } else if (days_left == 0 && time_left > 0) { 276 } else if (days_left == 0 && time_left > 0) {
284 if (time_left >= 3600) 277 if (time_left >= 3600) {
285 time_remaining = (int) time_left / 3600; 278 time_remaining = (int)time_left / 3600;
286 else 279 } else {
287 time_remaining = (int) time_left / 60; 280 time_remaining = (int)time_left / 60;
281 }
288 282
289 printf (_("%s - Certificate '%s' expires in %u %s (%s)\n"), 283 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
290 (days_left>days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining, 284 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining,
291 time_left >= 3600 ? "hours" : "minutes", timestamp); 285 time_left >= 3600 ? "hours" : "minutes", timestamp);
292 286
293 if ( days_left > days_till_exp_crit) 287 if (days_left > days_till_exp_crit) {
294 status = STATE_WARNING; 288 status = STATE_WARNING;
295 else 289 } else {
296 status = STATE_CRITICAL; 290 status = STATE_CRITICAL;
291 }
297 } else if (time_left < 0) { 292 } else if (time_left < 0) {
298 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp); 293 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp);
299 status=STATE_CRITICAL; 294 status = STATE_CRITICAL;
300 } else if (days_left == 0) { 295 } else if (days_left == 0) {
301 printf (_("%s - Certificate '%s' just expired (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", cn, timestamp); 296 printf(_("%s - Certificate '%s' just expired (%s).\n"),
302 if (days_left > days_till_exp_crit) 297 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, timestamp);
298 if (days_left > days_till_exp_crit) {
303 status = STATE_WARNING; 299 status = STATE_WARNING;
304 else 300 } else {
305 status = STATE_CRITICAL; 301 status = STATE_CRITICAL;
302 }
306 } else { 303 } else {
307 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp); 304 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp);
308 status = STATE_OK; 305 status = STATE_OK;
309 } 306 }
310 X509_free(certificate); 307 X509_free(certificate);
311 return status; 308 return status;
312# else /* ifndef USE_OPENSSL */ 309# else /* ifndef USE_OPENSSL */
313 printf("%s\n", _("WARNING - Plugin does not support checking certificates.")); 310 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
314 return STATE_WARNING; 311 return STATE_WARNING;
315# endif /* USE_OPENSSL */ 312# endif /* USE_OPENSSL */
316} 313}
317 314
318int 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) {
319# ifdef USE_OPENSSL 316# ifdef USE_OPENSSL
320 X509 *certificate = NULL; 317 X509 *certificate = NULL;
321 certificate=SSL_get_peer_certificate(s); 318 certificate = SSL_get_peer_certificate(s);
322 return(np_net_ssl_check_certificate(certificate, days_till_exp_warn, days_till_exp_crit)); 319 return (np_net_ssl_check_certificate(certificate, days_till_exp_warn, days_till_exp_crit));
323# else /* ifndef USE_OPENSSL */ 320# else /* ifndef USE_OPENSSL */
324 printf("%s\n", _("WARNING - Plugin does not support checking certificates.")); 321 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
325 return STATE_WARNING; 322 return STATE_WARNING;
326# endif /* USE_OPENSSL */ 323# endif /* USE_OPENSSL */
327} 324}
328 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 }
329 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}
330#endif /* HAVE_SSL */ 458#endif /* HAVE_SSL */
diff --git a/plugins/t/check_apt.t b/plugins/t/check_apt.t
index 430eb53e..736bc2f2 100644
--- a/plugins/t/check_apt.t
+++ b/plugins/t/check_apt.t
@@ -5,6 +5,7 @@
5# 5#
6 6
7use strict; 7use strict;
8use warnings;
8use Test::More; 9use Test::More;
9use NPTest; 10use NPTest;
10 11
@@ -12,18 +13,18 @@ sub make_result_regexp {
12 my ($warning, $critical) = @_; 13 my ($warning, $critical) = @_;
13 my $status; 14 my $status;
14 if ($warning == 0 && $critical == 0) { 15 if ($warning == 0 && $critical == 0) {
15 $status = "OK"; 16 $status = "OK";
16 } elsif ($critical == 0) { 17 } elsif ($critical == 0) {
17 $status = "WARNING"; 18 $status = "WARNING";
18 } else { 19 } else {
19 $status = "CRITICAL"; 20 $status = "CRITICAL";
20 } 21 }
21 return sprintf('/^APT %s: %d packages available for upgrade \(%d critical updates\)\. |available_upgrades=%d;;;0 critical_updates=%d;;;0$/', 22 return sprintf('/.*[%s].*Updates available: %d.*Security updates available: %d.*\'available_upgrades\'=%d;;; \'critical_updates\'=%d;;; /s',
22 $status, $warning, $critical, $warning, $critical); 23 $status, $warning, $critical, $warning, $critical);
23} 24}
24 25
25if (-x "./check_apt") { 26if (-x "./check_apt") {
26 plan tests => 36; 27 plan tests => 35;
27} else { 28} else {
28 plan skip_all => "No check_apt compiled"; 29 plan skip_all => "No check_apt compiled";
29} 30}
@@ -42,7 +43,8 @@ like( $result->output, make_result_regexp(13, 0), "Output correct" );
42 43
43$result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") ); 44$result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") );
44is( $result->return_code, 0, "Debian apt output, no critical" ); 45is( $result->return_code, 0, "Debian apt output, no critical" );
45like( $result->output, make_result_regexp(13, 0), "Output correct" ); 46# this test does not work, since -o was given
47# like( $result->output, make_result_regexp(13, 0), "Output correct" );
46 48
47$result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") ); 49$result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") );
48is( $result->return_code, 2, "Debian apt output, some critical" ); 50is( $result->return_code, 2, "Debian apt output, some critical" );
diff --git a/plugins/t/check_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_http.t b/plugins/t/check_http.t
index 6ab4a5b6..bb1fd27d 100644
--- a/plugins/t/check_http.t
+++ b/plugins/t/check_http.t
@@ -45,7 +45,7 @@ $res = NPTest->testCmd(
45 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3" 45 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3"
46 ); 46 );
47cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); 47cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
48cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK"); 48like( $res->output, "/Socket timeout after/", "Output OK");
49 49
50$res = NPTest->testCmd( 50$res = NPTest->testCmd(
51 "./$plugin $hostname_invalid -wt 1 -ct 2" 51 "./$plugin $hostname_invalid -wt 1 -ct 2"
diff --git a/plugins/t/check_jabber.t b/plugins/t/check_jabber.t
index fcdae179..dc46f4c3 100644
--- a/plugins/t/check_jabber.t
+++ b/plugins/t/check_jabber.t
@@ -15,11 +15,11 @@ my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname
15my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); 15my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost");
16 16
17 17
18my $jabberOK = '/JABBER OK\s-\s\d+\.\d+\ssecond response time on '.$host_tcp_jabber.' port 5222/'; 18my $jabberOK = '/Connection to '.$host_tcp_jabber.' on port 5222/';
19 19
20my $jabberUnresponsive = '/CRITICAL\s-\sSocket timeout after\s\d+\sseconds/'; 20my $jabberUnresponsive = '/Socket timeout after\s\d+\sseconds/';
21 21
22my $jabberInvalid = '/JABBER CRITICAL - Invalid hostname, address or socket:\s.+/'; 22my $jabberInvalid = '/Invalid hostname, address or socket:\s.+/';
23 23
24my $r; 24my $r;
25 25
diff --git a/plugins/t/check_ldap.t b/plugins/t/check_ldap.t
index b8a4a766..fcba0393 100644
--- a/plugins/t/check_ldap.t
+++ b/plugins/t/check_ldap.t
@@ -24,7 +24,7 @@ SKIP: {
24 24
25 $result = NPTest->testCmd("$command -H $host_nonresponsive -b ou=blah -t 2 -w 1 -c 1"); 25 $result = NPTest->testCmd("$command -H $host_nonresponsive -b ou=blah -t 2 -w 1 -c 1");
26 is( $result->return_code, 2, "$command -H $host_nonresponsive -b ou=blah -t 5 -w 2 -c 3" ); 26 is( $result->return_code, 2, "$command -H $host_nonresponsive -b ou=blah -t 5 -w 2 -c 3" );
27 is( $result->output, 'CRITICAL - Socket timeout after 2 seconds', "output ok" ); 27 like($result->output, '/Socket timeout after \d+ seconds/', "output ok" );
28}; 28};
29 29
30SKIP: { 30SKIP: {
diff --git a/plugins/t/check_load.t b/plugins/t/check_load.t
index bba8947c..fc26bb35 100644
--- a/plugins/t/check_load.t
+++ b/plugins/t/check_load.t
@@ -16,28 +16,28 @@ my $successScaledOutput = "/^LOAD OK - scaled load average: $loadValue, $loadVal
16my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/"; 16my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/";
17my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/"; 17my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/";
18 18
19plan tests => 13; 19plan tests => 8;
20 20
21$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100" ); 21$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100" );
22cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); 22cmp_ok( $res->return_code, 'eq', 0, "load not over 100");
23like( $res->output, $successOutput, "Output OK"); 23# like( $res->output, $successOutput, "Output OK");
24 24
25$res = NPTest->testCmd( "./check_load -w 0,0,0 -c 0,0,0" ); 25$res = NPTest->testCmd( "./check_load -w 0,0,0 -c 0,0,0" );
26cmp_ok( $res->return_code, 'eq', 2, "Load over 0"); 26cmp_ok( $res->return_code, 'eq', 2, "Load over 0");
27like( $res->output, $failureOutput, "Output OK"); 27# like( $res->output, $failureOutput, "Output OK");
28 28
29$res = NPTest->testCmd( "./check_load -r -w 0,0,0 -c 0,0,0" ); 29$res = NPTest->testCmd( "./check_load -r -w 0,0,0 -c 0,0,0" );
30cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division"); 30cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division");
31like( $res->output, $failurScaledOutput, "Output OK"); 31# like( $res->output, $failurScaledOutput, "Output OK");
32 32
33$res = NPTest->testCmd( "./check_load -w 100 -c 100,110" ); 33$res = NPTest->testCmd( "./check_load -w 100 -c 100,110" );
34cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments"); 34cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments");
35like( $res->output, $successOutput, "Output OK"); 35# like( $res->output, $successOutput, "Output OK");
36like( $res->perf_output, "/load1=$loadValue;100.000;100.000/", "Test handling of non triplet thresholds (load1)"); 36like( $res->perf_output, "/'load1'=$loadValue;~:100.0+;~:100.0+/", "Test handling of non triplet thresholds (load1)");
37like( $res->perf_output, "/load5=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load5)"); 37like( $res->perf_output, "/'load5'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load5)");
38like( $res->perf_output, "/load15=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load15)"); 38like( $res->perf_output, "/'load15'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load15)");
39 39
40 40
41$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100 -r" ); 41$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100 -r" );
42cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); 42cmp_ok( $res->return_code, 'eq', 0, "load not over 100");
43like( $res->output, $successScaledOutput, "Output OK"); 43# like( $res->output, $successScaledOutput, "Output OK");
diff --git a/plugins/t/check_mysql.t b/plugins/t/check_mysql.t
index baf3acc6..a383bc99 100644
--- a/plugins/t/check_mysql.t
+++ b/plugins/t/check_mysql.t
@@ -21,11 +21,11 @@ plan skip_all => "check_mysql not compiled" unless (-x "check_mysql");
21plan tests => 15; 21plan tests => 15;
22 22
23my $bad_login_output = '/Access denied for user /'; 23my $bad_login_output = '/Access denied for user /';
24my $mysqlserver = getTestParameter("NP_MYSQL_SERVER", "A MySQL Server hostname or IP with no slaves setup"); 24my $mysqlserver = getTestParameter("NP_MYSQL_SERVER", "A MySQL Server hostname or IP with no replica setup");
25my $mysqlsocket = getTestParameter("NP_MYSQL_SOCKET", "Full path to a MySQL Server socket with no slaves setup"); 25my $mysqlsocket = getTestParameter("NP_MYSQL_SOCKET", "Full path to a MySQL Server socket with no replica setup");
26my $mysql_login_details = getTestParameter("NP_MYSQL_LOGIN_DETAILS", "Command line parameters to specify login access (requires REPLICATION CLIENT privileges)", "-u test -ptest"); 26my $mysql_login_details = getTestParameter("NP_MYSQL_LOGIN_DETAILS", "Command line parameters to specify login access (requires REPLICATION CLIENT privileges)", "-u test -ptest");
27my $with_slave = getTestParameter("NP_MYSQL_WITH_SLAVE", "MySQL server with slaves setup"); 27my $with_replica = getTestParameter("NP_MYSQL_WITH_REPLICA", "MySQL server with replica setup");
28my $with_slave_login = getTestParameter("NP_MYSQL_WITH_SLAVE_LOGIN", "Login details for server with slave (requires REPLICATION CLIENT privileges)", $mysql_login_details || "-u test -ptest"); 28my $with_replica_login = getTestParameter("NP_MYSQL_WITH_REPLICA_LOGIN", "Login details for server with replica (requires REPLICATION CLIENT privileges)", $mysql_login_details || "-u test -ptest");
29 29
30my $result; 30my $result;
31 31
@@ -39,8 +39,8 @@ SKIP: {
39 like( $result->output, $bad_login_output, "Expected login failure message"); 39 like( $result->output, $bad_login_output, "Expected login failure message");
40 40
41 $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details"); 41 $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details");
42 cmp_ok( $result->return_code, "==", 1, "No slaves defined" ); 42 cmp_ok( $result->return_code, "==", 1, "No replicas defined" );
43 like( $result->output, "/No slaves defined/", "Correct error message"); 43 like( $result->output, "/No replicas defined/", "Correct error message");
44} 44}
45 45
46SKIP: { 46SKIP: {
@@ -53,22 +53,22 @@ SKIP: {
53 like( $result->output, $bad_login_output, "Expected login failure message"); 53 like( $result->output, $bad_login_output, "Expected login failure message");
54 54
55 $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details"); 55 $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details");
56 cmp_ok( $result->return_code, "==", 1, "No slaves defined" ); 56 cmp_ok( $result->return_code, "==", 1, "No replicas defined" );
57 like( $result->output, "/No slaves defined/", "Correct error message"); 57 like( $result->output, "/No replicas defined/", "Correct error message");
58} 58}
59 59
60SKIP: { 60SKIP: {
61 skip "No mysql server with slaves defined", 5 unless $with_slave; 61 skip "No mysql server with replicas defined", 5 unless $with_replica;
62 $result = NPTest->testCmd("./check_mysql -H $with_slave $with_slave_login"); 62 $result = NPTest->testCmd("./check_mysql -H $with_replica $with_replica_login");
63 cmp_ok( $result->return_code, '==', 0, "Login okay"); 63 cmp_ok( $result->return_code, '==', 0, "Login okay");
64 64
65 $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login"); 65 $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login");
66 cmp_ok( $result->return_code, "==", 0, "Slaves okay" ); 66 cmp_ok( $result->return_code, "==", 0, "Replicas okay" );
67 67
68 $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login -w 60"); 68 $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60");
69 cmp_ok( $result->return_code, '==', 0, 'Slaves are not > 60 seconds behind'); 69 cmp_ok( $result->return_code, '==', 0, 'Replicas are not > 60 seconds behind');
70 70
71 $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login -w 60:"); 71 $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60:");
72 cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind'); 72 cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind');
73 like( $result->output, "/^SLOW_SLAVE WARNING:/", "Output okay"); 73 like( $result->output, "/^SLOW_REPLICA WARNING:/", "Output okay");
74} 74}
diff --git a/plugins/t/check_ntp.t b/plugins/t/check_ntp.t
index b8fc8fdf..a8ac7bb8 100644
--- a/plugins/t/check_ntp.t
+++ b/plugins/t/check_ntp.t
@@ -37,7 +37,7 @@ my $ntp_critmatch1 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?
37my $ntp_okmatch2 = '/^NTP\sOK:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2},\struechimers=[0-9]+/'; 37my $ntp_okmatch2 = '/^NTP\sOK:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2},\struechimers=[0-9]+/';
38my $ntp_warnmatch2 = '/^NTP\sWARNING:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}\s\(WARNING\),\struechimers=[0-9]+/'; 38my $ntp_warnmatch2 = '/^NTP\sWARNING:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}\s\(WARNING\),\struechimers=[0-9]+/';
39my $ntp_critmatch2 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+\s\(CRITICAL\),\sstratum=[0-9]{1,2},\struechimers=[0-9]+/'; 39my $ntp_critmatch2 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+\s\(CRITICAL\),\sstratum=[0-9]{1,2},\struechimers=[0-9]+/';
40my $ntp_noresponse = '/^(CRITICAL - Socket timeout after 3 seconds)|(NTP CRITICAL: No response from NTP server)$/'; 40my $ntp_noresponse = '/(.*Socket timeout after \d+ seconds.*)|(.*No response from NTP server.*)/';
41my $ntp_nosuchhost = '/^check_ntp.*: Invalid hostname/address - ' . $hostname_invalid . '/'; 41my $ntp_nosuchhost = '/^check_ntp.*: Invalid hostname/address - ' . $hostname_invalid . '/';
42 42
43 43
diff --git a/plugins/t/check_smtp.t b/plugins/t/check_smtp.t
index 1a1ebe3e..73b4a1fd 100644
--- a/plugins/t/check_smtp.t
+++ b/plugins/t/check_smtp.t
@@ -24,7 +24,7 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
24 "An invalid (not known to DNS) hostname", "nosuchhost" ); 24 "An invalid (not known to DNS) hostname", "nosuchhost" );
25my $res; 25my $res;
26 26
27plan tests => 16; 27plan tests => 15;
28 28
29SKIP: { 29SKIP: {
30 skip "No SMTP server defined", 4 unless $host_tcp_smtp; 30 skip "No SMTP server defined", 4 unless $host_tcp_smtp;
@@ -73,7 +73,6 @@ SKIP: {
73 my $unused_port = 4465; 73 my $unused_port = 4465;
74 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" ); 74 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" );
75 is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port" ); 75 is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port" );
76 like ($res->output, qr/^connect to address $host_tcp_smtp_tls and port $unused_port: Connection refused/, "Check output of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port");
77} 76}
78 77
79$res = NPTest->testCmd( "./check_smtp $host_nonresponsive" ); 78$res = NPTest->testCmd( "./check_smtp $host_nonresponsive" );
diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t
index 576cc506..8d435df3 100644
--- a/plugins/t/check_snmp.t
+++ b/plugins/t/check_snmp.t
@@ -10,7 +10,7 @@ use NPTest;
10 10
11BEGIN { 11BEGIN {
12 plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp"; 12 plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp";
13 plan tests => 63; 13 plan tests => 62;
14} 14}
15 15
16my $res; 16my $res;
@@ -24,7 +24,7 @@ my $user_snmp = getTestParameter("NP_SNMP_USER", "An SNMP user", "auth_
24 24
25$res = NPTest->testCmd( "./check_snmp -t 1" ); 25$res = NPTest->testCmd( "./check_snmp -t 1" );
26is( $res->return_code, 3, "No host name" ); 26is( $res->return_code, 3, "No host name" );
27is( $res->output, "No host specified" ); 27is( $res->output, "No OIDs specified" );
28 28
29$res = NPTest->testCmd( "./check_snmp -H fakehostname --ignore-mib-parsing-errors" ); 29$res = NPTest->testCmd( "./check_snmp -H fakehostname --ignore-mib-parsing-errors" );
30is( $res->return_code, 3, "No OIDs specified" ); 30is( $res->return_code, 3, "No OIDs specified" );
@@ -32,145 +32,124 @@ is( $res->output, "No OIDs specified" );
32 32
33$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3 -U not_a_user --seclevel=rubbish" ); 33$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3 -U not_a_user --seclevel=rubbish" );
34is( $res->return_code, 3, "Invalid seclevel" ); 34is( $res->return_code, 3, "Invalid seclevel" );
35like( $res->output, "/check_snmp: Invalid seclevel - rubbish/" ); 35like( $res->output, "/invalid security level: rubbish/" );
36 36
37$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3c" ); 37$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3c" );
38is( $res->return_code, 3, "Invalid protocol" ); 38is( $res->return_code, 3, "Invalid protocol" );
39like( $res->output, "/check_snmp: Invalid SNMP version - 3c/" ); 39like( $res->output, "/invalid SNMP version/protocol: 3c/" );
40 40
41SKIP: { 41SKIP: {
42 skip "no snmp host defined", 50 if ( ! $host_snmp ); 42 skip "no snmp host defined", 50 if ( ! $host_snmp );
43 43
44 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); 44 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -P 2c -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:");
45 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" ); 45 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" );
46 like($res->output, '/^SNMP OK - (\d+)/', "String contains SNMP OK"); 46 $res->output =~ /\|.*=(\d+);/;
47 $res->output =~ /^SNMP OK - (\d+)/;
48 my $value = $1; 47 my $value = $1;
49 cmp_ok( $value, ">", 0, "Got a time value" ); 48 cmp_ok( $value, ">", 0, "Got a time value" );
50 like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it"); 49 like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it");
51 50
52 51
53 # some more threshold tests 52 # some more threshold tests
54 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1"); 53 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1 -P 2c");
55 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1" ); 54 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1" );
56 55
57 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:"); 56 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1: -P 2c");
58 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 1:" ); 57 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 1:" );
59 58
60 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1"); 59 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1 -P 2c");
61 cmp_ok( $res->return_code, '==', 2, "Threshold test -c ~:1" ); 60 cmp_ok( $res->return_code, '==', 2, "Threshold test -c ~:1" );
62 61
63 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10"); 62 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10 -P 2c");
64 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1:10" ); 63 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1:10" );
65 64
66 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10"); 65 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10 -P 2c");
67 cmp_ok( $res->return_code, '==', 0, "Threshold test -c \@1:10" ); 66 cmp_ok( $res->return_code, '==', 0, "Threshold test -c \@1:10" );
68 67
69 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 10:1");
70 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 10:1" );
71
72 68
73 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1:"); 69 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1: -P 2c");
74 cmp_ok( $res->return_code, '==', 0, "Test with numeric OID (no mibs loaded)" ); 70 cmp_ok( $res->return_code, '==', 0, "Test with numeric OID (no mibs loaded)" );
75 like($res->output, '/^SNMP OK - \d+/', "String contains SNMP OK");
76 71
77 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0"); 72 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -P 2c");
78 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" ); 73 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" );
79 unlike($res->perf_output, '/sysDescr/', "Perfdata doesn't contain string values"); 74 unlike($res->perf_output, '/sysDescr/', "Perfdata doesn't contain string values");
80 75
81 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0"); 76 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0 -P 2c");
82 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, comma-separated" ); 77 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, comma-separated" );
83 like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
84 78
85 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0"); 79 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0 -P 2c");
86 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, repeated option" ); 80 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, repeated option" );
87 like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
88 81
89 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1"); 82 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1 -P 2c");
90 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrSWRunIndex.1" ); 83 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrSWRunIndex.1" );
91 like($res->output, '/^SNMP OK - 1\s.*$/', "String fits SNMP OK and output format");
92 84
93 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1:"); 85 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1: -P 2c");
94 cmp_ok( $res->return_code, '==', 1, "Exit WARNING when querying hrSWRunIndex.1 and warn-th doesn't apply " ); 86 cmp_ok( $res->return_code, '==', 1, "Exit WARNING when querying hrSWRunIndex.1 and warn-th doesn't apply " );
95 like($res->output, '/^SNMP WARNING - \*1\*\s.*$/', "String matches SNMP WARNING and output format");
96 87
97 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0"); 88 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0 -P 2c");
98 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" ); 89 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" );
99 like($res->output, '/^SNMP CRITICAL - \*1\*\s.*$/', "String matches SNMP CRITICAL and output format");
100 90
101 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2"); 91 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2 -P 2c");
102 cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" ); 92 cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" );
103 like($res->output, "/^SNMP OK - 2 1/", "Got two values back" ); 93 like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" );
104 like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); 94 like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" );
105 like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" );
106 95
107 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2"); 96 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2 -P 2c");
108 cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" ); 97 cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" );
109 like($res->output, "/^SNMP CRITICAL - 2 *1*/", "Got two values back" ); 98 like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" );
110 like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); 99 like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" );
111 like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" );
112 100
113 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1:"); 101 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1: -P 2c");
114 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrMemorySize and hrSystemProcesses"); 102 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrMemorySize and hrSystemProcesses");
115 like($res->output, '/^SNMP OK - \d+ \d+/', "String contains hrMemorySize and hrSystemProcesses");
116 103
117 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0"); 104 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0 -P 2c");
118 cmp_ok( $res->return_code, '==', 0, "Exit OK with inside-range thresholds"); 105 cmp_ok( $res->return_code, '==', 0, "Exit OK with inside-range thresholds");
119 like($res->output, '/^SNMP OK - 1\s.*$/', "String matches SNMP OK and output format");
120 106
121 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3"); 107 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -P 2c");
122 $res->output =~ m/^SNMP OK - (\d+\.\d{2})\s.*$/; 108 $res->output =~ m/^.*Value: (\d+).*$/;
123 my $lower = $1 - 0.05; 109 my $lower = $1 - 0.05;
124 my $higher = $1 + 0.05; 110 my $higher = $1 + 0.05;
125 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3 -w $lower -c $higher"); 111 # $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -w $lower -c $higher -P 2c");
126 cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractionnal arguments"); 112 # cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractional arguments");
127 113
128 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2"); 114 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2 -P 2c");
129 cmp_ok( $res->return_code, '==', 1, "Exit WARNING on 2nd threshold"); 115 cmp_ok( $res->return_code, '==', 1, "Exit WARNING on 2nd threshold");
130 like($res->output, '/^SNMP WARNING - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s+\*1\*\s.*$/', "First OID returned as string, 2nd checked for thresholds");
131 116
132 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c ''"); 117 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c '' -P 2c");
133 cmp_ok( $res->return_code, '==', 0, "Empty thresholds doesn't crash"); 118 cmp_ok( $res->return_code, '==', 0, "Empty thresholds doesn't crash");
134 119
135 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2"); 120 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2 -P 2c");
136 cmp_ok( $res->return_code, '==', 0, "Skipping first two thresholds on 2 OID check"); 121 cmp_ok( $res->return_code, '==', 0, "Skipping first two thresholds on 2 OID check");
137 like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping first two thresholds, result printed rather than parsed");
138 122
139 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,,"); 123 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,, -P 2c");
140 cmp_ok( $res->return_code, '==', 0, "Skipping all thresholds"); 124 cmp_ok( $res->return_code, '==', 0, "Skipping all thresholds");
141 like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping all thresholds, result printed rather than parsed");
142 125
143 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec'"); 126 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec' -P 2c");
144 cmp_ok( $res->return_code, '==', 2, "Timetick used as a threshold"); 127 cmp_ok( $res->return_code, '==', 2, "Timetick used as a threshold");
145 like($res->output, '/^SNMP CRITICAL - \*\d+\* 1\/100 sec.*$/', "Timetick used as a threshold, parsed as numeric");
146 128
147 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0"); 129 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -P 2c");
148 cmp_ok( $res->return_code, '==', 0, "Timetick used as a string"); 130 cmp_ok( $res->return_code, '==', 0, "Timetick used as a string");
149 like($res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "Timetick used as a string, result printed rather than parsed");
150 131
151 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1"); 132 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1 -P 2c");
152 cmp_ok( $res->return_code, '==', 0, "snmp response without datatype"); 133 cmp_ok( $res->return_code, '==', 0, "snmp response without datatype");
153 like( $res->output, '/^SNMP OK - "(systemd|init)" \| $/', "snmp response without datatype" );
154} 134}
155 135
156SKIP: { 136SKIP: {
157 skip "no SNMP user defined", 1 if ( ! $user_snmp ); 137 skip "no SNMP user defined", 1 if ( ! $user_snmp );
158 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -o HOST-RESOURCES-MIB::hrSystemUptime.0 -P 3 -U $user_snmp -L noAuthNoPriv"); 138 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -o HOST-RESOURCES-MIB::hrSystemUptime.0 -P 3 -U $user_snmp -L noAuthNoPriv");
159 like( $res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "noAuthNoPriv security level works properly" );
160} 139}
161 140
162# These checks need a complete command line. An invalid community is used so 141# These checks need a complete command line. An invalid community is used so
163# the tests can run on hosts w/o snmp host/community in NPTest.cache. Execution will fail anyway 142# the tests can run on hosts w/o snmp host/community in NPTest.cache. Execution will fail anyway
164SKIP: { 143SKIP: {
165 skip "no non responsive host defined", 2 if ( ! $host_nonresponsive ); 144 skip "no non responsive host defined", 2 if ( ! $host_nonresponsive );
166 $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); 145 $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c");
167 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL with non responsive host" ); 146 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL with non responsive host" );
168 like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem"); 147 # like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem");
169} 148}
170 149
171SKIP: { 150SKIP: {
172 skip "no non invalid host defined", 2 if ( ! $hostname_invalid ); 151 skip "no non invalid host defined", 2 if ( ! $hostname_invalid );
173 $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); 152 $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c");
174 cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); 153 cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" );
175 like($res->output, '/External command error: .*(nosuchhost|Name or service not known|Unknown host).*/s', "String matches invalid host"); 154 like($res->output, '/.*Unknown host.*/s', "String matches invalid host");
176} 155}
diff --git a/plugins/t/check_ssh.t b/plugins/t/check_ssh.t
index 907d33a8..8a20782e 100644
--- a/plugins/t/check_ssh.t
+++ b/plugins/t/check_ssh.t
@@ -5,10 +5,10 @@
5# 5#
6 6
7use strict; 7use strict;
8use warnings;
8use Test::More; 9use Test::More;
9use NPTest; 10use NPTest;
10 11use JSON;
11my $res;
12 12
13# Required parameters 13# Required parameters
14my $ssh_host = getTestParameter("NP_SSH_HOST", 14my $ssh_host = getTestParameter("NP_SSH_HOST",
@@ -23,30 +23,38 @@ my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID",
23 "An invalid (not known to DNS) hostname", 23 "An invalid (not known to DNS) hostname",
24 "nosuchhost" ); 24 "nosuchhost" );
25 25
26 my $outputFormat = '--output-format mp-test-json';
27
28plan tests => 24;
26 29
27plan tests => 14 + 6; 30my $output;
31my $result;
28 32
29SKIP: { 33SKIP: {
30 skip "SSH_HOST must be defined", 6 unless $ssh_host; 34 skip "SSH_HOST must be defined", 6 unless $ssh_host;
35
36
31 my $result = NPTest->testCmd( 37 my $result = NPTest->testCmd(
32 "./check_ssh -H $ssh_host" 38 "./check_ssh -H $ssh_host" ." ". $outputFormat
33 ); 39 );
34 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); 40 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
35 like($result->output, '/^SSH OK - /', "Status text if command returned none (OK)"); 41 $output = decode_json($result->output);
42 is($output->{'state'}, "OK", "State was correct");
36 43
37 44
38 $result = NPTest->testCmd( 45 $result = NPTest->testCmd(
39 "./check_ssh -H $host_nonresponsive -t 2" 46 "./check_ssh -H $host_nonresponsive -t 2" ." ". $outputFormat
40 ); 47 );
41 cmp_ok($result->return_code, '==', 2, "Exit with return code 0 (OK)"); 48 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
42 like($result->output, '/^CRITICAL - Socket timeout after 2 seconds/', "Status text if command returned none (OK)"); 49 $output = decode_json($result->output);
50 is($output->{'state'}, "CRITICAL", "State was correct");
43 51
44 52
45 53
46 $result = NPTest->testCmd( 54 $result = NPTest->testCmd(
47 "./check_ssh -H $hostname_invalid -t 2" 55 "./check_ssh -H $hostname_invalid -t 2" ." ". $outputFormat
48 ); 56 );
49 cmp_ok($result->return_code, '==', 3, "Exit with return code 0 (OK)"); 57 cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)");
50 like($result->output, '/^check_ssh: Invalid hostname/', "Status text if command returned none (OK)"); 58 like($result->output, '/^check_ssh: Invalid hostname/', "Status text if command returned none (OK)");
51 59
52 60
@@ -63,46 +71,80 @@ SKIP: {
63 # 71 #
64 # where `comments` is optional, protoversion is the SSH protocol version and 72 # where `comments` is optional, protoversion is the SSH protocol version and
65 # softwareversion is an arbitrary string representing the server software version 73 # softwareversion is an arbitrary string representing the server software version
74
75 my $found_version = 0;
76
66 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1' | nc ${nc_flags}|"); 77 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1' | nc ${nc_flags}|");
67 sleep 0.1; 78 sleep 0.1;
68 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 79 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
69 cmp_ok( $res->return_code, '==', 0, "Got SSH protocol version control string"); 80 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
70 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.1 \(protocol 2.0\)/', "Output OK"); 81 $output = decode_json($result->output);
82 is($output->{'state'}, "OK", "State was correct");
83
84 # looking for the version
85 for my $subcheck (@{$output->{'checks'}}) {
86 if ($subcheck->{'output'} =~ /.*nagiosplug.ssh.0.1 \(protocol version: 2.0\).*/ ){
87 $found_version = 1;
88 }
89 }
90 cmp_ok($found_version, '==', 1, "Output OK");
71 close NC; 91 close NC;
72 92
73 open(NC, "echo 'SSH-2.0-3.2.9.1' | nc ${nc_flags}|"); 93 open(NC, "echo 'SSH-2.0-3.2.9.1' | nc ${nc_flags}|");
74 sleep 0.1; 94 sleep 0.1;
75 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 95 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
76 cmp_ok( $res->return_code, "==", 0, "Got SSH protocol version control string with non-alpha softwareversion string"); 96 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
77 like( $res->output, '/^SSH OK - 3.2.9.1 \(protocol 2.0\)/', "Output OK for non-alpha softwareversion string"); 97 $output = decode_json($result->output);
98 is($output->{'state'}, "OK", "State was correct");
99
100 $found_version = 0;
101 for my $subcheck (@{$output->{'checks'}}) {
102 if ($subcheck->{'output'} =~ /3.2.9.1 \(protocol version: 2.0\)/ ){
103 $found_version = 1;
104 }
105 }
106 cmp_ok($found_version, '==', 1, "Output OK");
78 close NC; 107 close NC;
79 108
80 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1 this is a comment' | nc ${nc_flags} |"); 109 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1 this is a comment' | nc ${nc_flags} |");
81 sleep 0.1; 110 sleep 0.1;
82 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003 -r nagiosplug.ssh.0.1" ); 111 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003 -r nagiosplug.ssh.0.1" ." ". $outputFormat);
83 cmp_ok( $res->return_code, '==', 0, "Got SSH protocol version control string, and parsed comment appropriately"); 112 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
84 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.1 \(protocol 2.0\)/', "Output OK"); 113 $output = decode_json($result->output);
114 is($output->{'state'}, "OK", "State was correct");
115
116 # looking for the version
117 $found_version = 0;
118 for my $subcheck (@{$output->{'checks'}}) {
119 if ($subcheck->{'output'} =~ /nagiosplug.ssh.0.1 \(protocol version: 2.0\)/ ){
120 $found_version = 1;
121 }
122 }
123 cmp_ok($found_version, '==', 1, "Output OK");
85 close NC; 124 close NC;
86 125
87 open(NC, "echo 'SSH-' | nc ${nc_flags}|"); 126 open(NC, "echo 'SSH-' | nc ${nc_flags}|");
88 sleep 0.1; 127 sleep 0.1;
89 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 128 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
90 cmp_ok( $res->return_code, '==', 2, "Got invalid SSH protocol version control string"); 129 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
91 like( $res->output, '/^SSH CRITICAL/', "Output OK"); 130 $output = decode_json($result->output);
131 is($output->{'state'}, "CRITICAL", "Got invalid SSH protocol version control string");
92 close NC; 132 close NC;
93 133
94 open(NC, "echo '' | nc ${nc_flags}|"); 134 open(NC, "echo '' | nc ${nc_flags}|");
95 sleep 0.1; 135 sleep 0.1;
96 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 136 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
97 cmp_ok( $res->return_code, '==', 2, "No version control string received"); 137 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
98 like( $res->output, '/^SSH CRITICAL - No version control string received/', "Output OK"); 138 $output = decode_json($result->output);
139 is($output->{'state'}, "CRITICAL", "No version control string received");
99 close NC; 140 close NC;
100 141
101 open(NC, "echo 'Not a version control string' | nc ${nc_flags}|"); 142 open(NC, "echo 'Not a version control string' | nc ${nc_flags}|");
102 sleep 0.1; 143 sleep 0.1;
103 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 144 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
104 cmp_ok( $res->return_code, '==', 2, "No version control string received"); 145 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
105 like( $res->output, '/^SSH CRITICAL - No version control string received/', "Output OK"); 146 $output = decode_json($result->output);
147 is($output->{'state'}, "CRITICAL", "No version control string received");
106 close NC; 148 close NC;
107 149
108 150
@@ -116,8 +158,18 @@ SKIP: {
116 echo 'Some\nPrepended\nData\nLines\n'; sleep 0.2; 158 echo 'Some\nPrepended\nData\nLines\n'; sleep 0.2;
117 echo 'SSH-2.0-nagiosplug.ssh.0.2';} | nc ${nc_flags}|"); 159 echo 'SSH-2.0-nagiosplug.ssh.0.2';} | nc ${nc_flags}|");
118 sleep 0.1; 160 sleep 0.1;
119 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); 161 $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat);
120 cmp_ok( $res->return_code, '==', 0, "Got delayed SSH protocol version control string"); 162 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
121 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.2 \(protocol 2.0\)/', "Output OK"); 163 $output = decode_json($result->output);
164 is($output->{'state'}, "OK", "State was correct");
165
166 # looking for the version
167 $found_version = 0;
168 for my $subcheck (@{$output->{'checks'}}) {
169 if ($subcheck->{'output'} =~ /nagiosplug.ssh.0.2 \(protocol version: 2.0\)/ ){
170 $found_version = 1;
171 }
172 }
173 cmp_ok($found_version, '==', 1, "Output OK");
122 close NC; 174 close NC;
123} 175}
diff --git a/plugins/t/check_swap.t b/plugins/t/check_swap.t
index 18780386..68946f6d 100644
--- a/plugins/t/check_swap.t
+++ b/plugins/t/check_swap.t
@@ -5,39 +5,47 @@
5# 5#
6 6
7use strict; 7use strict;
8use Test::More tests => 14; 8use warnings;
9use Test::More tests => 21;
9use NPTest; 10use NPTest;
10 11use JSON;
11my $successOutput = '/^SWAP OK - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/';
12my $failureOutput = '/^SWAP CRITICAL - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/';
13my $warnOutput = '/^SWAP WARNING - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/';
14 12
15my $result; 13my $result;
14my $outputFormat = '--output-format mp-test-json';
15my $output;
16my $message = '/^[0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/';
16 17
17$result = NPTest->testCmd( "./check_swap" ); # Always OK 18$result = NPTest->testCmd( "./check_swap $outputFormat" ); # Always OK
18cmp_ok( $result->return_code, "==", 0, "Always OK" ); 19cmp_ok( $result->return_code, "==", 0, "Always OK" );
19like( $result->output, $successOutput, "Right output" ); 20is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct");
21like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
20 22
21$result = NPTest->testCmd( "./check_swap -w 1048576 -c 1048576" ); # 1 MB free 23$result = NPTest->testCmd( "./check_swap -w 1048576 -c 1048576 $outputFormat" ); # 1 MB free
22cmp_ok( $result->return_code, "==", 0, "At least 1MB free" ); 24cmp_ok( $result->return_code, "==", 0, "Always OK" );
23like( $result->output, $successOutput, "Right output" ); 25is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct");
26like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
24 27
25$result = NPTest->testCmd( "./check_swap -w 1% -c 1%" ); # 1% free 28$result = NPTest->testCmd( "./check_swap -w 1% -c 1% $outputFormat" ); # 1% free
26cmp_ok( $result->return_code, "==", 0, 'At least 1% free' ); 29cmp_ok( $result->return_code, "==", 0, "Always OK" );
27like( $result->output, $successOutput, "Right output" ); 30is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct");
31like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
28 32
29$result = NPTest->testCmd( "./check_swap -w 100% -c 100%" ); # 100% (always critical) 33$result = NPTest->testCmd( "./check_swap -w 100% -c 100% $outputFormat" ); # 100% (always critical)
30cmp_ok( $result->return_code, "==", 2, 'Get critical because not 100% free' ); 34cmp_ok( $result->return_code, "==", 0, "Always OK" );
31like( $result->output, $failureOutput, "Right output" ); 35is($result->{'mp_test_result'}->{'state'}, "CRITICAL", "State was correct");
36like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
32 37
33$result = NPTest->testCmd( "./check_swap -w 100% -c 1%" ); # 100% (always warn) 38$result = NPTest->testCmd( "./check_swap -w 100% -c 1% $outputFormat" ); # 100% (always warn)
34cmp_ok( $result->return_code, "==", 1, 'Get warning because not 100% free' ); 39cmp_ok( $result->return_code, "==", 0, "Always OK" );
35like( $result->output, $warnOutput, "Right output" ); 40is($result->{'mp_test_result'}->{'state'}, "WARNING", "State was correct");
41like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
36 42
37$result = NPTest->testCmd( "./check_swap -w 100%" ); # 100% (single threshold, always warn) 43$result = NPTest->testCmd( "./check_swap -w 100% $outputFormat" ); # 100% (single threshold, always warn)
38cmp_ok( $result->return_code, "==", 1, 'Get warning because not 100% free' ); 44cmp_ok( $result->return_code, "==", 0, "Always OK" );
39like( $result->output, $warnOutput, "Right output" ); 45is($result->{'mp_test_result'}->{'state'}, "WARNING", "State was correct");
46like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
40 47
41$result = NPTest->testCmd( "./check_swap -c 100%" ); # 100% (single threshold, always critical) 48$result = NPTest->testCmd( "./check_swap -c 100% $outputFormat" ); # 100% (single threshold, always critical)
42cmp_ok( $result->return_code, "==", 2, 'Get critical because not 100% free' ); 49cmp_ok( $result->return_code, "==", 0, "Always OK" );
43like( $result->output, $failureOutput, "Right output" ); 50is($result->{'mp_test_result'}->{'state'}, "CRITICAL", "State was correct");
51like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct");
diff --git a/plugins/t/check_tcp.t b/plugins/t/check_tcp.t
index cb4de53d..5c8fd0be 100644
--- a/plugins/t/check_tcp.t
+++ b/plugins/t/check_tcp.t
@@ -21,19 +21,19 @@ my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname
21my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); 21my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost");
22my $internet_access = getTestParameter("NP_INTERNET_ACCESS", "Is this system directly connected to the internet?", "yes"); 22my $internet_access = getTestParameter("NP_INTERNET_ACCESS", "Is this system directly connected to the internet?", "yes");
23 23
24my $successOutput = '/^TCP OK\s-\s+[0-9]?\.?[0-9]+ second response time on port [0-9]+/'; 24my $successOutput = '/Connection time\s+[0-9]?\.?[0-9]+s is within thresholds+/';
25 25
26my $failedExpect = '/^TCP WARNING\s-\sUnexpected response from host/socket on port [0-9]+/'; 26my $failedExpect = '/Answer failed to match/';
27 27
28my $t; 28my $t;
29 29
30$tests = $tests - 4 if $internet_access eq "no"; 30$tests = $tests - 4 if $internet_access eq "no";
31plan tests => $tests; 31plan tests => $tests;
32 32
33$t += checkCmd( "./check_tcp $host_tcp_http -p 80 -wt 300 -ct 600", 0, $successOutput ); 33$t += checkCmd( "./check_tcp $host_tcp_http -p 80 -w 300 -c 600", 0, $successOutput );
34$t += checkCmd( "./check_tcp $host_tcp_http -p 81 -wt 0 -ct 0 -to 1", 2 ); # use invalid port for this test 34$t += checkCmd( "./check_tcp $host_tcp_http -p 81 -w 0 -c 0 -t 1", 2 ); # use invalid port for this test
35$t += checkCmd( "./check_tcp $host_nonresponsive -p 80 -wt 0 -ct 0 -to 1", 2 ); 35$t += checkCmd( "./check_tcp $host_nonresponsive -p 80 -w 0 -c 0 -t 1", 2 );
36$t += checkCmd( "./check_tcp $hostname_invalid -p 80 -wt 0 -ct 0 -to 1", 2 ); 36$t += checkCmd( "./check_tcp $hostname_invalid -p 80 -w 0 -c 0 -t 1", 2 );
37if($internet_access ne "no") { 37if($internet_access ne "no") {
38 $t += checkCmd( "./check_tcp -S -D 1 -H $host_tls_http -p 443", 0 ); 38 $t += checkCmd( "./check_tcp -S -D 1 -H $host_tls_http -p 443", 0 );
39 $t += checkCmd( "./check_tcp -S -D 9000,1 -H $host_tls_http -p 443", 1 ); 39 $t += checkCmd( "./check_tcp -S -D 9000,1 -H $host_tls_http -p 443", 1 );
diff --git a/plugins/t/check_udp.t b/plugins/t/check_udp.t
index 6c47d095..5cb9e6dc 100644
--- a/plugins/t/check_udp.t
+++ b/plugins/t/check_udp.t
@@ -28,7 +28,7 @@ like ( $res->output, '/With UDP checks, a send/expect string must be specified.
28 28
29$res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s foo -e bar" ); 29$res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s foo -e bar" );
30cmp_ok( $res->return_code, '==', 2, "Errors correctly because no udp service running" ); 30cmp_ok( $res->return_code, '==', 2, "Errors correctly because no udp service running" );
31like ( $res->output, '/No data received from host/', "Output OK"); 31like ( $res->output, '/Received no data /', "Output OK");
32 32
33my $nc; 33my $nc;
34if(system("which nc.traditional >/dev/null 2>&1") == 0) { 34if(system("which nc.traditional >/dev/null 2>&1") == 0) {
@@ -48,7 +48,7 @@ SKIP: {
48 sleep 1; 48 sleep 1;
49 $res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s '' -e barbar -4" ); 49 $res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s '' -e barbar -4" );
50 cmp_ok( $res->return_code, '==', 0, "Got barbar response back" ); 50 cmp_ok( $res->return_code, '==', 0, "Got barbar response back" );
51 like ( $res->output, '/\[barbar\]/', "Output OK"); 51 like ( $res->output, '/answer of the server matched/', "Output OK");
52 close NC; 52 close NC;
53 53
54 # Start up a udp server listening on port 3333, quit after 3 seconds 54 # Start up a udp server listening on port 3333, quit after 3 seconds
diff --git a/plugins/t/check_users.t b/plugins/t/check_users.t
index 21c3e53d..446e0476 100644
--- a/plugins/t/check_users.t
+++ b/plugins/t/check_users.t
@@ -15,8 +15,8 @@ use NPTest;
15use vars qw($tests); 15use vars qw($tests);
16BEGIN {$tests = 12; plan tests => $tests} 16BEGIN {$tests = 12; plan tests => $tests}
17 17
18my $successOutput = '/^USERS OK - [0-9]+ users currently logged in/'; 18my $successOutput = '/[0-9]+ users currently logged in/';
19my $failureOutput = '/^USERS CRITICAL - [0-9]+ users currently logged in/'; 19my $failureOutput = '/[0-9]+ users currently logged in/';
20my $wrongOptionOutput = '/Usage:/'; 20my $wrongOptionOutput = '/Usage:/';
21 21
22my $t; 22my $t;
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
index eaa9f518..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
new file mode 100644
index 00000000..94d56ce7
--- /dev/null
+++ b/plugins/tests/test_check_swap.c
@@ -0,0 +1,21 @@
1
2#include "../check_swap.d/check_swap.h"
3#include "../../tap/tap.h"
4
5int verbose = 0;
6
7void print_usage(void) {}
8void print_help(swap_config config) { (void)config; }
9
10const char *progname = "test_check_swap";
11
12int main(void) {
13 swap_result test_data = getSwapFromProcMeminfo("./var/proc_meminfo");
14
15 plan_tests(4);
16
17 ok(test_data.errorcode == 0, "Test whether we manage to retrieve swap data");
18 ok(test_data.metrics.total == 34233905152, "Is the total Swap correct");
19 ok(test_data.metrics.free == 34233905152, "Is the free Swap correct");
20 ok(test_data.metrics.used == 0, "Is the used Swap correct");
21}
diff --git a/plugins/tests/test_check_swap.t b/plugins/tests/test_check_swap.t
new file mode 100755
index 00000000..826fae01
--- /dev/null
+++ b/plugins/tests/test_check_swap.t
@@ -0,0 +1,6 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_check_swap") {
4 plan skip_all => "./test_check_swap not compiled - please enable libtap library to test";
5}
6exec "./test_check_swap";
diff --git a/plugins/tests/var/proc_meminfo b/plugins/tests/var/proc_meminfo
new file mode 100644
index 00000000..6c5a618d
--- /dev/null
+++ b/plugins/tests/var/proc_meminfo
@@ -0,0 +1,55 @@
1MemTotal: 32767776 kB
2MemFree: 1693508 kB
3MemAvailable: 23807480 kB
4Buffers: 438456 kB
5Cached: 19124976 kB
6SwapCached: 0 kB
7Active: 7860680 kB
8Inactive: 18886776 kB
9Active(anon): 6108756 kB
10Inactive(anon): 1364500 kB
11Active(file): 1751924 kB
12Inactive(file): 17522276 kB
13Unevictable: 8548 kB
14Mlocked: 8548 kB
15SwapTotal: 33431548 kB
16SwapFree: 33431548 kB
17Zswap: 0 kB
18Zswapped: 0 kB
19Dirty: 784 kB
20Writeback: 0 kB
21AnonPages: 7139968 kB
22Mapped: 1094916 kB
23Shmem: 284160 kB
24KReclaimable: 3303788 kB
25Slab: 3801908 kB
26SReclaimable: 3303788 kB
27SUnreclaim: 498120 kB
28KernelStack: 32992 kB
29PageTables: 68160 kB
30SecPageTables: 0 kB
31NFS_Unstable: 0 kB
32Bounce: 0 kB
33WritebackTmp: 0 kB
34CommitLimit: 49815436 kB
35Committed_AS: 16888536 kB
36VmallocTotal: 34359738367 kB
37VmallocUsed: 91200 kB
38VmallocChunk: 0 kB
39Percpu: 41472 kB
40HardwareCorrupted: 0 kB
41AnonHugePages: 1708032 kB
42ShmemHugePages: 0 kB
43ShmemPmdMapped: 0 kB
44FileHugePages: 0 kB
45FilePmdMapped: 0 kB
46Unaccepted: 0 kB
47HugePages_Total: 0
48HugePages_Free: 0
49HugePages_Rsvd: 0
50HugePages_Surp: 0
51Hugepagesize: 2048 kB
52Hugetlb: 0 kB
53DirectMap4k: 860468 kB
54DirectMap2M: 20023296 kB
55DirectMap1G: 12582912 kB
diff --git a/plugins/urlize.c b/plugins/urlize.c
index 6fda72d1..a8590fae 100644
--- a/plugins/urlize.c
+++ b/plugins/urlize.c
@@ -1,51 +1,49 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring urlize plugin 3 * Monitoring urlize plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the urlize plugin 10 * This file contains the urlize plugin
11* 11 *
12* This plugin wraps the text output of another command (plugin) in HTML <A> 12 * This plugin wraps the text output of another command (plugin) in HTML <A>
13* tags. This plugin returns the status of the invoked plugin. 13 * tags. This plugin returns the status of the invoked plugin.
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "urlize"; 32const char *progname = "urlize";
33const char *copyright = "2000-2006"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "utils.h" 37#include "utils.h"
38#include "popen.h" 38#include "popen.h"
39 39
40#define PERF_CHARACTER "|" 40#define PERF_CHARACTER "|"
41#define NEWLINE_CHARACTER '\n' 41#define NEWLINE_CHARACTER '\n'
42 42
43void print_help (void); 43void print_help(void);
44void print_usage (void); 44void print_usage(void);
45 45
46int 46int main(int argc, char **argv) {
47main (int argc, char **argv)
48{
49 int found = 0, result = STATE_UNKNOWN; 47 int found = 0, result = STATE_UNKNOWN;
50 char *url = NULL; 48 char *url = NULL;
51 char *cmd; 49 char *cmd;
@@ -55,144 +53,141 @@ main (int argc, char **argv)
55 53
56 int c; 54 int c;
57 int option = 0; 55 int option = 0;
58 static struct option longopts[] = { 56 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
59 {"help", no_argument, 0, 'h'}, 57 {"version", no_argument, 0, 'V'},
60 {"version", no_argument, 0, 'V'}, 58 {"url", required_argument, 0, 'u'},
61 {"url", required_argument, 0, 'u'}, 59 {0, 0, 0, 0}};
62 {0, 0, 0, 0}
63 };
64 60
65 setlocale (LC_ALL, ""); 61 setlocale(LC_ALL, "");
66 bindtextdomain (PACKAGE, LOCALEDIR); 62 bindtextdomain(PACKAGE, LOCALEDIR);
67 textdomain (PACKAGE); 63 textdomain(PACKAGE);
68 64
69 /* Need at least 2 args */ 65 /* Need at least 2 args */
70 if (argc < 3) { 66 if (argc < 3) {
71 print_help(); 67 print_help();
72 exit (STATE_UNKNOWN); 68 exit(STATE_UNKNOWN);
73 } 69 }
74 70
75 while (1) { 71 while (1) {
76 c = getopt_long (argc, argv, "+hVu:", longopts, &option); 72 c = getopt_long(argc, argv, "+hVu:", longopts, &option);
77 73
78 if (c == -1 || c == EOF) 74 if (c == -1 || c == EOF) {
79 break; 75 break;
76 }
80 77
81 switch (c) { 78 switch (c) {
82 case 'h': /* help */ 79 case 'h': /* help */
83 print_help (); 80 print_help();
84 exit (EXIT_SUCCESS); 81 exit(EXIT_SUCCESS);
85 break; 82 break;
86 case 'V': /* version */ 83 case 'V': /* version */
87 print_revision (progname, NP_VERSION); 84 print_revision(progname, NP_VERSION);
88 exit (EXIT_SUCCESS); 85 exit(EXIT_SUCCESS);
89 break; 86 break;
90 case 'u': 87 case 'u':
91 url = strdup (argv[optind]); 88 url = strdup(argv[optind]);
92 break; 89 break;
93 case '?': 90 case '?':
94 default: 91 default:
95 usage5 (); 92 usage5();
96 } 93 }
97 } 94 }
98 95
99 if (url == NULL) 96 if (url == NULL) {
100 url = strdup (argv[optind++]); 97 url = strdup(argv[optind++]);
98 }
101 99
102 cmd = strdup (argv[optind++]); 100 cmd = strdup(argv[optind++]);
103 for (c = optind; c < argc; c++) { 101 for (c = optind; c < argc; c++) {
104 xasprintf (&cmd, "%s %s", cmd, argv[c]); 102 xasprintf(&cmd, "%s %s", cmd, argv[c]);
105 } 103 }
106 104
107 child_process = spopen (cmd); 105 child_process = spopen(cmd);
108 if (child_process == NULL) { 106 if (child_process == NULL) {
109 printf (_("Could not open pipe: %s\n"), cmd); 107 printf(_("Could not open pipe: %s\n"), cmd);
110 exit (STATE_UNKNOWN); 108 exit(STATE_UNKNOWN);
111 } 109 }
112 110
113 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 111 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
114 if (child_stderr == NULL) { 112 if (child_stderr == NULL) {
115 printf (_("Could not open stderr for %s\n"), cmd); 113 printf(_("Could not open stderr for %s\n"), cmd);
116 } 114 }
117 115
118 bzero(tstr, sizeof(tstr)); 116 bzero(tstr, sizeof(tstr));
119 buf = malloc(MAX_INPUT_BUFFER); 117 buf = malloc(MAX_INPUT_BUFFER);
120 printf ("<A href=\"%s\">", argv[1]); 118 printf("<A href=\"%s\">", argv[1]);
121 while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) { 119 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) {
122 found++; 120 found++;
123 /* Collect the string in temp str so we can tokenize */ 121 /* Collect the string in temp str so we can tokenize */
124 strcat(tstr, buf); 122 strcat(tstr, buf);
125 } 123 }
126 124
127 if (!found) 125 if (!found) {
128 die (STATE_UNKNOWN, 126 die(STATE_UNKNOWN, _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), argv[0],
129 _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), 127 cmd);
130 argv[0], cmd); 128 }
131
132 129
133 /* chop the newline character */ 130 /* chop the newline character */
134 if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) 131 if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) {
135 *nstr = '\0'; 132 *nstr = '\0';
133 }
136 134
137 /* tokenize the string for Perfdata if there is some */ 135 /* tokenize the string for Perfdata if there is some */
138 nstr = strtok(tstr, PERF_CHARACTER); 136 nstr = strtok(tstr, PERF_CHARACTER);
139 printf ("%s", nstr); 137 printf("%s", nstr);
140 printf ("</A>"); 138 printf("</A>");
141 nstr = strtok(NULL, PERF_CHARACTER); 139 nstr = strtok(NULL, PERF_CHARACTER);
142 if (nstr != NULL) 140 if (nstr != NULL) {
143 printf (" | %s", nstr); 141 printf(" | %s", nstr);
142 }
144 143
145 /* close the pipe */ 144 /* close the pipe */
146 result = spclose (child_process); 145 result = spclose(child_process);
147 146
148 /* WARNING if output found on stderr */ 147 /* WARNING if output found on stderr */
149 if (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr)) 148 if (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
150 result = max_state (result, STATE_WARNING); 149 result = max_state(result, STATE_WARNING);
150 }
151 151
152 /* close stderr */ 152 /* close stderr */
153 (void) fclose (child_stderr); 153 (void)fclose(child_stderr);
154 154
155 return result; 155 return result;
156} 156}
157 157
158void print_help(void) {
159 print_revision(progname, NP_VERSION);
158 160
161 printf("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
162 printf(COPYRIGHT, copyright, email);
159 163
160void 164 printf("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>"));
161print_help (void) 165 printf("%s\n",
162{ 166 _("tags, thus displaying the child plugin's output as a clickable link in compatible"));
163 print_revision (progname, NP_VERSION); 167 printf("%s\n",
164 168 _("monitoring status screen. This plugin returns the status of the invoked plugin."));
165 printf ("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
166 printf (COPYRIGHT, copyright, email);
167
168 printf ("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>"));
169 printf ("%s\n", _("tags, thus displaying the child plugin's output as a clickable link in compatible"));
170 printf ("%s\n", _("monitoring status screen. This plugin returns the status of the invoked plugin."));
171 169
172 printf ("\n\n"); 170 printf("\n\n");
173 171
174 print_usage (); 172 print_usage();
175 173
176 printf (UT_HELP_VRSN); 174 printf(UT_HELP_VRSN);
177 175
178 printf ("\n"); 176 printf("\n");
179 printf ("%s\n", _("Examples:")); 177 printf("%s\n", _("Examples:"));
180 printf ("%s\n", _("Pay close attention to quoting to ensure that the shell passes the expected")); 178 printf("%s\n",
181 printf ("%s\n\n", _("data to the plugin. For example, in:")); 179 _("Pay close attention to quoting to ensure that the shell passes the expected"));
182 printf (" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'")); 180 printf("%s\n\n", _("data to the plugin. For example, in:"));
183 printf (" %s\n", _("the shell will remove the single quotes and urlize will see:")); 181 printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'"));
184 printf (" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r two words")); 182 printf(" %s\n", _("the shell will remove the single quotes and urlize will see:"));
185 printf (" %s\n\n", _("You probably want:")); 183 printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r two words"));
186 printf (" %s\n", _("urlize http://example.com/ \"check_http -H example.com -r 'two words'\"")); 184 printf(" %s\n\n", _("You probably want:"));
185 printf(" %s\n", _("urlize http://example.com/ \"check_http -H example.com -r 'two words'\""));
187 186
188 printf (UT_SUPPORT); 187 printf(UT_SUPPORT);
189} 188}
190 189
191 190void print_usage(void) {
192 191 printf("%s\n", _("Usage:"));
193void 192 printf("%s <url> <plugin> <arg1> ... <argN>\n", progname);
194print_usage (void)
195{
196 printf ("%s\n", _("Usage:"));
197 printf ("%s <url> <plugin> <arg1> ... <argN>\n", progname);
198} 193}
diff --git a/plugins/utils.c b/plugins/utils.c
index 77d6a6f9..41fe5fcf 100644
--- a/plugins/utils.c
+++ b/plugins/utils.c
@@ -1,26 +1,26 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Library of useful functions for plugins 3 * Library of useful functions for plugins
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000 Karl DeBisschop (karl@debisschop.net) 6 * Copyright (c) 2000 Karl DeBisschop (karl@debisschop.net)
7* Copyright (c) 2002-2007 Monitoring Plugins Development Team 7 * Copyright (c) 2002-2024 Monitoring Plugins Development Team
8* 8 *
9* This program is free software: you can redistribute it and/or modify 9 * This program is free software: you can redistribute it and/or modify
10* it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
11* the Free Software Foundation, either version 3 of the License, or 11 * the Free Software Foundation, either version 3 of the License, or
12* (at your option) any later version. 12 * (at your option) any later version.
13* 13 *
14* This program is distributed in the hope that it will be useful, 14 * This program is distributed in the hope that it will be useful,
15* but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17* GNU General Public License for more details. 17 * GNU General Public License for more details.
18* 18 *
19* You should have received a copy of the GNU General Public License 19 * You should have received a copy of the GNU General Public License
20* along with this program. If not, see <http://www.gnu.org/licenses/>. 20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21* 21 *
22* 22 *
23*****************************************************************************/ 23 *****************************************************************************/
24 24
25#include "common.h" 25#include "common.h"
26#include "./utils.h" 26#include "./utils.h"
@@ -34,7 +34,7 @@
34 34
35#include <arpa/inet.h> 35#include <arpa/inet.h>
36 36
37extern void print_usage (void); 37extern void print_usage(void);
38extern const char *progname; 38extern const char *progname;
39 39
40#define STRLEN 64 40#define STRLEN 64
@@ -42,173 +42,114 @@ extern const char *progname;
42 42
43time_t start_time, end_time; 43time_t start_time, end_time;
44 44
45/* ************************************************************************** 45void usage(const char *msg) {
46 * max_state(STATE_x, STATE_y) 46 printf("%s\n", msg);
47 * compares STATE_x to STATE_y and returns result based on the following 47 print_usage();
48 * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL 48 exit(STATE_UNKNOWN);
49 * 49}
50 * Note that numerically the above does not hold 50
51 ****************************************************************************/ 51void usage_va(const char *fmt, ...) {
52
53int
54max_state (int a, int b)
55{
56 if (a == STATE_CRITICAL || b == STATE_CRITICAL)
57 return STATE_CRITICAL;
58 else if (a == STATE_WARNING || b == STATE_WARNING)
59 return STATE_WARNING;
60 else if (a == STATE_OK || b == STATE_OK)
61 return STATE_OK;
62 else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
63 return STATE_UNKNOWN;
64 else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
65 return STATE_DEPENDENT;
66 else
67 return max (a, b);
68}
69
70/* **************************************************************************
71 * max_state_alt(STATE_x, STATE_y)
72 * compares STATE_x to STATE_y and returns result based on the following
73 * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
74 *
75 * The main difference between max_state_alt and max_state it that it doesn't
76 * allow setting a default to UNKNOWN. It will instead prioritixe any valid
77 * non-OK state.
78 ****************************************************************************/
79
80int
81max_state_alt (int a, int b)
82{
83 if (a == STATE_CRITICAL || b == STATE_CRITICAL)
84 return STATE_CRITICAL;
85 else if (a == STATE_WARNING || b == STATE_WARNING)
86 return STATE_WARNING;
87 else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
88 return STATE_UNKNOWN;
89 else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
90 return STATE_DEPENDENT;
91 else if (a == STATE_OK || b == STATE_OK)
92 return STATE_OK;
93 else
94 return max (a, b);
95}
96
97void usage (const char *msg)
98{
99 printf ("%s\n", msg);
100 print_usage ();
101 exit (STATE_UNKNOWN);
102}
103
104void usage_va (const char *fmt, ...)
105{
106 va_list ap; 52 va_list ap;
107 printf("%s: ", progname); 53 printf("%s: ", progname);
108 va_start(ap, fmt); 54 va_start(ap, fmt);
109 vprintf(fmt, ap); 55 vprintf(fmt, ap);
110 va_end(ap); 56 va_end(ap);
111 printf("\n"); 57 printf("\n");
112 exit (STATE_UNKNOWN); 58 exit(STATE_UNKNOWN);
113} 59}
114 60
115void usage2(const char *msg, const char *arg) 61void usage2(const char *msg, const char *arg) {
116{ 62 printf("%s: %s - %s\n", progname, msg, arg ? arg : "(null)");
117 printf ("%s: %s - %s\n", progname, msg, arg?arg:"(null)" ); 63 print_usage();
118 print_usage (); 64 exit(STATE_UNKNOWN);
119 exit (STATE_UNKNOWN);
120} 65}
121 66
122void 67void usage3(const char *msg, int arg) {
123usage3 (const char *msg, int arg) 68 printf("%s: %s - %c\n", progname, msg, arg);
124{
125 printf ("%s: %s - %c\n", progname, msg, arg);
126 print_usage(); 69 print_usage();
127 exit (STATE_UNKNOWN); 70 exit(STATE_UNKNOWN);
128} 71}
129 72
130void 73void usage4(const char *msg) {
131usage4 (const char *msg) 74 printf("%s: %s\n", progname, msg);
132{
133 printf ("%s: %s\n", progname, msg);
134 print_usage(); 75 print_usage();
135 exit (STATE_UNKNOWN); 76 exit(STATE_UNKNOWN);
136} 77}
137 78
138void 79void usage5(void) {
139usage5 (void)
140{
141 print_usage(); 80 print_usage();
142 exit (STATE_UNKNOWN); 81 exit(STATE_UNKNOWN);
143} 82}
144 83
145void 84void print_revision(const char *command_name, const char *revision) {
146print_revision (const char *command_name, const char *revision) 85 printf("%s v%s (%s %s)\n", command_name, revision, PACKAGE, VERSION);
147{
148 printf ("%s v%s (%s %s)\n",
149 command_name, revision, PACKAGE, VERSION);
150} 86}
151 87
152bool is_numeric (char *number) { 88bool is_numeric(char *number) {
153 char tmp[1]; 89 char tmp[1];
154 float x; 90 float x;
155 91
156 if (!number) 92 if (!number) {
157 return false; 93 return false;
158 else if (sscanf (number, "%f%c", &x, tmp) == 1) 94 } else if (sscanf(number, "%f%c", &x, tmp) == 1) {
159 return true; 95 return true;
160 else 96 } else {
161 return false; 97 return false;
98 }
162} 99}
163 100
164bool is_positive (char *number) { 101bool is_positive(char *number) {
165 if (is_numeric (number) && atof (number) > 0.0) 102 if (is_numeric(number) && atof(number) > 0.0) {
166 return true; 103 return true;
167 else 104 } else {
168 return false; 105 return false;
106 }
169} 107}
170 108
171bool is_negative (char *number) { 109bool is_negative(char *number) {
172 if (is_numeric (number) && atof (number) < 0.0) 110 if (is_numeric(number) && atof(number) < 0.0) {
173 return true; 111 return true;
174 else 112 } else {
175 return false; 113 return false;
114 }
176} 115}
177 116
178bool is_nonnegative (char *number) { 117bool is_nonnegative(char *number) {
179 if (is_numeric (number) && atof (number) >= 0.0) 118 if (is_numeric(number) && atof(number) >= 0.0) {
180 return true; 119 return true;
181 else 120 } else {
182 return false; 121 return false;
122 }
183} 123}
184 124
185bool is_percentage (char *number) { 125bool is_percentage(char *number) {
186 int x; 126 int x;
187 if (is_numeric (number) && (x = atof (number)) >= 0 && x <= 100) 127 if (is_numeric(number) && (x = atof(number)) >= 0 && x <= 100) {
188 return true; 128 return true;
189 else 129 } else {
190 return false; 130 return false;
131 }
191} 132}
192 133
193bool is_percentage_expression (const char str[]) { 134bool is_percentage_expression(const char str[]) {
194 if (!str) { 135 if (!str) {
195 return false; 136 return false;
196 } 137 }
197 138
198 size_t len = strlen(str); 139 size_t len = strlen(str);
199 140
200 if (str[len-1] != '%') { 141 if (str[len - 1] != '%') {
201 return false; 142 return false;
202 } 143 }
203 144
204 char *foo = calloc(sizeof(char), len + 1); 145 char *foo = calloc(len + 1, sizeof(char));
205 146
206 if (!foo) { 147 if (!foo) {
207 die (STATE_UNKNOWN, _("calloc failed \n")); 148 die(STATE_UNKNOWN, _("calloc failed \n"));
208 } 149 }
209 150
210 strcpy(foo, str); 151 strcpy(foo, str);
211 foo[len-1] = '\0'; 152 foo[len - 1] = '\0';
212 153
213 bool result = is_numeric(foo); 154 bool result = is_numeric(foo);
214 155
@@ -217,39 +158,44 @@ bool is_percentage_expression (const char str[]) {
217 return result; 158 return result;
218} 159}
219 160
220bool is_integer (char *number) { 161bool is_integer(char *number) {
221 long int n; 162 long int n;
222 163
223 if (!number || (strspn (number, "-0123456789 ") != strlen (number))) 164 if (!number || (strspn(number, "-0123456789 ") != strlen(number))) {
224 return false; 165 return false;
166 }
225 167
226 n = strtol (number, NULL, 10); 168 n = strtol(number, NULL, 10);
227 169
228 if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX) 170 if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX) {
229 return true; 171 return true;
230 else 172 } else {
231 return false; 173 return false;
174 }
232} 175}
233 176
234bool is_intpos (char *number) { 177bool is_intpos(char *number) {
235 if (is_integer (number) && atoi (number) > 0) 178 if (is_integer(number) && atoi(number) > 0) {
236 return true; 179 return true;
237 else 180 } else {
238 return false; 181 return false;
182 }
239} 183}
240 184
241bool is_intneg (char *number) { 185bool is_intneg(char *number) {
242 if (is_integer (number) && atoi (number) < 0) 186 if (is_integer(number) && atoi(number) < 0) {
243 return true; 187 return true;
244 else 188 } else {
245 return false; 189 return false;
190 }
246} 191}
247 192
248bool is_intnonneg (char *number) { 193bool is_intnonneg(char *number) {
249 if (is_integer (number) && atoi (number) >= 0) 194 if (is_integer(number) && atoi(number) >= 0) {
250 return true; 195 return true;
251 else 196 } else {
252 return false; 197 return false;
198 }
253} 199}
254 200
255/* 201/*
@@ -259,7 +205,7 @@ bool is_intnonneg (char *number) {
259 */ 205 */
260bool is_int64(char *number, int64_t *target) { 206bool is_int64(char *number, int64_t *target) {
261 errno = 0; 207 errno = 0;
262 char *endptr = { 0 }; 208 char *endptr = {0};
263 209
264 int64_t tmp = strtoll(number, &endptr, 10); 210 int64_t tmp = strtoll(number, &endptr, 10);
265 if (errno != 0) { 211 if (errno != 0) {
@@ -287,7 +233,7 @@ bool is_int64(char *number, int64_t *target) {
287 */ 233 */
288bool is_uint64(char *number, uint64_t *target) { 234bool is_uint64(char *number, uint64_t *target) {
289 errno = 0; 235 errno = 0;
290 char *endptr = { 0 }; 236 char *endptr = {0};
291 unsigned long long tmp = strtoull(number, &endptr, 10); 237 unsigned long long tmp = strtoull(number, &endptr, 10);
292 238
293 if (errno != 0) { 239 if (errno != 0) {
@@ -309,74 +255,61 @@ bool is_uint64(char *number, uint64_t *target) {
309 return true; 255 return true;
310} 256}
311 257
312bool is_intpercent (char *number) { 258bool is_intpercent(char *number) {
313 int i; 259 int i;
314 if (is_integer (number) && (i = atoi (number)) >= 0 && i <= 100) 260 if (is_integer(number) && (i = atoi(number)) >= 0 && i <= 100) {
315 return true; 261 return true;
316 else 262 } else {
317 return false; 263 return false;
264 }
318} 265}
319 266
320bool is_option (char *str) { 267bool is_option(char *str) {
321 if (!str) 268 if (!str) {
322 return false; 269 return false;
323 else if (strspn (str, "-") == 1 || strspn (str, "-") == 2) 270 } else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) {
324 return true; 271 return true;
325 else 272 } else {
326 return false; 273 return false;
274 }
327} 275}
328 276
329#ifdef NEED_GETTIMEOFDAY 277#ifdef NEED_GETTIMEOFDAY
330int 278int gettimeofday(struct timeval *tv, struct timezone *tz) {
331gettimeofday (struct timeval *tv, struct timezone *tz)
332{
333 tv->tv_usec = 0; 279 tv->tv_usec = 0;
334 tv->tv_sec = (long) time ((time_t) 0); 280 tv->tv_sec = (long)time((time_t)0);
335} 281}
336#endif 282#endif
337 283
338 284double delta_time(struct timeval tv) {
339
340double
341delta_time (struct timeval tv)
342{
343 struct timeval now; 285 struct timeval now;
344 286
345 gettimeofday (&now, NULL); 287 gettimeofday(&now, NULL);
346 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);
347} 290}
348 291
349 292long deltime(struct timeval tv) {
350
351long
352deltime (struct timeval tv)
353{
354 struct timeval now; 293 struct timeval now;
355 gettimeofday (&now, NULL); 294 gettimeofday(&now, NULL);
356 return (now.tv_sec - tv.tv_sec)*1000000 + now.tv_usec - tv.tv_usec; 295 return (now.tv_sec - tv.tv_sec) * 1000000 + now.tv_usec - tv.tv_usec;
357} 296}
358 297
359 298void strip(char *buffer) {
360
361
362void
363strip (char *buffer)
364{
365 size_t x; 299 size_t x;
366 int i; 300 int i;
367 301
368 for (x = strlen (buffer); x >= 1; x--) { 302 for (x = strlen(buffer); x >= 1; x--) {
369 i = x - 1; 303 i = x - 1;
370 if (buffer[i] == ' ' || 304 if (buffer[i] == ' ' || buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t') {
371 buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t')
372 buffer[i] = '\0'; 305 buffer[i] = '\0';
373 else 306 } else {
374 break; 307 break;
308 }
375 } 309 }
376 return; 310 return;
377} 311}
378 312
379
380/****************************************************************************** 313/******************************************************************************
381 * 314 *
382 * Copies one string to another. Any previously existing data in 315 * Copies one string to another. Any previously existing data in
@@ -389,19 +322,16 @@ strip (char *buffer)
389 * 322 *
390 *****************************************************************************/ 323 *****************************************************************************/
391 324
392char * 325char *strscpy(char *dest, const char *src) {
393strscpy (char *dest, const char *src) 326 if (src == NULL) {
394{
395 if (src == NULL)
396 return NULL; 327 return NULL;
328 }
397 329
398 xasprintf (&dest, "%s", src); 330 xasprintf(&dest, "%s", src);
399 331
400 return dest; 332 return dest;
401} 333}
402 334
403
404
405/****************************************************************************** 335/******************************************************************************
406 * 336 *
407 * Returns a pointer to the next line of a multiline string buffer 337 * Returns a pointer to the next line of a multiline string buffer
@@ -418,7 +348,7 @@ strscpy (char *dest, const char *src)
418 * This 348 * This
419 * is 349 * is
420 * a 350 * a
421 * 351 *
422 * multiline string buffer 352 * multiline string buffer
423 * ============================== 353 * ==============================
424 * 354 *
@@ -431,7 +361,7 @@ strscpy (char *dest, const char *src)
431 * printf("%d %s",i++,firstword(ptr)); 361 * printf("%d %s",i++,firstword(ptr));
432 * ptr = strnl(ptr); 362 * ptr = strnl(ptr);
433 * } 363 * }
434 * 364 *
435 * Produces the following: 365 * Produces the following:
436 * 366 *
437 * 1 This 367 * 1 This
@@ -452,25 +382,26 @@ strscpy (char *dest, const char *src)
452 * 382 *
453 *****************************************************************************/ 383 *****************************************************************************/
454 384
455char * 385char *strnl(char *str) {
456strnl (char *str)
457{
458 size_t len; 386 size_t len;
459 if (str == NULL) 387 if (str == NULL) {
460 return NULL; 388 return NULL;
461 str = strpbrk (str, "\r\n"); 389 }
462 if (str == NULL) 390 str = strpbrk(str, "\r\n");
391 if (str == NULL) {
463 return NULL; 392 return NULL;
464 len = strspn (str, "\r\n"); 393 }
465 if (str[len] == '\0') 394 len = strspn(str, "\r\n");
395 if (str[len] == '\0') {
466 return NULL; 396 return NULL;
397 }
467 str += len; 398 str += len;
468 if (strlen (str) == 0) 399 if (strlen(str) == 0) {
469 return NULL; 400 return NULL;
401 }
470 return str; 402 return str;
471} 403}
472 404
473
474/****************************************************************************** 405/******************************************************************************
475 * 406 *
476 * Like strscpy, except only the portion of the source string up to 407 * Like strscpy, except only the portion of the source string up to
@@ -487,29 +418,28 @@ strnl (char *str)
487 * 418 *
488 *****************************************************************************/ 419 *****************************************************************************/
489 420
490char * 421char *strpcpy(char *dest, const char *src, const char *str) {
491strpcpy (char *dest, const char *src, const char *str)
492{
493 size_t len; 422 size_t len;
494 423
495 if (src) 424 if (src) {
496 len = strcspn (src, str); 425 len = strcspn(src, str);
497 else 426 } else {
498 return NULL; 427 return NULL;
428 }
499 429
500 if (dest == NULL || strlen (dest) < len) 430 if (dest == NULL || strlen(dest) < len) {
501 dest = realloc (dest, len + 1); 431 dest = realloc(dest, len + 1);
502 if (dest == NULL) 432 }
503 die (STATE_UNKNOWN, _("failed realloc in strpcpy\n")); 433 if (dest == NULL) {
434 die(STATE_UNKNOWN, _("failed realloc in strpcpy\n"));
435 }
504 436
505 strncpy (dest, src, len); 437 strncpy(dest, src, len);
506 dest[len] = '\0'; 438 dest[len] = '\0';
507 439
508 return dest; 440 return dest;
509} 441}
510 442
511
512
513/****************************************************************************** 443/******************************************************************************
514 * 444 *
515 * Like strscat, except only the portion of the source string up to 445 * Like strscat, except only the portion of the source string up to
@@ -518,62 +448,57 @@ strpcpy (char *dest, const char *src, const char *str)
518 * str = strpcpy(str,"This is a line of text with no trailing newline","x"); 448 * str = strpcpy(str,"This is a line of text with no trailing newline","x");
519 * str = strpcat(str,"This is a line of text with no trailing newline","x"); 449 * str = strpcat(str,"This is a line of text with no trailing newline","x");
520 * printf("%s\n",str); 450 * printf("%s\n",str);
521 * 451 *
522 *This is a line of texThis is a line of tex 452 *This is a line of texThis is a line of tex
523 * 453 *
524 *****************************************************************************/ 454 *****************************************************************************/
525 455
526char * 456char *strpcat(char *dest, const char *src, const char *str) {
527strpcat (char *dest, const char *src, const char *str)
528{
529 size_t len, l2; 457 size_t len, l2;
530 458
531 if (dest) 459 if (dest) {
532 len = strlen (dest); 460 len = strlen(dest);
533 else 461 } else {
534 len = 0; 462 len = 0;
463 }
535 464
536 if (src) { 465 if (src) {
537 l2 = strcspn (src, str); 466 l2 = strcspn(src, str);
538 } 467 } else {
539 else {
540 return dest; 468 return dest;
541 } 469 }
542 470
543 dest = realloc (dest, len + l2 + 1); 471 dest = realloc(dest, len + l2 + 1);
544 if (dest == NULL) 472 if (dest == NULL) {
545 die (STATE_UNKNOWN, _("failed malloc in strscat\n")); 473 die(STATE_UNKNOWN, _("failed malloc in strscat\n"));
474 }
546 475
547 strncpy (dest + len, src, l2); 476 strncpy(dest + len, src, l2);
548 dest[len + l2] = '\0'; 477 dest[len + l2] = '\0';
549 478
550 return dest; 479 return dest;
551} 480}
552 481
553
554/****************************************************************************** 482/******************************************************************************
555 * 483 *
556 * asprintf, but die on failure 484 * asprintf, but die on failure
557 * 485 *
558 ******************************************************************************/ 486 ******************************************************************************/
559 487
560int 488int xvasprintf(char **strp, const char *fmt, va_list ap) {
561xvasprintf (char **strp, const char *fmt, va_list ap) 489 int result = vasprintf(strp, fmt, ap);
562{ 490 if (result == -1 || *strp == NULL) {
563 int result = vasprintf (strp, fmt, ap); 491 die(STATE_UNKNOWN, _("failed malloc in xvasprintf\n"));
564 if (result == -1 || *strp == NULL) 492 }
565 die (STATE_UNKNOWN, _("failed malloc in xvasprintf\n"));
566 return result; 493 return result;
567} 494}
568 495
569int 496int xasprintf(char **strp, const char *fmt, ...) {
570xasprintf (char **strp, const char *fmt, ...)
571{
572 va_list ap; 497 va_list ap;
573 int result; 498 int result;
574 va_start (ap, fmt); 499 va_start(ap, fmt);
575 result = xvasprintf (strp, fmt, ap); 500 result = xvasprintf(strp, fmt, ap);
576 va_end (ap); 501 va_end(ap);
577 return result; 502 return result;
578} 503}
579 504
@@ -583,247 +508,223 @@ xasprintf (char **strp, const char *fmt, ...)
583 * 508 *
584 ******************************************************************************/ 509 ******************************************************************************/
585 510
586char *perfdata (const char *label, 511char *perfdata(const char *label, long int val, const char *uom, bool warnp, long int warn,
587 long int val, 512 bool critp, long int crit, bool minp, long int minv, bool maxp, long int maxv) {
588 const char *uom,
589 int warnp,
590 long int warn,
591 int critp,
592 long int crit,
593 int minp,
594 long int minv,
595 int maxp,
596 long int maxv)
597{
598 char *data = NULL; 513 char *data = NULL;
599 514
600 if (strpbrk (label, "'= ")) 515 if (strpbrk(label, "'= ")) {
601 xasprintf (&data, "'%s'=%ld%s;", label, val, uom); 516 xasprintf(&data, "'%s'=%ld%s;", label, val, uom);
602 else 517 } else {
603 xasprintf (&data, "%s=%ld%s;", label, val, uom); 518 xasprintf(&data, "%s=%ld%s;", label, val, uom);
519 }
604 520
605 if (warnp) 521 if (warnp) {
606 xasprintf (&data, "%s%ld;", data, warn); 522 xasprintf(&data, "%s%ld;", data, warn);
607 else 523 } else {
608 xasprintf (&data, "%s;", data); 524 xasprintf(&data, "%s;", data);
525 }
609 526
610 if (critp) 527 if (critp) {
611 xasprintf (&data, "%s%ld;", data, crit); 528 xasprintf(&data, "%s%ld;", data, crit);
612 else 529 } else {
613 xasprintf (&data, "%s;", data); 530 xasprintf(&data, "%s;", data);
531 }
614 532
615 if (minp) 533 if (minp) {
616 xasprintf (&data, "%s%ld;", data, minv); 534 xasprintf(&data, "%s%ld;", data, minv);
617 else 535 } else {
618 xasprintf (&data, "%s;", data); 536 xasprintf(&data, "%s;", data);
537 }
619 538
620 if (maxp) 539 if (maxp) {
621 xasprintf (&data, "%s%ld", data, maxv); 540 xasprintf(&data, "%s%ld", data, maxv);
541 }
622 542
623 return data; 543 return data;
624} 544}
625 545
626 546char *perfdata_uint64(const char *label, uint64_t val, const char *uom,
627char *perfdata_uint64 (const char *label, 547 bool warnp, /* Warning present */
628 uint64_t val, 548 uint64_t warn, bool critp, /* Critical present */
629 const char *uom, 549 uint64_t crit, bool minp, /* Minimum present */
630 int warnp, /* Warning present */ 550 uint64_t minv, bool maxp, /* Maximum present */
631 uint64_t warn, 551 uint64_t maxv) {
632 int critp, /* Critical present */
633 uint64_t crit,
634 int minp, /* Minimum present */
635 uint64_t minv,
636 int maxp, /* Maximum present */
637 uint64_t maxv)
638{
639 char *data = NULL; 552 char *data = NULL;
640 553
641 if (strpbrk (label, "'= ")) 554 if (strpbrk(label, "'= ")) {
642 xasprintf (&data, "'%s'=%" PRIu64 "%s;", label, val, uom); 555 xasprintf(&data, "'%s'=%" PRIu64 "%s;", label, val, uom);
643 else 556 } else {
644 xasprintf (&data, "%s=%" PRIu64 "%s;", label, val, uom); 557 xasprintf(&data, "%s=%" PRIu64 "%s;", label, val, uom);
558 }
645 559
646 if (warnp) 560 if (warnp) {
647 xasprintf (&data, "%s%" PRIu64 ";", data, warn); 561 xasprintf(&data, "%s%" PRIu64 ";", data, warn);
648 else 562 } else {
649 xasprintf (&data, "%s;", data); 563 xasprintf(&data, "%s;", data);
564 }
650 565
651 if (critp) 566 if (critp) {
652 xasprintf (&data, "%s%" PRIu64 ";", data, crit); 567 xasprintf(&data, "%s%" PRIu64 ";", data, crit);
653 else 568 } else {
654 xasprintf (&data, "%s;", data); 569 xasprintf(&data, "%s;", data);
570 }
655 571
656 if (minp) 572 if (minp) {
657 xasprintf (&data, "%s%" PRIu64 ";", data, minv); 573 xasprintf(&data, "%s%" PRIu64 ";", data, minv);
658 else 574 } else {
659 xasprintf (&data, "%s;", data); 575 xasprintf(&data, "%s;", data);
576 }
660 577
661 if (maxp) 578 if (maxp) {
662 xasprintf (&data, "%s%" PRIu64, data, maxv); 579 xasprintf(&data, "%s%" PRIu64, data, maxv);
580 }
663 581
664 return data; 582 return data;
665} 583}
666 584
667 585char *perfdata_int64(const char *label, int64_t val, const char *uom,
668char *perfdata_int64 (const char *label, 586 bool warnp, /* Warning present */
669 int64_t val, 587 int64_t warn, bool critp, /* Critical present */
670 const char *uom, 588 int64_t crit, bool minp, /* Minimum present */
671 int warnp, /* Warning present */ 589 int64_t minv, bool maxp, /* Maximum present */
672 int64_t warn, 590 int64_t maxv) {
673 int critp, /* Critical present */
674 int64_t crit,
675 int minp, /* Minimum present */
676 int64_t minv,
677 int maxp, /* Maximum present */
678 int64_t maxv)
679{
680 char *data = NULL; 591 char *data = NULL;
681 592
682 if (strpbrk (label, "'= ")) 593 if (strpbrk(label, "'= ")) {
683 xasprintf (&data, "'%s'=%" PRId64 "%s;", label, val, uom); 594 xasprintf(&data, "'%s'=%" PRId64 "%s;", label, val, uom);
684 else 595 } else {
685 xasprintf (&data, "%s=%" PRId64 "%s;", label, val, uom); 596 xasprintf(&data, "%s=%" PRId64 "%s;", label, val, uom);
597 }
686 598
687 if (warnp) 599 if (warnp) {
688 xasprintf (&data, "%s%" PRId64 ";", data, warn); 600 xasprintf(&data, "%s%" PRId64 ";", data, warn);
689 else 601 } else {
690 xasprintf (&data, "%s;", data); 602 xasprintf(&data, "%s;", data);
603 }
691 604
692 if (critp) 605 if (critp) {
693 xasprintf (&data, "%s%" PRId64 ";", data, crit); 606 xasprintf(&data, "%s%" PRId64 ";", data, crit);
694 else 607 } else {
695 xasprintf (&data, "%s;", data); 608 xasprintf(&data, "%s;", data);
609 }
696 610
697 if (minp) 611 if (minp) {
698 xasprintf (&data, "%s%" PRId64 ";", data, minv); 612 xasprintf(&data, "%s%" PRId64 ";", data, minv);
699 else 613 } else {
700 xasprintf (&data, "%s;", data); 614 xasprintf(&data, "%s;", data);
615 }
701 616
702 if (maxp) 617 if (maxp) {
703 xasprintf (&data, "%s%" PRId64, data, maxv); 618 xasprintf(&data, "%s%" PRId64, data, maxv);
619 }
704 620
705 return data; 621 return data;
706} 622}
707 623
708 624char *fperfdata(const char *label, double val, const char *uom, bool warnp, double warn, bool critp,
709char *fperfdata (const char *label, 625 double crit, bool minp, double minv, bool maxp, double maxv) {
710 double val,
711 const char *uom,
712 int warnp,
713 double warn,
714 int critp,
715 double crit,
716 int minp,
717 double minv,
718 int maxp,
719 double maxv)
720{
721 char *data = NULL; 626 char *data = NULL;
722 627
723 if (strpbrk (label, "'= ")) 628 if (strpbrk(label, "'= ")) {
724 xasprintf (&data, "'%s'=", label); 629 xasprintf(&data, "'%s'=", label);
725 else 630 } else {
726 xasprintf (&data, "%s=", label); 631 xasprintf(&data, "%s=", label);
632 }
727 633
728 xasprintf (&data, "%s%f", data, val); 634 xasprintf(&data, "%s%f", data, val);
729 xasprintf (&data, "%s%s;", data, uom); 635 xasprintf(&data, "%s%s;", data, uom);
730 636
731 if (warnp) 637 if (warnp) {
732 xasprintf (&data, "%s%f", data, warn); 638 xasprintf(&data, "%s%f", data, warn);
639 }
733 640
734 xasprintf (&data, "%s;", data); 641 xasprintf(&data, "%s;", data);
735 642
736 if (critp) 643 if (critp) {
737 xasprintf (&data, "%s%f", data, crit); 644 xasprintf(&data, "%s%f", data, crit);
645 }
738 646
739 xasprintf (&data, "%s;", data); 647 xasprintf(&data, "%s;", data);
740 648
741 if (minp) 649 if (minp) {
742 xasprintf (&data, "%s%f", data, minv); 650 xasprintf(&data, "%s%f", data, minv);
651 }
743 652
744 if (maxp) { 653 if (maxp) {
745 xasprintf (&data, "%s;", data); 654 xasprintf(&data, "%s;", data);
746 xasprintf (&data, "%s%f", data, maxv); 655 xasprintf(&data, "%s%f", data, maxv);
747 } 656 }
748 657
749 return data; 658 return data;
750} 659}
751 660
752char *sperfdata (const char *label, 661char *sperfdata(const char *label, double val, const char *uom, char *warn, char *crit, bool minp,
753 double val, 662 double minv, bool maxp, double maxv) {
754 const char *uom,
755 char *warn,
756 char *crit,
757 int minp,
758 double minv,
759 int maxp,
760 double maxv)
761{
762 char *data = NULL; 663 char *data = NULL;
763 if (strpbrk (label, "'= ")) 664 if (strpbrk(label, "'= ")) {
764 xasprintf (&data, "'%s'=", label); 665 xasprintf(&data, "'%s'=", label);
765 else 666 } else {
766 xasprintf (&data, "%s=", label); 667 xasprintf(&data, "%s=", label);
668 }
767 669
768 xasprintf (&data, "%s%f", data, val); 670 xasprintf(&data, "%s%f", data, val);
769 xasprintf (&data, "%s%s;", data, uom); 671 xasprintf(&data, "%s%s;", data, uom);
770 672
771 if (warn!=NULL) 673 if (warn != NULL) {
772 xasprintf (&data, "%s%s", data, warn); 674 xasprintf(&data, "%s%s", data, warn);
675 }
773 676
774 xasprintf (&data, "%s;", data); 677 xasprintf(&data, "%s;", data);
775 678
776 if (crit!=NULL) 679 if (crit != NULL) {
777 xasprintf (&data, "%s%s", data, crit); 680 xasprintf(&data, "%s%s", data, crit);
681 }
778 682
779 xasprintf (&data, "%s;", data); 683 xasprintf(&data, "%s;", data);
780 684
781 if (minp) 685 if (minp) {
782 xasprintf (&data, "%s%f", data, minv); 686 xasprintf(&data, "%s%f", data, minv);
687 }
783 688
784 if (maxp) { 689 if (maxp) {
785 xasprintf (&data, "%s;", data); 690 xasprintf(&data, "%s;", data);
786 xasprintf (&data, "%s%f", data, maxv); 691 xasprintf(&data, "%s%f", data, maxv);
787 } 692 }
788 693
789 return data; 694 return data;
790} 695}
791 696
792char *sperfdata_int (const char *label, 697char *sperfdata_int(const char *label, int val, const char *uom, char *warn, char *crit, bool minp,
793 int val, 698 int minv, bool maxp, int maxv) {
794 const char *uom,
795 char *warn,
796 char *crit,
797 int minp,
798 int minv,
799 int maxp,
800 int maxv)
801{
802 char *data = NULL; 699 char *data = NULL;
803 if (strpbrk (label, "'= ")) 700 if (strpbrk(label, "'= ")) {
804 xasprintf (&data, "'%s'=", label); 701 xasprintf(&data, "'%s'=", label);
805 else 702 } else {
806 xasprintf (&data, "%s=", label); 703 xasprintf(&data, "%s=", label);
704 }
807 705
808 xasprintf (&data, "%s%d", data, val); 706 xasprintf(&data, "%s%d", data, val);
809 xasprintf (&data, "%s%s;", data, uom); 707 xasprintf(&data, "%s%s;", data, uom);
810 708
811 if (warn!=NULL) 709 if (warn != NULL) {
812 xasprintf (&data, "%s%s", data, warn); 710 xasprintf(&data, "%s%s", data, warn);
711 }
813 712
814 xasprintf (&data, "%s;", data); 713 xasprintf(&data, "%s;", data);
815 714
816 if (crit!=NULL) 715 if (crit != NULL) {
817 xasprintf (&data, "%s%s", data, crit); 716 xasprintf(&data, "%s%s", data, crit);
717 }
818 718
819 xasprintf (&data, "%s;", data); 719 xasprintf(&data, "%s;", data);
820 720
821 if (minp) 721 if (minp) {
822 xasprintf (&data, "%s%d", data, minv); 722 xasprintf(&data, "%s%d", data, minv);
723 }
823 724
824 if (maxp) { 725 if (maxp) {
825 xasprintf (&data, "%s;", data); 726 xasprintf(&data, "%s;", data);
826 xasprintf (&data, "%s%d", data, maxv); 727 xasprintf(&data, "%s%d", data, maxv);
827 } 728 }
828 729
829 return data; 730 return data;
diff --git a/plugins/utils.h b/plugins/utils.h
index f939e337..1f0e021b 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -13,51 +13,51 @@ in order to resist overflow attacks. In addition, a few functions are
13provided to standardize version and error reporting across the entire 13provided to standardize version and error reporting across the entire
14suite of plugins. */ 14suite of plugins. */
15 15
16/* now some functions etc are being defined in ../lib/utils_base.c */ 16#include "../config.h"
17#include "utils_base.h"
18
19#include <stdbool.h> 17#include <stdbool.h>
20 18#include <stdint.h>
19#include <stdio.h>
20#include <time.h>
21 21
22#ifdef NP_EXTRA_OPTS 22#ifdef NP_EXTRA_OPTS
23/* Include extra-opts functions if compiled in */ 23/* Include extra-opts functions if compiled in */
24#include "extra_opts.h" 24# include "extra_opts.h"
25#else 25#else
26/* else, fake np_extra_opts */ 26/* else, fake np_extra_opts */
27#define np_extra_opts(acptr,av,pr) av 27# define np_extra_opts(acptr, av, pr) av
28#endif 28#endif
29 29
30/* Standardize version information, termination */ 30/* Standardize version information, termination */
31 31
32void support (void); 32void support(void);
33void print_revision (const char *, const char *); 33void print_revision(const char *, const char *);
34 34
35extern time_t start_time, end_time; 35extern time_t start_time, end_time;
36 36
37/* Test input types */ 37/* Test input types */
38 38
39bool is_integer (char *); 39bool is_integer(char *);
40bool is_intpos (char *); 40bool is_intpos(char *);
41bool is_intneg (char *); 41bool is_intneg(char *);
42bool is_intnonneg (char *); 42bool is_intnonneg(char *);
43bool is_intpercent (char *); 43bool is_intpercent(char *);
44bool is_uint64(char *number, uint64_t *target); 44bool is_uint64(char *number, uint64_t *target);
45bool is_int64(char *number, int64_t *target); 45bool is_int64(char *number, int64_t *target);
46 46
47bool is_numeric (char *); 47bool is_numeric(char *);
48bool is_positive (char *); 48bool is_positive(char *);
49bool is_negative (char *); 49bool is_negative(char *);
50bool is_nonnegative (char *); 50bool is_nonnegative(char *);
51bool is_percentage (char *); 51bool is_percentage(char *);
52bool is_percentage_expression (const char[]); 52bool is_percentage_expression(const char[]);
53 53
54bool is_option (char *); 54bool is_option(char *);
55 55
56/* Generalized timer that will do milliseconds if available */ 56/* Generalized timer that will do milliseconds if available */
57#ifndef HAVE_STRUCT_TIMEVAL 57#ifndef HAVE_STRUCT_TIMEVAL
58struct timeval { 58struct timeval {
59 long tv_sec; /* seconds */ 59 long tv_sec; /* seconds */
60 long tv_usec; /* microseconds */ 60 long tv_usec; /* microseconds */
61}; 61};
62#endif 62#endif
63 63
@@ -65,137 +65,148 @@ struct timeval {
65int gettimeofday(struct timeval *, struct timezone *); 65int gettimeofday(struct timeval *, struct timezone *);
66#endif 66#endif
67 67
68double delta_time (struct timeval tv); 68double delta_time(struct timeval tv);
69long deltime (struct timeval tv); 69long deltime(struct timeval tv);
70 70
71/* Handle strings safely */ 71/* Handle strings safely */
72 72
73void strip (char *); 73void strip(char *);
74char *strscpy (char *, const char *); 74char *strscpy(char *, const char *);
75char *strnl (char *); 75char *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
81int max_state (int a, int b); 81void usage(const char *) __attribute__((noreturn));
82int max_state_alt (int a, int b);
83
84void usage (const char *) __attribute__((noreturn));
85void usage2(const char *, const char *) __attribute__((noreturn)); 82void usage2(const char *, const char *) __attribute__((noreturn));
86void usage3(const char *, int) __attribute__((noreturn)); 83void usage3(const char *, int) __attribute__((noreturn));
87void usage4(const char *) __attribute__((noreturn)); 84void usage4(const char *) __attribute__((noreturn));
88void usage5(void) __attribute__((noreturn)); 85void usage5(void) __attribute__((noreturn));
89void usage_va(const char *fmt, ...) __attribute__((noreturn)); 86void usage_va(const char *fmt, ...) __attribute__((noreturn));
90 87
91#define max(a,b) (((a)>(b))?(a):(b)) 88#define max(a, b) (((a) > (b)) ? (a) : (b))
92#define min(a,b) (((a)<(b))?(a):(b)) 89#define min(a, b) (((a) < (b)) ? (a) : (b))
93 90
94char *perfdata (const char *, long int, const char *, int, long int, 91char *perfdata(const char *, long int, const char *, bool, long int, bool, long int, bool, long int,
95 int, long int, int, long int, int, long int); 92 bool, long int);
96 93
97char *perfdata_uint64 (const char *, uint64_t , const char *, int, uint64_t, 94char *perfdata_uint64(const char *, uint64_t, const char *, bool, uint64_t, bool, uint64_t, bool,
98 int, uint64_t, int, uint64_t, int, uint64_t); 95 uint64_t, bool, uint64_t);
99 96
100char *perfdata_int64 (const char *, int64_t, const char *, int, int64_t, 97char *perfdata_int64(const char *, int64_t, const char *, bool, int64_t, bool, int64_t, bool,
101 int, int64_t, int, int64_t, int, int64_t); 98 int64_t, bool, int64_t);
102 99
103char *fperfdata (const char *, double, const char *, int, double, 100char *fperfdata(const char *, double, const char *, bool, double, bool, double, bool, double, bool,
104 int, double, int, double, int, double); 101 double);
105 102
106char *sperfdata (const char *, double, const char *, char *, char *, 103char *sperfdata(const char *, double, const char *, char *, char *, bool, double, bool, double);
107 int, double, int, double);
108 104
109char *sperfdata_int (const char *, int, const char *, char *, char *, 105char *sperfdata_int(const char *, int, const char *, char *, char *, bool, int, bool, int);
110 int, int, int, int);
111 106
112/* The idea here is that, although not every plugin will use all of these, 107/* The idea here is that, although not every plugin will use all of these,
113 most will or should. Therefore, for consistency, these very common 108 most will or should. Therefore, for consistency, these very common
114 options should have only these meanings throughout the overall suite */ 109 options should have only these meanings throughout the overall suite */
115 110
116#define STD_LONG_OPTS \ 111#define STD_LONG_OPTS \
117{"version",no_argument,0,'V'},\ 112 {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, \
118{"verbose",no_argument,0,'v'},\ 113 {"help", no_argument, 0, 'h'}, {"timeout", required_argument, 0, 't'}, \
119{"help",no_argument,0,'h'},\ 114 {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, \
120{"timeout",required_argument,0,'t'},\ 115 {"hostname", required_argument, 0, 'H'}
121{"critical",required_argument,0,'c'},\
122{"warning",required_argument,0,'w'},\
123{"hostname",required_argument,0,'H'}
124 116
125#define COPYRIGHT "Copyright (c) %s Monitoring Plugins Development Team\n\ 117#define COPYRIGHT \
118 "Copyright (c) %s Monitoring Plugins Development Team\n\
126\t<%s>\n\n" 119\t<%s>\n\n"
127 120
128#define UT_HLP_VRS _("\ 121#define UT_HLP_VRS \
122 _("\
129 %s (-h | --help) for detailed help\n\ 123 %s (-h | --help) for detailed help\n\
130 %s (-V | --version) for version information\n") 124 %s (-V | --version) for version information\n")
131 125
132#define UT_HELP_VRSN _("\ 126#define UT_HELP_VRSN \
127 _("\
133\nOptions:\n\ 128\nOptions:\n\
134 -h, --help\n\ 129 -h, --help\n\
135 Print detailed help screen\n\ 130 Print detailed help screen\n\
136 -V, --version\n\ 131 -V, --version\n\
137 Print version information\n") 132 Print version information\n")
138 133
139#define UT_HOST_PORT _("\ 134#define UT_HOST_PORT \
135 _("\
140 -H, --hostname=ADDRESS\n\ 136 -H, --hostname=ADDRESS\n\
141 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\
142 -%c, --port=INTEGER\n\ 138 -%c, --port=INTEGER\n\
143 Port number (default: %s)\n") 139 Port number (default: %s)\n")
144 140
145#define UT_IPv46 _("\ 141#define UT_IPv46 \
142 _("\
146 -4, --use-ipv4\n\ 143 -4, --use-ipv4\n\
147 Use IPv4 connection\n\ 144 Use IPv4 connection\n\
148 -6, --use-ipv6\n\ 145 -6, --use-ipv6\n\
149 Use IPv6 connection\n") 146 Use IPv6 connection\n")
150 147
151#define UT_VERBOSE _("\ 148#define UT_VERBOSE \
149 _("\
152 -v, --verbose\n\ 150 -v, --verbose\n\
153 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\
154 the monitoring system)\n") 152 the monitoring system)\n")
155 153
156#define UT_WARN_CRIT _("\ 154#define UT_WARN_CRIT \
155 _("\
157 -w, --warning=DOUBLE\n\ 156 -w, --warning=DOUBLE\n\
158 Response time to result in warning status (seconds)\n\ 157 Response time to result in warning status (seconds)\n\
159 -c, --critical=DOUBLE\n\ 158 -c, --critical=DOUBLE\n\
160 Response time to result in critical status (seconds)\n") 159 Response time to result in critical status (seconds)\n")
161 160
162#define UT_WARN_CRIT_RANGE _("\ 161#define UT_WARN_CRIT_RANGE \
162 _("\
163 -w, --warning=RANGE\n\ 163 -w, --warning=RANGE\n\
164 Warning range (format: start:end). Alert if outside this range\n\ 164 Warning range (format: start:end). Alert if outside this range\n\
165 -c, --critical=RANGE\n\ 165 -c, --critical=RANGE\n\
166 Critical range\n") 166 Critical range\n")
167 167
168#define UT_CONN_TIMEOUT _("\ 168#define UT_CONN_TIMEOUT \
169 _("\
169 -t, --timeout=INTEGER\n\ 170 -t, --timeout=INTEGER\n\
170 Seconds before connection times out (default: %d)\n") 171 Seconds before connection times out (default: %d)\n")
171 172
172#define UT_PLUG_TIMEOUT _("\ 173#define UT_PLUG_TIMEOUT \
174 _("\
173 -t, --timeout=INTEGER\n\ 175 -t, --timeout=INTEGER\n\
174 Seconds before plugin times out (default: %d)\n") 176 Seconds before plugin times out (default: %d)\n")
175 177
176#ifdef NP_EXTRA_OPTS 178#ifdef NP_EXTRA_OPTS
177#define UT_EXTRA_OPTS _("\ 179# define UT_EXTRA_OPTS \
180 _("\
178 --extra-opts=[section][@file]\n\ 181 --extra-opts=[section][@file]\n\
179 Read options from an ini file. See\n\ 182 Read options from an ini file. See\n\
180 https://www.monitoring-plugins.org/doc/extra-opts.html\n\ 183 https://www.monitoring-plugins.org/doc/extra-opts.html\n\
181 for usage and examples.\n") 184 for usage and examples.\n")
182#else 185#else
183#define UT_EXTRA_OPTS " \b" 186# define UT_EXTRA_OPTS " \b"
184#endif 187#endif
185 188
186#define UT_THRESHOLDS_NOTES _("\ 189#define UT_THRESHOLDS_NOTES \
190 _("\
187 See:\n\ 191 See:\n\
188 https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT\n\ 192 https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT\n\
189 for THRESHOLD format and examples.\n") 193 for THRESHOLD format and examples.\n")
190 194
191#define UT_SUPPORT _("\n\ 195#define UT_SUPPORT \
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 _("\n\ 201#define UT_NOWARRANTY \
202 _("\n\
197The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\ 203The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\
198copies of the plugins under the terms of the GNU General Public License.\n\ 204copies of the plugins under the terms of the GNU General Public License.\n\
199For more information about these matters, see the file named COPYING.\n") 205For more information about these matters, see the file named COPYING.\n")
200 206
207#define UT_OUTPUT_FORMAT \
208 _("\
209 --output-format=OUTPUT_FORMAT\n\
210 Select output format. Valid values: \"multi-line\", \"mp-test-json\"\n")
211
201#endif /* NP_UTILS_H */ 212#endif /* NP_UTILS_H */